1
0
mirror of https://github.com/d47081/qBittorrent.git synced 2025-02-02 09:55:55 +00:00

Merge pull request #4109 from buinsky/master

WebUI: Implement peers tab
This commit is contained in:
sledgehammer999 2015-12-06 13:34:25 -06:00
commit 75d4e2a2f2
22 changed files with 1040 additions and 656 deletions

View File

@ -29,6 +29,7 @@
#include "base/net/geoipmanager.h" #include "base/net/geoipmanager.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "base/unicodestrings.h" #include "base/unicodestrings.h"
#include "base/bittorrent/torrenthandle.h"
#include "peerinfo.h" #include "peerinfo.h"
namespace libt = libtorrent; namespace libt = libtorrent;
@ -49,9 +50,11 @@ PeerAddress::PeerAddress(QHostAddress ip, ushort port)
// PeerInfo // PeerInfo
PeerInfo::PeerInfo(const libt::peer_info &nativeInfo) PeerInfo::PeerInfo(const TorrentHandle *torrent, const libt::peer_info &nativeInfo)
: m_nativeInfo(nativeInfo) : m_nativeInfo(nativeInfo)
{ {
calcRelevance(torrent);
determineFlags();
} }
bool PeerInfo::fromDHT() const bool PeerInfo::fromDHT() const
@ -253,3 +256,155 @@ QString PeerInfo::connectionType() const
return connection; return connection;
} }
void PeerInfo::calcRelevance(const TorrentHandle *torrent)
{
const QBitArray &allPieces = torrent->pieces();
const QBitArray &peerPieces = pieces();
int localMissing = 0;
int remoteHaves = 0;
for (int i = 0; i < allPieces.size(); ++i) {
if (!allPieces[i]) {
++localMissing;
if (peerPieces[i])
++remoteHaves;
}
}
if (localMissing == 0)
m_relevance = 0.0;
else
m_relevance = static_cast<qreal>(remoteHaves) / localMissing;
}
qreal PeerInfo::relevance() const
{
return m_relevance;
}
void PeerInfo::determineFlags()
{
if (isInteresting()) {
//d = Your client wants to download, but peer doesn't want to send (interested and choked)
if (isRemoteChocked()) {
m_flags += "d ";
m_flagsDescription += tr("interested(local) and choked(peer)");
m_flagsDescription += ", ";
}
else {
//D = Currently downloading (interested and not choked)
m_flags += "D ";
m_flagsDescription += tr("interested(local) and unchoked(peer)");
m_flagsDescription += ", ";
}
}
if (isRemoteInterested()) {
//u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
if (isChocked()) {
m_flags += "u ";
m_flagsDescription += tr("interested(peer) and choked(local)");
m_flagsDescription += ", ";
}
else {
//U = Currently uploading (interested and not choked)
m_flags += "U ";
m_flagsDescription += tr("interested(peer) and unchoked(local)");
m_flagsDescription += ", ";
}
}
//O = Optimistic unchoke
if (optimisticUnchoke()) {
m_flags += "O ";
m_flagsDescription += tr("optimistic unchoke");
m_flagsDescription += ", ";
}
//S = Peer is snubbed
if (isSnubbed()) {
m_flags += "S ";
m_flagsDescription += tr("peer snubbed");
m_flagsDescription += ", ";
}
//I = Peer is an incoming connection
if (!isLocalConnection()) {
m_flags += "I ";
m_flagsDescription += tr("incoming connection");
m_flagsDescription += ", ";
}
//K = Peer is unchoking your client, but your client is not interested
if (!isRemoteChocked() && !isInteresting()) {
m_flags += "K ";
m_flagsDescription += tr("not interested(local) and unchoked(peer)");
m_flagsDescription += ", ";
}
//? = Your client unchoked the peer but the peer is not interested
if (!isChocked() && !isRemoteInterested()) {
m_flags += "? ";
m_flagsDescription += tr("not interested(peer) and unchoked(local)");
m_flagsDescription += ", ";
}
//X = Peer was included in peerlists obtained through Peer Exchange (PEX)
if (fromPeX()) {
m_flags += "X ";
m_flagsDescription += tr("peer from PEX");
m_flagsDescription += ", ";
}
//H = Peer was obtained through DHT
if (fromDHT()) {
m_flags += "H ";
m_flagsDescription += tr("peer from DHT");
m_flagsDescription += ", ";
}
//E = Peer is using Protocol Encryption (all traffic)
if (isRC4Encrypted()) {
m_flags += "E ";
m_flagsDescription += tr("encrypted traffic");
m_flagsDescription += ", ";
}
//e = Peer is using Protocol Encryption (handshake)
if (isPlaintextEncrypted()) {
m_flags += "e ";
m_flagsDescription += tr("encrypted handshake");
m_flagsDescription += ", ";
}
//P = Peer is using uTorrent uTP
if (useUTPSocket()) {
m_flags += "P ";
m_flagsDescription += QString::fromUtf8(C_UTP);
m_flagsDescription += ", ";
}
//L = Peer is local
if (fromLSD()) {
m_flags += "L";
m_flagsDescription += tr("peer from LSD");
}
m_flags = m_flags.trimmed();
m_flagsDescription = m_flagsDescription.trimmed();
if (m_flagsDescription.endsWith(',', Qt::CaseInsensitive))
m_flagsDescription.chop(1);
}
QString PeerInfo::flags() const
{
return m_flags;
}
QString PeerInfo::flagsDescription() const
{
return m_flagsDescription;
}

View File

@ -33,9 +33,12 @@
#include <QHostAddress> #include <QHostAddress>
#include <QBitArray> #include <QBitArray>
#include <QCoreApplication>
namespace BitTorrent namespace BitTorrent
{ {
class TorrentHandle;
struct PeerAddress struct PeerAddress
{ {
QHostAddress ip; QHostAddress ip;
@ -47,8 +50,10 @@ namespace BitTorrent
class PeerInfo class PeerInfo
{ {
Q_DECLARE_TR_FUNCTIONS(PeerInfo)
public: public:
PeerInfo(const libtorrent::peer_info &nativeInfo); PeerInfo(const TorrentHandle *torrent, const libtorrent::peer_info &nativeInfo);
bool fromDHT() const; bool fromDHT() const;
bool fromPeX() const; bool fromPeX() const;
@ -89,12 +94,21 @@ namespace BitTorrent
qlonglong totalDownload() const; qlonglong totalDownload() const;
QBitArray pieces() const; QBitArray pieces() const;
QString connectionType() const; QString connectionType() const;
qreal relevance() const;
QString flags() const;
QString flagsDescription() const;
#ifndef DISABLE_COUNTRIES_RESOLUTION #ifndef DISABLE_COUNTRIES_RESOLUTION
QString country() const; QString country() const;
#endif #endif
private: private:
void calcRelevance(const TorrentHandle *torrent);
void determineFlags();
libtorrent::peer_info m_nativeInfo; libtorrent::peer_info m_nativeInfo;
qreal m_relevance;
QString m_flags;
QString m_flagsDescription;
}; };
} }

View File

@ -960,7 +960,7 @@ QList<PeerInfo> TorrentHandle::peers() const
SAFE_CALL(get_peer_info, nativePeers); SAFE_CALL(get_peer_info, nativePeers);
foreach (const libt::peer_info &peer, nativePeers) foreach (const libt::peer_info &peer, nativePeers)
peers << peer; peers << PeerInfo(this, peer);
return peers; return peers;
} }

View File

@ -51,464 +51,338 @@
#include "peerlistsortmodel.h" #include "peerlistsortmodel.h"
#include "peerlistwidget.h" #include "peerlistwidget.h"
PeerListWidget::PeerListWidget(PropertiesWidget *parent): PeerListWidget::PeerListWidget(PropertiesWidget *parent)
QTreeView(parent), m_properties(parent), m_displayFlags(false) : QTreeView(parent)
, m_properties(parent)
, m_displayFlags(false)
{ {
// Load settings // Load settings
loadSettings(); loadSettings();
// Visual settings // Visual settings
setUniformRowHeights(true); setUniformRowHeights(true);
setRootIsDecorated(false); setRootIsDecorated(false);
setItemsExpandable(false); setItemsExpandable(false);
setAllColumnsShowFocus(true); setAllColumnsShowFocus(true);
setSelectionMode(QAbstractItemView::ExtendedSelection); setSelectionMode(QAbstractItemView::ExtendedSelection);
// List Model // List Model
m_listModel = new QStandardItemModel(0, PeerListDelegate::COL_COUNT); m_listModel = new QStandardItemModel(0, PeerListDelegate::COL_COUNT);
m_listModel->setHeaderData(PeerListDelegate::COUNTRY, Qt::Horizontal, QVariant()); // Country flag column m_listModel->setHeaderData(PeerListDelegate::COUNTRY, Qt::Horizontal, QVariant()); // Country flag column
m_listModel->setHeaderData(PeerListDelegate::IP, Qt::Horizontal, tr("IP")); m_listModel->setHeaderData(PeerListDelegate::IP, Qt::Horizontal, tr("IP"));
m_listModel->setHeaderData(PeerListDelegate::PORT, Qt::Horizontal, tr("Port")); m_listModel->setHeaderData(PeerListDelegate::PORT, Qt::Horizontal, tr("Port"));
m_listModel->setHeaderData(PeerListDelegate::FLAGS, Qt::Horizontal, tr("Flags")); m_listModel->setHeaderData(PeerListDelegate::FLAGS, Qt::Horizontal, tr("Flags"));
m_listModel->setHeaderData(PeerListDelegate::CONNECTION, Qt::Horizontal, tr("Connection")); m_listModel->setHeaderData(PeerListDelegate::CONNECTION, Qt::Horizontal, tr("Connection"));
m_listModel->setHeaderData(PeerListDelegate::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application")); m_listModel->setHeaderData(PeerListDelegate::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application"));
m_listModel->setHeaderData(PeerListDelegate::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded")); m_listModel->setHeaderData(PeerListDelegate::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
m_listModel->setHeaderData(PeerListDelegate::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed")); m_listModel->setHeaderData(PeerListDelegate::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed"));
m_listModel->setHeaderData(PeerListDelegate::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed")); m_listModel->setHeaderData(PeerListDelegate::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed"));
m_listModel->setHeaderData(PeerListDelegate::TOT_DOWN, Qt::Horizontal, tr("Downloaded", "i.e: total data downloaded")); m_listModel->setHeaderData(PeerListDelegate::TOT_DOWN, Qt::Horizontal, tr("Downloaded", "i.e: total data downloaded"));
m_listModel->setHeaderData(PeerListDelegate::TOT_UP, Qt::Horizontal, tr("Uploaded", "i.e: total data uploaded")); m_listModel->setHeaderData(PeerListDelegate::TOT_UP, Qt::Horizontal, tr("Uploaded", "i.e: total data uploaded"));
m_listModel->setHeaderData(PeerListDelegate::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(PeerListDelegate::RELEVANCE, Qt::Horizontal, tr("Relevance", "i.e: How relevant this peer is to us. How many pieces it has that we don't."));
// Proxy model to support sorting without actually altering the underlying model // Proxy model to support sorting without actually altering the underlying model
m_proxyModel = new PeerListSortModel(); m_proxyModel = new PeerListSortModel();
m_proxyModel->setDynamicSortFilter(true); m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->setSourceModel(m_listModel); m_proxyModel->setSourceModel(m_listModel);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
setModel(m_proxyModel); setModel(m_proxyModel);
//Explicitly set the column visibility. When columns are added/removed //Explicitly set the column visibility. When columns are added/removed
//between versions this prevents some of them being hidden due to //between versions this prevents some of them being hidden due to
//incorrect restoreState() being used. //incorrect restoreState() being used.
for (unsigned int i=0; i<PeerListDelegate::IP_HIDDEN; i++) for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++)
showColumn(i); showColumn(i);
hideColumn(PeerListDelegate::IP_HIDDEN); hideColumn(PeerListDelegate::IP_HIDDEN);
hideColumn(PeerListDelegate::COL_COUNT); hideColumn(PeerListDelegate::COL_COUNT);
if (!Preferences::instance()->resolvePeerCountries()) if (!Preferences::instance()->resolvePeerCountries())
hideColumn(PeerListDelegate::COUNTRY); hideColumn(PeerListDelegate::COUNTRY);
//To also mitigate the above issue, we have to resize each column when //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 //its size is 0, because explicitly 'showing' the column isn't enough
//in the above scenario. //in the above scenario.
for (unsigned int i=0; i<PeerListDelegate::IP_HIDDEN; i++) for (unsigned int i = 0; i < PeerListDelegate::IP_HIDDEN; i++)
if (!columnWidth(i)) if (!columnWidth(i))
resizeColumnToContents(i); resizeColumnToContents(i);
// Context menu // Context menu
setContextMenuPolicy(Qt::CustomContextMenu); setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showPeerListMenu(QPoint))); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showPeerListMenu(QPoint)));
// List delegate // List delegate
m_listDelegate = new PeerListDelegate(this); m_listDelegate = new PeerListDelegate(this);
setItemDelegate(m_listDelegate); setItemDelegate(m_listDelegate);
// Enable sorting // Enable sorting
setSortingEnabled(true); setSortingEnabled(true);
// IP to Hostname resolver // IP to Hostname resolver
updatePeerHostNameResolutionState(); updatePeerHostNameResolutionState();
// SIGNAL/SLOT // SIGNAL/SLOT
connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int))); connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int)));
handleSortColumnChanged(header()->sortIndicatorSection()); handleSortColumnChanged(header()->sortIndicatorSection());
copyHotkey = new QShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_C), this, SLOT(copySelectedPeers()), 0, Qt::WidgetShortcut); m_copyHotkey = new QShortcut(QKeySequence(Qt::ControlModifier + Qt::Key_C), this, SLOT(copySelectedPeers()), 0, Qt::WidgetShortcut);
} }
PeerListWidget::~PeerListWidget() PeerListWidget::~PeerListWidget()
{ {
saveSettings(); saveSettings();
delete m_proxyModel; delete m_proxyModel;
delete m_listModel; delete m_listModel;
delete m_listDelegate; delete m_listDelegate;
if (m_resolver) if (m_resolver)
delete m_resolver; delete m_resolver;
delete copyHotkey; delete m_copyHotkey;
} }
void PeerListWidget::updatePeerHostNameResolutionState() void PeerListWidget::updatePeerHostNameResolutionState()
{ {
if (Preferences::instance()->resolvePeerHostNames()) { if (Preferences::instance()->resolvePeerHostNames()) {
if (!m_resolver) { if (!m_resolver) {
m_resolver = new Net::ReverseResolution(this); m_resolver = new Net::ReverseResolution(this);
connect(m_resolver, SIGNAL(ipResolved(QString,QString)), SLOT(handleResolved(QString,QString))); connect(m_resolver, SIGNAL(ipResolved(QString, QString)), SLOT(handleResolved(QString, QString)));
loadPeers(m_properties->getCurrentTorrent(), true); loadPeers(m_properties->getCurrentTorrent(), true);
}
}
else if (m_resolver) {
delete m_resolver;
} }
} else {
if (m_resolver)
delete m_resolver;
}
} }
void PeerListWidget::updatePeerCountryResolutionState() void PeerListWidget::updatePeerCountryResolutionState()
{ {
if (Preferences::instance()->resolvePeerCountries() != m_displayFlags) { if (Preferences::instance()->resolvePeerCountries() != m_displayFlags) {
m_displayFlags = !m_displayFlags; m_displayFlags = !m_displayFlags;
if (m_displayFlags) { if (m_displayFlags) {
loadPeers(m_properties->getCurrentTorrent()); loadPeers(m_properties->getCurrentTorrent());
showColumn(PeerListDelegate::COUNTRY); showColumn(PeerListDelegate::COUNTRY);
resizeColumnToContents(PeerListDelegate::COUNTRY); resizeColumnToContents(PeerListDelegate::COUNTRY);
}
else {
hideColumn(PeerListDelegate::COUNTRY);
}
} }
else {
hideColumn(PeerListDelegate::COUNTRY);
}
}
} }
void PeerListWidget::showPeerListMenu(const QPoint&) void PeerListWidget::showPeerListMenu(const QPoint&)
{ {
QMenu menu; QMenu menu;
bool empty_menu = true; bool emptyMenu = true;
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent(); BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
if (!torrent) return; if (!torrent) return;
// Add Peer Action // Add Peer Action
QAction *addPeerAct = 0; QAction *addPeerAct = 0;
if (!torrent->isQueued() && !torrent->isChecking()) { if (!torrent->isQueued() && !torrent->isChecking()) {
addPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer...")); addPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer..."));
empty_menu = false; emptyMenu = false;
} }
QAction *banAct = 0; QAction *banAct = 0;
QAction *copyPeerAct = 0; QAction *copyPeerAct = 0;
if (!selectionModel()->selectedRows().isEmpty()) { if (!selectionModel()->selectedRows().isEmpty()) {
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy selected")); copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy selected"));
menu.addSeparator(); menu.addSeparator();
banAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently")); banAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently"));
empty_menu = false; emptyMenu = false;
} }
if (empty_menu) return; if (emptyMenu) return;
QAction *act = menu.exec(QCursor::pos()); QAction *act = menu.exec(QCursor::pos());
if (act == 0) return; if (act == 0) return;
if (act == addPeerAct) { if (act == addPeerAct) {
QList<BitTorrent::PeerAddress> peersList = PeersAdditionDlg::askForPeers(); QList<BitTorrent::PeerAddress> peersList = PeersAdditionDlg::askForPeers();
int peerCount = 0; int peerCount = 0;
foreach (const BitTorrent::PeerAddress &addr, peersList) { foreach (const BitTorrent::PeerAddress &addr, peersList) {
if (torrent->connectPeer(addr)) { if (torrent->connectPeer(addr)) {
qDebug("Adding peer %s...", qPrintable(addr.ip.toString())); qDebug("Adding peer %s...", qPrintable(addr.ip.toString()));
Logger::instance()->addMessage(tr("Manually adding peer '%1'...").arg(addr.ip.toString())); Logger::instance()->addMessage(tr("Manually adding peer '%1'...").arg(addr.ip.toString()));
peerCount++; peerCount++;
} }
else { else {
Logger::instance()->addMessage(tr("The peer '%1' could not be added to this torrent.").arg(addr.ip.toString()), Log::WARNING); Logger::instance()->addMessage(tr("The peer '%1' could not be added to this torrent.").arg(addr.ip.toString()), Log::WARNING);
} }
}
if (peerCount < peersList.length())
QMessageBox::information(0, tr("Peer addition"), tr("Some peers could not be added. Check the Log for details."));
else if (peerCount > 0)
QMessageBox::information(0, tr("Peer addition"), tr("The peers were added to this torrent."));
return;
}
if (act == banAct) {
banSelectedPeers();
return;
}
if (act == copyPeerAct) {
copySelectedPeers();
return;
} }
if (peerCount < peersList.length())
QMessageBox::information(0, tr("Peer addition"), tr("Some peers could not be added. Check the Log for details."));
else if (peerCount > 0)
QMessageBox::information(0, tr("Peer addition"), tr("The peers were added to this torrent."));
return;
}
if (act == banAct) {
banSelectedPeers();
return;
}
if (act == copyPeerAct) {
copySelectedPeers();
return;
}
} }
void PeerListWidget::banSelectedPeers() void PeerListWidget::banSelectedPeers()
{ {
// Confirm first // Confirm first
int ret = QMessageBox::question(this, tr("Ban peer permanently"), tr("Are you sure you want to ban permanently the selected peers?"), int ret = QMessageBox::question(this, tr("Ban peer permanently"), tr("Are you sure you want to ban permanently the selected peers?"),
tr("&Yes"), tr("&No"), tr("&Yes"), tr("&No"),
QString(), 0, 1); QString(), 0, 1);
if (ret) if (ret)
return; return;
QModelIndexList selectedIndexes = selectionModel()->selectedRows(); QModelIndexList selectedIndexes = selectionModel()->selectedRows();
foreach (const QModelIndex &index, selectedIndexes) { foreach (const QModelIndex &index, selectedIndexes) {
int row = m_proxyModel->mapToSource(index).row(); int row = m_proxyModel->mapToSource(index).row();
QString ip = m_listModel->data(m_listModel->index(row, PeerListDelegate::IP_HIDDEN)).toString(); QString ip = m_listModel->data(m_listModel->index(row, PeerListDelegate::IP_HIDDEN)).toString();
qDebug("Banning peer %s...", ip.toLocal8Bit().data()); qDebug("Banning peer %s...", ip.toLocal8Bit().data());
Logger::instance()->addMessage(tr("Manually banning peer '%1'...").arg(ip)); Logger::instance()->addMessage(tr("Manually banning peer '%1'...").arg(ip));
BitTorrent::Session::instance()->banIP(ip); BitTorrent::Session::instance()->banIP(ip);
} }
// Refresh list // Refresh list
loadPeers(m_properties->getCurrentTorrent()); loadPeers(m_properties->getCurrentTorrent());
} }
void PeerListWidget::copySelectedPeers() void PeerListWidget::copySelectedPeers()
{ {
QModelIndexList selectedIndexes = selectionModel()->selectedRows(); QModelIndexList selectedIndexes = selectionModel()->selectedRows();
QStringList selectedPeers; QStringList selectedPeers;
foreach (const QModelIndex &index, selectedIndexes) { foreach (const QModelIndex &index, selectedIndexes) {
int row = m_proxyModel->mapToSource(index).row(); int row = m_proxyModel->mapToSource(index).row();
QString ip = m_listModel->data(m_listModel->index(row, PeerListDelegate::IP_HIDDEN)).toString(); QString ip = m_listModel->data(m_listModel->index(row, PeerListDelegate::IP_HIDDEN)).toString();
QString myport = m_listModel->data(m_listModel->index(row, PeerListDelegate::PORT)).toString(); QString myport = m_listModel->data(m_listModel->index(row, PeerListDelegate::PORT)).toString();
if (ip.indexOf(".") == -1) // IPv6 if (ip.indexOf(".") == -1) // IPv6
selectedPeers << "[" + ip + "]:" + myport; selectedPeers << "[" + ip + "]:" + myport;
else // IPv4 else // IPv4
selectedPeers << ip + ":" + myport; selectedPeers << ip + ":" + myport;
}
QApplication::clipboard()->setText(selectedPeers.join("\n"));
}
void PeerListWidget::clear() {
qDebug("clearing peer list");
m_peerItems.clear();
m_peerAddresses.clear();
m_missingFlags.clear();
int nbrows = m_listModel->rowCount();
if (nbrows > 0) {
qDebug("Cleared %d peers", nbrows);
m_listModel->removeRows(0, nbrows);
}
}
void PeerListWidget::loadSettings() {
header()->restoreState(Preferences::instance()->getPeerListState());
}
void PeerListWidget::saveSettings() const {
Preferences::instance()->setPeerListState(header()->saveState());
}
void PeerListWidget::loadPeers(BitTorrent::TorrentHandle *const torrent, bool force_hostname_resolution) {
if (!torrent) return;
QList<BitTorrent::PeerInfo> peers = torrent->peers();
QSet<QString> old_peers_set = m_peerItems.keys().toSet();
foreach (const BitTorrent::PeerInfo &peer, peers) {
BitTorrent::PeerAddress addr = peer.address();
if (addr.ip.isNull()) continue;
QString peer_ip = addr.ip.toString();
if (m_peerItems.contains(peer_ip)) {
// Update existing peer
updatePeer(peer_ip, torrent, peer);
old_peers_set.remove(peer_ip);
if (force_hostname_resolution && m_resolver) {
m_resolver->resolve(peer_ip);
}
} else {
// Add new peer
m_peerItems[peer_ip] = addPeer(peer_ip, torrent, peer);
m_peerAddresses[peer_ip] = addr;
// Resolve peer host name is asked
if (m_resolver)
m_resolver->resolve(peer_ip);
} }
} QApplication::clipboard()->setText(selectedPeers.join("\n"));
// Delete peers that are gone
QSetIterator<QString> it(old_peers_set);
while(it.hasNext()) {
const QString& ip = it.next();
m_missingFlags.remove(ip);
m_peerAddresses.remove(ip);
QStandardItem *item = m_peerItems.take(ip);
m_listModel->removeRow(item->row());
}
} }
QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer) { void PeerListWidget::clear()
int row = m_listModel->rowCount(); {
// Adding Peer to peer list qDebug("clearing peer list");
m_listModel->insertRow(row); m_peerItems.clear();
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip); m_peerAddresses.clear();
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip, Qt::ToolTipRole); m_missingFlags.clear();
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port); int nbrows = m_listModel->rowCount();
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip); if (nbrows > 0) {
if (m_displayFlags) { qDebug("Cleared %d peers", nbrows);
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country()); m_listModel->removeRows(0, nbrows);
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
const QString country_name = Net::GeoIPManager::CountryName(peer.country());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole);
} else {
m_missingFlags.insert(ip);
} }
}
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), peer.connectionType());
QString flags, tooltip;
getFlags(peer, flags, tooltip);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), flags);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), tooltip, Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(torrent->pieces(), peer.pieces()));
return m_listModel->item(row, PeerListDelegate::IP);
} }
void PeerListWidget::updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer) { void PeerListWidget::loadSettings()
QStandardItem *item = m_peerItems.value(ip); {
int row = item->row(); header()->restoreState(Preferences::instance()->getPeerListState());
if (m_displayFlags) { }
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country());
if (!ico.isNull()) { void PeerListWidget::saveSettings() const
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole); {
const QString country_name = Net::GeoIPManager::CountryName(peer.country()); Preferences::instance()->setPeerListState(header()->saveState());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole); }
m_missingFlags.remove(ip);
void PeerListWidget::loadPeers(BitTorrent::TorrentHandle *const torrent, bool forceHostnameResolution)
{
if (!torrent) return;
QList<BitTorrent::PeerInfo> peers = torrent->peers();
QSet<QString> oldeersSet = m_peerItems.keys().toSet();
foreach (const BitTorrent::PeerInfo &peer, peers) {
BitTorrent::PeerAddress addr = peer.address();
if (addr.ip.isNull()) continue;
QString peerIp = addr.ip.toString();
if (m_peerItems.contains(peerIp)) {
// Update existing peer
updatePeer(peerIp, torrent, peer);
oldeersSet.remove(peerIp);
if (forceHostnameResolution && m_resolver)
m_resolver->resolve(peerIp);
}
else {
// Add new peer
m_peerItems[peerIp] = addPeer(peerIp, torrent, peer);
m_peerAddresses[peerIp] = addr;
// Resolve peer host name is asked
if (m_resolver)
m_resolver->resolve(peerIp);
}
}
// Delete peers that are gone
QSetIterator<QString> it(oldeersSet);
while (it.hasNext()) {
const QString& ip = it.next();
m_missingFlags.remove(ip);
m_peerAddresses.remove(ip);
QStandardItem *item = m_peerItems.take(ip);
m_listModel->removeRow(item->row());
} }
}
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), peer.connectionType());
QString flags, tooltip;
getFlags(peer, flags, tooltip);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), flags);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), tooltip, Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(torrent->pieces(), peer.pieces()));
} }
void PeerListWidget::handleResolved(const QString &ip, const QString &hostname) { QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer)
QStandardItem *item = m_peerItems.value(ip, 0); {
if (item) { int row = m_listModel->rowCount();
qDebug("Resolved %s -> %s", qPrintable(ip), qPrintable(hostname)); // Adding Peer to peer list
item->setData(hostname, Qt::DisplayRole); m_listModel->insertRow(row);
} m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip, Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip);
if (m_displayFlags) {
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
const QString countryName = Net::GeoIPManager::CountryName(peer.country());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), countryName, Qt::ToolTipRole);
}
else {
m_missingFlags.insert(ip);
}
}
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), peer.connectionType());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flags());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flagsDescription(), Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), peer.relevance());
return m_listModel->item(row, PeerListDelegate::IP);
}
void PeerListWidget::updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer)
{
QStandardItem *item = m_peerItems.value(ip);
int row = item->row();
if (m_displayFlags) {
const QIcon ico = GuiIconProvider::instance()->getFlagIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
const QString countryName = Net::GeoIPManager::CountryName(peer.country());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), countryName, Qt::ToolTipRole);
m_missingFlags.remove(ip);
}
}
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), peer.connectionType());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flags());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flagsDescription(), Qt::ToolTipRole);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), peer.relevance());
}
void PeerListWidget::handleResolved(const QString &ip, const QString &hostname)
{
QStandardItem *item = m_peerItems.value(ip, 0);
if (item) {
qDebug("Resolved %s -> %s", qPrintable(ip), qPrintable(hostname));
item->setData(hostname, Qt::DisplayRole);
}
} }
void PeerListWidget::handleSortColumnChanged(int col) void PeerListWidget::handleSortColumnChanged(int col)
{ {
if (col == PeerListDelegate::COUNTRY) { if (col == PeerListDelegate::COUNTRY) {
qDebug("Sorting by decoration"); qDebug("Sorting by decoration");
m_proxyModel->setSortRole(Qt::ToolTipRole); m_proxyModel->setSortRole(Qt::ToolTipRole);
} else {
m_proxyModel->setSortRole(Qt::DisplayRole);
}
}
void PeerListWidget::getFlags(const BitTorrent::PeerInfo &peer, QString& flags, QString& tooltip)
{
if (peer.isInteresting()) {
//d = Your client wants to download, but peer doesn't want to send (interested and choked)
if (peer.isRemoteChocked()) {
flags += "d ";
tooltip += tr("interested(local) and choked(peer)");
tooltip += ", ";
} }
else { else {
//D = Currently downloading (interested and not choked) m_proxyModel->setSortRole(Qt::DisplayRole);
flags += "D ";
tooltip += tr("interested(local) and unchoked(peer)");
tooltip += ", ";
} }
}
if (peer.isRemoteInterested()) {
//u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
if (peer.isChocked()) {
flags += "u ";
tooltip += tr("interested(peer) and choked(local)");
tooltip += ", ";
}
else {
//U = Currently uploading (interested and not choked)
flags += "U ";
tooltip += tr("interested(peer) and unchoked(local)");
tooltip += ", ";
}
}
//O = Optimistic unchoke
if (peer.optimisticUnchoke()) {
flags += "O ";
tooltip += tr("optimistic unchoke");
tooltip += ", ";
}
//S = Peer is snubbed
if (peer.isSnubbed()) {
flags += "S ";
tooltip += tr("peer snubbed");
tooltip += ", ";
}
//I = Peer is an incoming connection
if (!peer.isLocalConnection()) {
flags += "I ";
tooltip += tr("incoming connection");
tooltip += ", ";
}
//K = Peer is unchoking your client, but your client is not interested
if (!peer.isRemoteChocked() && !peer.isInteresting()) {
flags += "K ";
tooltip += tr("not interested(local) and unchoked(peer)");
tooltip += ", ";
}
//? = Your client unchoked the peer but the peer is not interested
if (!peer.isChocked() && !peer.isRemoteInterested()) {
flags += "? ";
tooltip += tr("not interested(peer) and unchoked(local)");
tooltip += ", ";
}
//X = Peer was included in peerlists obtained through Peer Exchange (PEX)
if (peer.fromPeX()) {
flags += "X ";
tooltip += tr("peer from PEX");
tooltip += ", ";
}
//H = Peer was obtained through DHT
if (peer.fromDHT()) {
flags += "H ";
tooltip += tr("peer from DHT");
tooltip += ", ";
}
//E = Peer is using Protocol Encryption (all traffic)
if (peer.isRC4Encrypted()) {
flags += "E ";
tooltip += tr("encrypted traffic");
tooltip += ", ";
}
//e = Peer is using Protocol Encryption (handshake)
if (peer.isPlaintextEncrypted()) {
flags += "e ";
tooltip += tr("encrypted handshake");
tooltip += ", ";
}
//P = Peer is using uTorrent uTP
if (peer.useUTPSocket()) {
flags += "P ";
tooltip += QString::fromUtf8(C_UTP);
tooltip += ", ";
}
//L = Peer is local
if (peer.fromLSD()) {
flags += "L";
tooltip += tr("peer from LSD");
}
flags = flags.trimmed();
tooltip = tooltip.trimmed();
if (tooltip.endsWith(',', Qt::CaseInsensitive))
tooltip.chop(1);
} }
qreal PeerListWidget::getPeerRelevance(const QBitArray &allPieces, const QBitArray &peerPieces)
{
int localMissing = 0;
int remoteHaves = 0;
for (int i = 0; i < allPieces.size(); ++i) {
if (!allPieces[i]) {
++localMissing;
if (peerPieces[i])
++remoteHaves;
}
}
if (localMissing == 0)
return 0.0;
return static_cast<qreal>(remoteHaves) / localMissing;
}

View File

@ -54,52 +54,46 @@ QT_END_NAMESPACE
namespace BitTorrent namespace BitTorrent
{ {
class TorrentHandle;
class TorrentHandle; class PeerInfo;
class PeerInfo; struct PeerAddress;
struct PeerAddress;
} }
class PeerListWidget : public QTreeView { class PeerListWidget: public QTreeView
Q_OBJECT {
Q_OBJECT
public: public:
PeerListWidget(PropertiesWidget *parent); explicit PeerListWidget(PropertiesWidget *parent);
~PeerListWidget(); ~PeerListWidget();
public slots: void loadPeers(BitTorrent::TorrentHandle *const torrent, bool forceHostnameResolution = false);
void loadPeers(BitTorrent::TorrentHandle *const torrent, bool force_hostname_resolution = false); QStandardItem *addPeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer);
QStandardItem *addPeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer); void updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer);
void updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer); void handleResolved(const QString &ip, const QString &hostname);
void handleResolved(const QString &ip, const QString &hostname); void updatePeerHostNameResolutionState();
void updatePeerHostNameResolutionState(); void updatePeerCountryResolutionState();
void updatePeerCountryResolutionState(); void clear();
void clear();
protected slots: private slots:
void loadSettings(); void loadSettings();
void saveSettings() const; void saveSettings() const;
void showPeerListMenu(const QPoint&); void showPeerListMenu(const QPoint&);
void banSelectedPeers(); void banSelectedPeers();
void copySelectedPeers(); void copySelectedPeers();
void handleSortColumnChanged(int col); void handleSortColumnChanged(int col);
private: private:
static void getFlags(const BitTorrent::PeerInfo &peer, QString &flags, QString &tooltip); QStandardItemModel *m_listModel;
qreal getPeerRelevance(const QBitArray &allPieces, const QBitArray &peerPieces); PeerListDelegate *m_listDelegate;
PeerListSortModel *m_proxyModel;
private: QHash<QString, QStandardItem*> m_peerItems;
QStandardItemModel *m_listModel; QHash<QString, BitTorrent::PeerAddress> m_peerAddresses;
PeerListDelegate *m_listDelegate; QSet<QString> m_missingFlags;
PeerListSortModel *m_proxyModel; QPointer<Net::ReverseResolution> m_resolver;
QHash<QString, QStandardItem*> m_peerItems; PropertiesWidget *m_properties;
QHash<QString, BitTorrent::PeerAddress> m_peerAddresses; bool m_displayFlags;
QSet<QString> m_missingFlags; QShortcut *m_copyHotkey;
QPointer<Net::ReverseResolution> m_resolver;
PropertiesWidget *m_properties;
bool m_displayFlags;
QShortcut *copyHotkey;
}; };
#endif // PEERLISTWIDGET_H #endif // PEERLISTWIDGET_H

View File

@ -36,7 +36,9 @@
#include "base/bittorrent/sessionstatus.h" #include "base/bittorrent/sessionstatus.h"
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"
#include "base/bittorrent/trackerentry.h" #include "base/bittorrent/trackerentry.h"
#include "base/bittorrent/peerinfo.h"
#include "base/torrentfilter.h" #include "base/torrentfilter.h"
#include "base/net/geoipmanager.h"
#include "jsonutils.h" #include "jsonutils.h"
#include <QDebug> #include <QDebug>
@ -105,6 +107,22 @@ static const char KEY_TORRENT_SUPER_SEEDING[] = "super_seeding";
static const char KEY_TORRENT_FORCE_START[] = "force_start"; static const char KEY_TORRENT_FORCE_START[] = "force_start";
static const char KEY_TORRENT_SAVE_PATH[] = "save_path"; static const char KEY_TORRENT_SAVE_PATH[] = "save_path";
// Peer keys
static const char KEY_PEER_IP[] = "ip";
static const char KEY_PEER_PORT[] = "port";
static const char KEY_PEER_COUNTRY_CODE[] = "country_code";
static const char KEY_PEER_COUNTRY[] = "country";
static const char KEY_PEER_CLIENT[] = "client";
static const char KEY_PEER_PROGRESS[] = "progress";
static const char KEY_PEER_DOWN_SPEED[] = "dl_speed";
static const char KEY_PEER_UP_SPEED[] = "up_speed";
static const char KEY_PEER_TOT_DOWN[] = "downloaded";
static const char KEY_PEER_TOT_UP[] = "uploaded";
static const char KEY_PEER_CONNECTION_TYPE[] = "connection";
static const char KEY_PEER_FLAGS[] = "flags";
static const char KEY_PEER_FLAGS_DESCRIPTION[] = "flags_desc";
static const char KEY_PEER_RELEVANCE[] = "relevance";
// Tracker keys // Tracker keys
static const char KEY_TRACKER_URL[] = "url"; static const char KEY_TRACKER_URL[] = "url";
static const char KEY_TRACKER_STATUS[] = "status"; static const char KEY_TRACKER_STATUS[] = "status";
@ -171,6 +189,9 @@ static const char KEY_SYNC_MAINDATA_QUEUEING[] = "queueing";
static const char KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS[] = "use_alt_speed_limits"; static const char KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS[] = "use_alt_speed_limits";
static const char KEY_SYNC_MAINDATA_REFRESH_INTERVAL[] = "refresh_interval"; static const char KEY_SYNC_MAINDATA_REFRESH_INTERVAL[] = "refresh_interval";
// Sync torrent peers keys
static const char KEY_SYNC_TORRENT_PEERS_SHOW_FLAGS[] = "show_flags";
static const char KEY_FULL_UPDATE[] = "full_update"; static const char KEY_FULL_UPDATE[] = "full_update";
static const char KEY_RESPONSE_ID[] = "rid"; static const char KEY_RESPONSE_ID[] = "rid";
static const char KEY_SUFFIX_REMOVED[] = "_removed"; static const char KEY_SUFFIX_REMOVED[] = "_removed";
@ -354,6 +375,54 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData
return json::toJson(generateSyncData(acceptedResponseId, data, lastAcceptedData, lastData)); return json::toJson(generateSyncData(acceptedResponseId, data, lastAcceptedData, lastData));
} }
QByteArray btjson::getSyncTorrentPeersData(int acceptedResponseId, QString hash, QVariantMap &lastData, QVariantMap &lastAcceptedData)
{
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
if (!torrent) {
qWarning() << Q_FUNC_INFO << "Invalid torrent " << qPrintable(hash);
return QByteArray();
}
QVariantMap data;
QVariantHash peers;
QList<BitTorrent::PeerInfo> peersList = torrent->peers();
#ifndef DISABLE_COUNTRIES_RESOLUTION
bool resolvePeerCountries = Preferences::instance()->resolvePeerCountries();
#else
bool resolvePeerCountries = false;
#endif
data[KEY_SYNC_TORRENT_PEERS_SHOW_FLAGS] = resolvePeerCountries;
foreach (const BitTorrent::PeerInfo &pi, peersList) {
if (pi.address().ip.isNull()) continue;
QVariantMap peer;
#ifndef DISABLE_COUNTRIES_RESOLUTION
if (resolvePeerCountries) {
peer[KEY_PEER_COUNTRY_CODE] = pi.country().toLower();
peer[KEY_PEER_COUNTRY] = Net::GeoIPManager::CountryName(pi.country());
}
#endif
peer[KEY_PEER_IP] = pi.address().ip.toString();
peer[KEY_PEER_PORT] = pi.address().port;
peer[KEY_PEER_CLIENT] = pi.client();
peer[KEY_PEER_PROGRESS] = pi.progress();
peer[KEY_PEER_DOWN_SPEED] = pi.payloadDownSpeed();
peer[KEY_PEER_UP_SPEED] = pi.payloadUpSpeed();
peer[KEY_PEER_TOT_DOWN] = pi.totalDownload();
peer[KEY_PEER_TOT_UP] = pi.totalUpload();
peer[KEY_PEER_CONNECTION_TYPE] = pi.connectionType();
peer[KEY_PEER_FLAGS] = pi.flags();
peer[KEY_PEER_FLAGS_DESCRIPTION] = pi.flagsDescription();
peer[KEY_PEER_RELEVANCE] = pi.relevance();
peers[pi.address().ip.toString() + ":" + QString::number(pi.address().port)] = peer;
}
data["peers"] = peers;
return json::toJson(generateSyncData(acceptedResponseId, data, lastAcceptedData, lastData));
}
/** /**
* Returns the trackers for a torrent in JSON format. * Returns the trackers for a torrent in JSON format.
* *
@ -610,7 +679,7 @@ QByteArray btjson::getTorrentsRatesLimits(QStringList &hashes, bool downloadLimi
map[hash] = limit; map[hash] = limit;
} }
return json::toJson(map); return json::toJson(map);
} }
QVariantMap toMap(BitTorrent::TorrentHandle *const torrent) QVariantMap toMap(BitTorrent::TorrentHandle *const torrent)

View File

@ -46,6 +46,7 @@ public:
static QByteArray getTorrents(QString filter = "all", QString label = QString(), static QByteArray getTorrents(QString filter = "all", QString label = QString(),
QString sortedColumn = "name", bool reverse = false, int limit = 0, int offset = 0); QString sortedColumn = "name", bool reverse = false, int limit = 0, int offset = 0);
static QByteArray getSyncMainData(int acceptedResponseId, QVariantMap &lastData, QVariantMap &lastAcceptedData); static QByteArray getSyncMainData(int acceptedResponseId, QVariantMap &lastData, QVariantMap &lastAcceptedData);
static QByteArray getSyncTorrentPeersData(int acceptedResponseId, QString hash, QVariantMap &lastData, QVariantMap &lastAcceptedData);
static QByteArray getTrackersForTorrent(const QString& hash); static QByteArray getTrackersForTorrent(const QString& hash);
static QByteArray getWebSeedsForTorrent(const QString& hash); static QByteArray getWebSeedsForTorrent(const QString& hash);
static QByteArray getPropertiesForTorrent(const QString& hash); static QByteArray getPropertiesForTorrent(const QString& hash);

View File

@ -82,6 +82,7 @@ QMap<QString, QMap<QString, WebApplication::Action> > WebApplication::initialize
ADD_ACTION(query, propertiesWebSeeds); ADD_ACTION(query, propertiesWebSeeds);
ADD_ACTION(query, propertiesFiles); ADD_ACTION(query, propertiesFiles);
ADD_ACTION(sync, maindata); ADD_ACTION(sync, maindata);
ADD_ACTION(sync, torrent_peers);
ADD_ACTION(command, shutdown); ADD_ACTION(command, shutdown);
ADD_ACTION(command, download); ADD_ACTION(command, download);
ADD_ACTION(command, upload); ADD_ACTION(command, upload);
@ -277,6 +278,19 @@ void WebApplication::action_sync_maindata()
session()->syncMainDataLastAcceptedResponse), Http::CONTENT_TYPE_JSON); session()->syncMainDataLastAcceptedResponse), Http::CONTENT_TYPE_JSON);
} }
// GET param:
// - hash (string): torrent hash
// - rid (int): last response id
void WebApplication::action_sync_torrent_peers()
{
CHECK_URI(0);
print(btjson::getSyncTorrentPeersData(request().gets["rid"].toInt(),
request().gets["hash"],
session()->syncTorrentPeersLastResponse,
session()->syncTorrentPeersLastAcceptedResponse), Http::CONTENT_TYPE_JSON);
}
void WebApplication::action_version_api() void WebApplication::action_version_api()
{ {
CHECK_URI(0); CHECK_URI(0);

View File

@ -55,6 +55,7 @@ private:
void action_query_propertiesWebSeeds(); void action_query_propertiesWebSeeds();
void action_query_propertiesFiles(); void action_query_propertiesFiles();
void action_sync_maindata(); void action_sync_maindata();
void action_sync_torrent_peers();
void action_command_shutdown(); void action_command_shutdown();
void action_command_download(); void action_command_download();
void action_command_upload(); void action_command_upload();

View File

@ -35,6 +35,8 @@ struct WebSessionData
{ {
QVariantMap syncMainDataLastResponse; QVariantMap syncMainDataLastResponse;
QVariantMap syncMainDataLastAcceptedResponse; QVariantMap syncMainDataLastAcceptedResponse;
QVariantMap syncTorrentPeersLastResponse;
QVariantMap syncTorrentPeersLastAcceptedResponse;
}; };
#endif // WEBSESSIONDATA #endif // WEBSESSIONDATA

View File

@ -29,7 +29,8 @@
} }
#myTable tr:nth-child(even), #torrentsTable tr:nth-child(even),
#torrentPeersTable tr:nth-child(even),
#filesTable tr:nth-child(even), #filesTable tr:nth-child(even),
#properties #torrentFiles tr.alt, #properties #torrentFiles tr.alt,
#properties #trackers tr.alt, #properties #trackers tr.alt,
@ -50,8 +51,13 @@
background-color: #415A8D; background-color: #415A8D;
color: #fff; color: #fff;
} }
#torrentPeersTable tr.selected {
background-color: #354158;
color: #fff;
}
#myTable tr:hover, #torrentsTable tr:hover,
#torrentPeersTable tr:hover,
#filesTable tr:hover, #filesTable tr:hover,
#properties #torrentFiles tr.over, #properties #torrentFiles tr.over,
#properties #trackers tr.over, #properties #trackers tr.over,
@ -60,7 +66,8 @@
color: #fff; color: #fff;
} }
#myTable tr:hover, #torrentsTable tr:hover,
#torrentsTable tr:hover,
#properties #torrentFiles tr.over, #properties #torrentFiles tr.over,
#properties #trackers tr.over, #properties #trackers tr.over,
#transferList tr.over { #transferList tr.over {

View File

@ -2,6 +2,7 @@
<ul id="propertiesTabs" class="tab-menu"> <ul id="propertiesTabs" class="tab-menu">
<li id="PropGeneralLink" class="selected"><a>QBT_TR(General)QBT_TR</a></li> <li id="PropGeneralLink" class="selected"><a>QBT_TR(General)QBT_TR</a></li>
<li id="PropTrackersLink"><a>QBT_TR(Trackers)QBT_TR</a></li> <li id="PropTrackersLink"><a>QBT_TR(Trackers)QBT_TR</a></li>
<li id="PropPeersLink"><a>QBT_TR(Peers)QBT_TR</a></li>
<li id="PropWebSeedsLink"><a>QBT_TR(HTTP Sources)QBT_TR</a></li> <li id="PropWebSeedsLink"><a>QBT_TR(HTTP Sources)QBT_TR</a></li>
<li id="PropFilesLink"><a>QBT_TR(Content)QBT_TR</a></li> <li id="PropFilesLink"><a>QBT_TR(Content)QBT_TR</a></li>
</ul> </ul>

View File

@ -63,6 +63,18 @@
</div> </div>
</div> </div>
<div id="prop_peers" class="invisible">
<div id="peers">
<table class="torrentTable" cellpadding="0" cellspacing="0" style="width: 100%">
<thead>
<tr id="torrentPeersTableHeader">
</tr>
</thead>
<tbody id="torrentPeersTable"></tbody>
</table>
</div>
</div>
<div id="prop_webseeds" class="invisible"> <div id="prop_webseeds" class="invisible">
<div id="webseeds"> <div id="webseeds">
<table class="torrentTable" cellpadding="0" cellspacing="0" style="width: 100%"> <table class="torrentTable" cellpadding="0" cellspacing="0" style="width: 100%">
@ -92,3 +104,7 @@
</table> </table>
</div> </div>
</div> </div>
<script type="text/javascript">
torrentPeersTable.setup('torrentPeersTable', 'torrentPeersTableHeader', null);
</script>

View File

@ -22,9 +22,11 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
myTable = new dynamicTable(); torrentsTable = new TorrentsTable();
torrentPeersTable = new TorrentPeersTable();
var updatePropertiesPanel = function(){}; var updatePropertiesPanel = function(){};
var updateTorrentPeersData = function(){};
var updateMainData = function(){}; var updateMainData = function(){};
var alternativeSpeedLimits = false; var alternativeSpeedLimits = false;
var queueing_enabled = true; var queueing_enabled = true;
@ -99,7 +101,7 @@ window.addEvent('load', function () {
selected_label = hash; selected_label = hash;
localStorage.setItem('selected_label', selected_label); localStorage.setItem('selected_label', selected_label);
highlightSelectedLabel(); highlightSelectedLabel();
if (typeof myTable.table != 'undefined') if (typeof torrentsTable.table != 'undefined')
updateMainData(); updateMainData();
}; };
@ -117,7 +119,7 @@ window.addEvent('load', function () {
selected_filter = f; selected_filter = f;
localStorage.setItem('selected_filter', f); localStorage.setItem('selected_filter', f);
// Reload torrents // Reload torrents
if (typeof myTable.table != 'undefined') if (typeof torrentsTable.table != 'undefined')
updateMainData(); updateMainData();
} }
@ -230,9 +232,9 @@ window.addEvent('load', function () {
return new Element('li', {id: hash, html: html}); return new Element('li', {id: hash, html: html});
}; };
var all = myTable.getRowIds().length; var all = torrentsTable.getRowIds().length;
var unlabelled = 0; var unlabelled = 0;
Object.each(myTable.rows, function(row) { Object.each(torrentsTable.rows, function(row) {
if (row['full_data'].label.length === 0) if (row['full_data'].label.length === 0)
unlabelled += 1; unlabelled += 1;
}); });
@ -286,7 +288,7 @@ window.addEvent('load', function () {
var update_labels = false; var update_labels = false;
var full_update = (response['full_update'] == true); var full_update = (response['full_update'] == true);
if (full_update) { if (full_update) {
myTable.rows.erase(); torrentsTable.clear();
label_list = {}; label_list = {};
} }
if (response['rid']) { if (response['rid']) {
@ -309,19 +311,20 @@ window.addEvent('load', function () {
if (response['torrents']) { if (response['torrents']) {
for (var key in response['torrents']) { for (var key in response['torrents']) {
response['torrents'][key]['hash'] = key; response['torrents'][key]['hash'] = key;
myTable.updateRowData(response['torrents'][key]); response['torrents'][key]['rowId'] = key;
torrentsTable.updateRowData(response['torrents'][key]);
if (addTorrentToLabelList(response['torrents'][key])) if (addTorrentToLabelList(response['torrents'][key]))
update_labels = true; update_labels = true;
} }
} }
if (response['torrents_removed']) if (response['torrents_removed'])
response['torrents_removed'].each(function (hash) { response['torrents_removed'].each(function (hash) {
myTable.removeRow(hash); torrentsTable.removeRow(hash);
removeTorrentFromLabelList(hash); removeTorrentFromLabelList(hash);
update_labels = true; // Allways to update All label update_labels = true; // Allways to update All label
}); });
myTable.updateTable(full_update); torrentsTable.updateTable(full_update);
myTable.altRow(); torrentsTable.altRow();
if (response['server_state']) { if (response['server_state']) {
var tmp = response['server_state']; var tmp = response['server_state'];
for(var key in tmp) for(var key in tmp)
@ -340,7 +343,7 @@ window.addEvent('load', function () {
}; };
updateMainData = function() { updateMainData = function() {
myTable.updateTable(); torrentsTable.updateTable();
clearTimeout(syncMainDataTimer); clearTimeout(syncMainDataTimer);
syncMainDataTimer = syncMainData.delay(100); syncMainDataTimer = syncMainData.delay(100);
} }
@ -373,8 +376,8 @@ window.addEvent('load', function () {
if (queueing_enabled != serverState.queueing) { if (queueing_enabled != serverState.queueing) {
queueing_enabled = serverState.queueing; queueing_enabled = serverState.queueing;
myTable.columns['priority'].force_hide = !queueing_enabled; torrentsTable.columns['priority'].force_hide = !queueing_enabled;
myTable.updateColumn('priority'); torrentsTable.updateColumn('priority');
if (queueing_enabled) { if (queueing_enabled) {
$('queueingLinks').removeClass('invisible'); $('queueingLinks').removeClass('invisible');
$('queueingButtons').removeClass('invisible'); $('queueingButtons').removeClass('invisible');
@ -424,11 +427,6 @@ window.addEvent('load', function () {
$('DlInfos').addEvent('click', globalDownloadLimitFN); $('DlInfos').addEvent('click', globalDownloadLimitFN);
$('UpInfos').addEvent('click', globalUploadLimitFN); $('UpInfos').addEvent('click', globalUploadLimitFN);
setSortedColumn = function (column) {
myTable.setSortedColumn(column);
updateMainData();
};
$('showTopToolbarLink').addEvent('click', function(e) { $('showTopToolbarLink').addEvent('click', function(e) {
showTopToolbar = !showTopToolbar; showTopToolbar = !showTopToolbar;
localStorage.setItem('show_top_toolbar', showTopToolbar.toString()); localStorage.setItem('show_top_toolbar', showTopToolbar.toString());
@ -489,7 +487,7 @@ window.addEvent('load', function () {
}, },
contentURL : 'properties_content.html', contentURL : 'properties_content.html',
require : { require : {
css : ['css/Tabs.css'], css : ['css/Tabs.css', 'css/dynamicTable.css'],
js : ['scripts/prop-general.js', 'scripts/prop-trackers.js', 'scripts/prop-webseeds.js', 'scripts/prop-files.js'], js : ['scripts/prop-general.js', 'scripts/prop-trackers.js', 'scripts/prop-webseeds.js', 'scripts/prop-files.js'],
}, },
tabsURL : 'properties.html', tabsURL : 'properties.html',
@ -501,6 +499,8 @@ window.addEvent('load', function () {
updateTorrentData(); updateTorrentData();
else if (!$('prop_trackers').hasClass('invisible')) else if (!$('prop_trackers').hasClass('invisible'))
updateTrackersData(); updateTrackersData();
else if (!$('prop_peers').hasClass('invisible'))
updateTorrentPeersData();
else if (!$('prop_webseeds').hasClass('invisible')) else if (!$('prop_webseeds').hasClass('invisible'))
updateWebSeedsData(); updateWebSeedsData();
else if (!$('prop_files').hasClass('invisible')) else if (!$('prop_files').hasClass('invisible'))
@ -512,6 +512,7 @@ window.addEvent('load', function () {
$('prop_trackers').addClass("invisible"); $('prop_trackers').addClass("invisible");
$('prop_webseeds').addClass("invisible"); $('prop_webseeds').addClass("invisible");
$('prop_files').addClass("invisible"); $('prop_files').addClass("invisible");
$('prop_peers').addClass("invisible");
updatePropertiesPanel(); updatePropertiesPanel();
}); });
@ -520,6 +521,16 @@ window.addEvent('load', function () {
$('prop_general').addClass("invisible"); $('prop_general').addClass("invisible");
$('prop_webseeds').addClass("invisible"); $('prop_webseeds').addClass("invisible");
$('prop_files').addClass("invisible"); $('prop_files').addClass("invisible");
$('prop_peers').addClass("invisible");
updatePropertiesPanel();
});
$('PropPeersLink').addEvent('click', function(e){
$('prop_peers').removeClass("invisible");
$('prop_trackers').addClass("invisible");
$('prop_general').addClass("invisible");
$('prop_webseeds').addClass("invisible");
$('prop_files').addClass("invisible");
updatePropertiesPanel(); updatePropertiesPanel();
}); });
@ -528,6 +539,7 @@ window.addEvent('load', function () {
$('prop_general').addClass("invisible"); $('prop_general').addClass("invisible");
$('prop_trackers').addClass("invisible"); $('prop_trackers').addClass("invisible");
$('prop_files').addClass("invisible"); $('prop_files').addClass("invisible");
$('prop_peers').addClass("invisible");
updatePropertiesPanel(); updatePropertiesPanel();
}); });
@ -536,6 +548,7 @@ window.addEvent('load', function () {
$('prop_general').addClass("invisible"); $('prop_general').addClass("invisible");
$('prop_trackers').addClass("invisible"); $('prop_trackers').addClass("invisible");
$('prop_webseeds').addClass("invisible"); $('prop_webseeds').addClass("invisible");
$('prop_peers').addClass("invisible");
updatePropertiesPanel(); updatePropertiesPanel();
}); });
@ -556,7 +569,7 @@ var keyboardEvents = new Keyboard({
defaultEventType: 'keydown', defaultEventType: 'keydown',
events: { events: {
'ctrl+a': function(event) { 'ctrl+a': function(event) {
myTable.selectAll(); torrentsTable.selectAll();
event.preventDefault(); event.preventDefault();
}, },
'delete': function(event) { 'delete': function(event) {
@ -567,3 +580,78 @@ var keyboardEvents = new Keyboard({
}); });
keyboardEvents.activate(); keyboardEvents.activate();
var loadTorrentPeersTimer;
var syncTorrentPeersLastResponseId = 0;
var show_flags = true;
var loadTorrentPeersData = function(){
if ($('prop_peers').hasClass('invisible') ||
$('propertiesPanel_collapseToggle').hasClass('panel-expand')) {
syncTorrentPeersLastResponseId = 0;
torrentPeersTable.clear();
return;
}
var current_hash = torrentsTable.getCurrentTorrentHash();
if (current_hash == "") {
syncTorrentPeersLastResponseId = 0;
torrentPeersTable.clear();
clearTimeout(loadTorrentPeersTimer);
loadTorrentPeersTimer = loadTorrentPeersData.delay(syncMainDataTimerPeriod);
return;
}
var url = new URI('sync/torrent_peers');
url.setData('rid', syncTorrentPeersLastResponseId);
url.setData('hash', current_hash);
var request = new Request.JSON({
url: url,
noCache: true,
method: 'get',
onFailure: function() {
$('error_div').set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR');
clearTimeout(loadTorrentPeersTimer);
loadTorrentPeersTimer = loadTorrentPeersData.delay(5000);
},
onSuccess: function(response) {
$('error_div').set('html', '');
if (response) {
var full_update = (response['full_update'] == true);
if (full_update) {
torrentPeersTable.clear();
}
if (response['rid']) {
syncTorrentPeersLastResponseId = response['rid'];
}
if (response['peers']) {
for (var key in response['peers']) {
response['peers'][key]['rowId'] = key;
torrentPeersTable.updateRowData(response['peers'][key]);
}
}
if (response['peers_removed'])
response['peers_removed'].each(function (hash) {
torrentPeersTable.removeRow(hash);
});
torrentPeersTable.updateTable(full_update);
torrentPeersTable.altRow();
if (response['show_flags']) {
if (show_flags != response['show_flags']) {
show_flags = response['show_flags'];
torrentPeersTable.columns['country'].force_hide = !show_flags;
torrentPeersTable.updateColumn('country');
}
}
}
else {
torrentPeersTable.clear();
}
clearTimeout(loadTorrentPeersTimer);
loadTorrentPeersTimer = loadTorrentPeersData.delay(syncMainDataTimerPeriod);
}
}).send();
};
updateTorrentPeersData = function(){
clearTimeout(loadTorrentPeersTimer);
loadTorrentPeersData();
};

View File

@ -140,9 +140,9 @@ var ContextMenu = new Class({
there_are_force_start = false; there_are_force_start = false;
all_are_super_seeding = true; all_are_super_seeding = true;
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
h.each(function(item, index){ h.each(function(item, index){
var data = myTable.rows.get(item).full_data; var data = torrentsTable.rows.get(item).full_data;
if (data['seq_dl'] != true) if (data['seq_dl'] != true)
all_are_seq_dl = false; all_are_seq_dl = false;

View File

@ -31,55 +31,35 @@
**************************************************************/ **************************************************************/
var dynamicTable = new Class({ var DynamicTable = new Class({
initialize : function () {}, initialize : function () {},
setup : function (table, context_menu) { setup : function (tableId, tableHeaderId, context_menu) {
this.table = $(table); this.tableId = tableId;
this.tableHeaderId = tableHeaderId;
this.table = $(tableId);
this.rows = new Hash(); this.rows = new Hash();
this.cur = new Array(); this.cur = new Array();
this.columns = new Array(); this.columns = new Array();
this.context_menu = context_menu; this.context_menu = context_menu;
this.sortedColumn = getLocalStorageItem('sorted_column', 'name'); this.sortedColumn = getLocalStorageItem('sorted_column_' + this.tableId, 0);
this.reverseSort = getLocalStorageItem('reverse_sort', '0'); this.reverseSort = getLocalStorageItem('reverse_sort_' + this.tableId, '0');
this.initColumns(); this.initColumns();
this.loadColumnsOrder(); this.loadColumnsOrder();
this.updateHeader(); this.updateHeader();
}, },
initColumns : function () { initColumns : function () {},
this.newColumn('priority', 'width: 30px; cursor: pointer', '#');
this.newColumn('state_icon', 'width: 16px', '');
this.newColumn('name', 'min-width: 200px; cursor: pointer', 'QBT_TR(Name)QBT_TR');
this.newColumn('size', 'width: 100px; cursor: pointer', 'QBT_TR(Size)QBT_TR');
this.newColumn('progress', 'width: 80px; cursor: pointer', 'QBT_TR(Done)QBT_TR');
this.newColumn('num_seeds', 'width: 100px; cursor: pointer', 'QBT_TR(Seeds)QBT_TR');
this.newColumn('num_leechs', 'width: 100px; cursor: pointer', 'QBT_TR(Peers)QBT_TR');
this.newColumn('dlspeed', 'width: 100px; cursor: pointer', 'QBT_TR(Down Speed)QBT_TR');
this.newColumn('upspeed', 'width: 100px; cursor: pointer', 'QBT_TR(Up Speed)QBT_TR');
this.newColumn('eta', 'width: 100px; cursor: pointer', 'QBT_TR(ETA)QBT_TR');
this.newColumn('ratio', 'width: 100px; cursor: pointer', 'QBT_TR(Ratio)QBT_TR');
this.newColumn('label', 'width: 100px; cursor: pointer', 'QBT_TR(Label)QBT_TR');
this.columns['state_icon'].onclick = '';
this.columns['state_icon'].dataProperties[0] = 'state';
this.columns['num_seeds'].dataProperties.push('num_complete');
this.columns['num_leechs'].dataProperties.push('num_incomplete');
this.initColumnsFunctions();
},
newColumn : function (name, style, caption) { newColumn : function (name, style, caption) {
var column = {}; var column = {};
column['name'] = name; column['name'] = name;
column['visible'] = getLocalStorageItem('column_' + name + '_visible', '1'); column['visible'] = getLocalStorageItem('column_' + name + '_visible_' + this.tableId, '1');
column['force_hide'] = false; column['force_hide'] = false;
column['caption'] = caption; column['caption'] = caption;
column['style'] = style; column['style'] = style;
column['onclick'] = 'setSortedColumn(\'' + name + '\');'; column['onclick'] = 'this._this.setSortedColumn(\'' + name + '\');';
column['dataProperties'] = [name]; column['dataProperties'] = [name];
column['getRowValue'] = function (row, pos) { column['getRowValue'] = function (row, pos) {
if (pos == undefined) if (pos == undefined)
@ -99,12 +79,12 @@ var dynamicTable = new Class({
this.columns.push(column); this.columns.push(column);
this.columns[name] = column; this.columns[name] = column;
$('torrentTableHeader').appendChild(new Element('th')); $(this.tableHeaderId).appendChild(new Element('th'));
}, },
loadColumnsOrder : function () { loadColumnsOrder : function () {
columnsOrder = ['state_icon']; // status icon column is always the first columnsOrder = ['state_icon']; // status icon column is always the first
val = localStorage.getItem('columns_order'); val = localStorage.getItem('columns_order_' + this.tableId);
if (val === null || val === undefined) return; if (val === null || val === undefined) return;
val.split(',').forEach(function(v) { val.split(',').forEach(function(v) {
if ((v in this.columns) && (!columnsOrder.contains(v))) if ((v in this.columns) && (!columnsOrder.contains(v)))
@ -126,14 +106,15 @@ var dynamicTable = new Class({
val += ','; val += ',';
val += this.columns[i].name; val += this.columns[i].name;
} }
localStorage.setItem('columns_order', val); localStorage.setItem('columns_order_' + this.tableId, val);
}, },
updateHeader : function () { updateHeader : function () {
ths = $('torrentTableHeader').getElements('th'); var ths = $(this.tableHeaderId).getElements('th');
for (var i = 0; i < ths.length; i++) { for (var i = 0; i < ths.length; i++) {
th = ths[i]; th = ths[i];
th._this = this;
th.setAttribute('onclick', this.columns[i].onclick); th.setAttribute('onclick', this.columns[i].onclick);
th.innerHTML = this.columns[i].caption; th.innerHTML = this.columns[i].caption;
th.setAttribute('style', this.columns[i].style); th.setAttribute('style', this.columns[i].style);
@ -154,7 +135,7 @@ var dynamicTable = new Class({
updateColumn : function (columnName) { updateColumn : function (columnName) {
var pos = this.getColumnPos(columnName); var pos = this.getColumnPos(columnName);
var visible = ((this.columns[pos].visible != '0') && !this.columns[pos].force_hide); var visible = ((this.columns[pos].visible != '0') && !this.columns[pos].force_hide);
var ths = $('torrentTableHeader').getElements('th'); var ths = $(this.tableHeaderId).getElements('th');
if (visible) if (visible)
ths[pos].removeClass('invisible'); ths[pos].removeClass('invisible');
else else
@ -176,11 +157,12 @@ var dynamicTable = new Class({
// Toggle sort order // Toggle sort order
this.reverseSort = this.reverseSort == '0' ? '1' : '0'; this.reverseSort = this.reverseSort == '0' ? '1' : '0';
} }
localStorage.setItem('sorted_column', column); localStorage.setItem('sorted_column_' + this.tableId, column);
localStorage.setItem('reverse_sort', this.reverseSort); localStorage.setItem('reverse_sort_' + this.tableId, this.reverseSort);
this.updateTable(false);
}, },
getCurrentTorrentHash : function () { getSelectedRowId : function () {
if (this.cur.length > 0) if (this.cur.length > 0)
return this.cur[0]; return this.cur[0];
return ''; return '';
@ -206,19 +188,19 @@ var dynamicTable = new Class({
var trs = this.table.getElements('tr'); var trs = this.table.getElements('tr');
for (var i = 0; i < trs.length; i++) { for (var i = 0; i < trs.length; i++) {
var tr = trs[i]; var tr = trs[i];
this.cur.push(tr.hash); this.cur.push(tr.rowId);
if (!tr.hasClass('selected')) if (!tr.hasClass('selected'))
tr.addClass('selected'); tr.addClass('selected');
} }
}, },
selectRow : function (hash) { selectRow : function (rowId) {
this.cur.empty(); this.cur.empty();
this.cur.push(hash); this.cur.push(rowId);
var trs = this.table.getElements('tr'); var trs = this.table.getElements('tr');
for (var i = 0; i < trs.length; i++) { for (var i = 0; i < trs.length; i++) {
var tr = trs[i]; var tr = trs[i];
if (tr.hash == hash) { if (tr.rowId == rowId) {
if (!tr.hasClass('selected')) if (!tr.hasClass('selected'))
tr.addClass('selected'); tr.addClass('selected');
} }
@ -229,17 +211,17 @@ var dynamicTable = new Class({
}, },
updateRowData : function (data) { updateRowData : function (data) {
var hash = data['hash']; var rowId = data['rowId'];
var row; var row;
if (!this.rows.has(hash)) { if (!this.rows.has(rowId)) {
row = {}; row = {};
this.rows.set(hash, row); this.rows.set(rowId, row);
row['full_data'] = {}; row['full_data'] = {};
row['hash'] = hash; row['rowId'] = rowId;
} }
else else
row = this.rows.get(hash); row = this.rows.get(rowId);
row['data'] = data; row['data'] = data;
@ -247,64 +229,19 @@ var dynamicTable = new Class({
row['full_data'][x] = data[x]; row['full_data'][x] = data[x];
}, },
applyFilter : function (row, filterName, labelName) {
var state = row['full_data'].state;
switch(filterName) {
case 'downloading':
if (state != 'downloading' && !~state.indexOf('DL'))
return false;
break;
case 'seeding':
if (state != 'uploading' && state != 'forcedUP' && state != 'stalledUP' && state != 'queuedUP' && state != 'checkingUP')
return false;
break;
case 'completed':
if (state != 'uploading' && !~state.indexOf('UP'))
return false;
break;
case 'paused':
if (state != 'pausedDL')
return false;
break;
case 'resumed':
if (~state.indexOf('paused'))
return false;
break;
case 'active':
if (state != 'downloading' && state != 'forcedDL' && state != 'uploading' && state != 'forcedUP')
return false;
break;
case 'inactive':
if (state == 'downloading' || state == 'forcedDL' || state == 'uploading' || state == 'forcedUP')
return false;
break;
}
if (labelName == LABELS_ALL)
return true;
if (labelName == LABELS_UNLABELLED && row['full_data'].label.length === 0)
return true;
if (labelName != genHash( row['full_data'].label) )
return false;
return true;
},
getFilteredAndSortedRows : function () { getFilteredAndSortedRows : function () {
var filteredRows = new Array(); var filteredRows = new Array();
var rows = this.rows.getValues(); var rows = this.rows.getValues();
for (i = 0; i < rows.length; i++) for (i = 0; i < rows.length; i++)
if (this.applyFilter(rows[i], selected_filter, selected_label)) { {
filteredRows.push(rows[i]); filteredRows.push(rows[i]);
filteredRows[rows[i].hash] = rows[i]; filteredRows[rows[i].rowId] = rows[i];
} }
filteredRows.sort(function (row1, row2) { filteredRows.sort(function (row1, row2) {
column = this.columns[this.sortedColumn]; var column = this.columns[this.sortedColumn];
res = column.compareRows(row1, row2); res = column.compareRows(row1, row2);
if (this.reverseSort == '0') if (this.reverseSort == '0')
return res; return res;
@ -314,10 +251,10 @@ var dynamicTable = new Class({
return filteredRows; return filteredRows;
}, },
getTrByHash : function (hash) { getTrByRowId : function (rowId) {
trs = this.table.getElements('tr'); trs = this.table.getElements('tr');
for (var i = 0; i < trs.length; i++) for (var i = 0; i < trs.length; i++)
if (trs[i].hash == hash) if (trs[i].rowId == rowId)
return trs[i]; return trs[i];
return null; return null;
}, },
@ -337,10 +274,10 @@ var dynamicTable = new Class({
var trs = this.table.getElements('tr'); var trs = this.table.getElements('tr');
for (var rowPos = 0; rowPos < rows.length; rowPos++) { for (var rowPos = 0; rowPos < rows.length; rowPos++) {
var hash = rows[rowPos]['hash']; var rowId = rows[rowPos]['rowId'];
tr_found = false; tr_found = false;
for (j = rowPos; j < trs.length; j++) for (j = rowPos; j < trs.length; j++)
if (trs[j]['hash'] == hash) { if (trs[j]['rowId'] == rowId) {
trs[rowPos].removeClass('over'); trs[rowPos].removeClass('over');
tr_found = true; tr_found = true;
if (rowPos == j) if (rowPos == j)
@ -357,59 +294,49 @@ var dynamicTable = new Class({
var tr = new Element('tr'); var tr = new Element('tr');
tr.addClass("menu-target"); tr.addClass("menu-target");
tr['hash'] = rows[rowPos]['hash']; tr['rowId'] = rows[rowPos]['rowId'];
tr._this = this;
tr.addEvent('contextmenu', function (e) { tr.addEvent('contextmenu', function (e) {
if (!myTable.cur.contains(this.hash)) if (!this._this.cur.contains(this.rowId))
myTable.selectRow(this.hash); this._this.selectRow(this.rowId);
return true;
});
tr.addEvent('dblclick', function (e) {
e.stop();
myTable.selectRow(this.hash);
var row = myTable.rows.get(this.hash);
var state = row['full_data'].state;
if (~state.indexOf('paused'))
startFN();
else
pauseFN();
return true; return true;
}); });
tr.addEvent('click', function (e) { tr.addEvent('click', function (e) {
e.stop(); e.stop();
if (e.control) { if (e.control) {
// CTRL key was pressed // CTRL key was pressed
if (myTable.cur.contains(this.hash)) { if (this._this.cur.contains(this.rowId)) {
// remove it // remove it
myTable.cur.erase(this.hash); this._this.cur.erase(this.rowId);
// Remove selected style // Remove selected style
this.removeClass('selected'); this.removeClass('selected');
} }
else { else {
myTable.cur.push(this.hash); this._this.cur.push(this.rowId);
// Add selected style // Add selected style
this.addClass('selected'); this.addClass('selected');
} }
} }
else { else {
if (e.shift && myTable.cur.length == 1) { if (e.shift && this._this.cur.length == 1) {
// Shift key was pressed // Shift key was pressed
var first_row_hash = myTable.cur[0]; var first_row_id = this._this.cur[0];
var last_row_hash = this.hash; var last_row_id = this.rowId;
myTable.cur.empty(); this._this.cur.empty();
var trs = myTable.table.getElements('tr'); var trs = this._this.table.getElements('tr');
var select = false; var select = false;
for (var i = 0; i < trs.length; i++) { for (var i = 0; i < trs.length; i++) {
var tr = trs[i]; var tr = trs[i];
if ((tr.hash == first_row_hash) || (tr.hash == last_row_hash)) { if ((tr.rowId == first_row_id) || (tr.rowId == last_row_id)) {
myTable.cur.push(tr.hash); this._this.cur.push(tr.rowId);
tr.addClass('selected'); tr.addClass('selected');
select = !select; select = !select;
} }
else { else {
if (select) { if (select) {
myTable.cur.push(tr.hash); this._this.cur.push(tr.rowId);
tr.addClass('selected'); tr.addClass('selected');
} }
else else
@ -418,13 +345,15 @@ var dynamicTable = new Class({
} }
} else { } else {
// Simple selection // Simple selection
myTable.selectRow(this.hash); this._this.selectRow(this.rowId);
updatePropertiesPanel(); updatePropertiesPanel();
} }
} }
return false; return false;
}); });
this.setupTrEvents(tr);
for (var j = 0 ; j < this.columns.length; j++) { for (var j = 0 ; j < this.columns.length; j++) {
var td = new Element('td'); var td = new Element('td');
if ((this.columns[j].visible == '0') || this.columns[j].force_hide) if ((this.columns[j].visible == '0') || this.columns[j].force_hide)
@ -443,7 +372,8 @@ var dynamicTable = new Class({
} }
// Update context menu // Update context menu
this.context_menu.addTarget(tr); if (this.context_menu)
this.context_menu.addTarget(tr);
this.updateRow(tr, true); this.updateRow(tr, true);
} }
@ -457,8 +387,10 @@ var dynamicTable = new Class({
} }
}, },
setupTrEvents : function (tr) {},
updateRow : function (tr, fullUpdate) { updateRow : function (tr, fullUpdate) {
var row = this.rows.get(tr.hash); var row = this.rows.get(tr.rowId);
data = row[fullUpdate ? 'full_data' : 'data']; data = row[fullUpdate ? 'full_data' : 'data'];
tds = tr.getElements('td'); tds = tr.getElements('td');
@ -467,43 +399,64 @@ var dynamicTable = new Class({
this.columns[i].updateTd(tds[i], row); this.columns[i].updateTd(tds[i], row);
} }
row['data'] = {}; row['data'] = {};
/*
for(var prop in data)
for (var i = 0; i < this.columns.length; i++)
for (var j = 0; j < this.columns[i].dataProperties.length; j++)
if (this.columns[i].dataProperties[j] == prop)
this.columns[i].updateTd(tds[i], row);
if (this.cur.contains(tr.hash)) {
if (!tr.hasClass('selected'))
tr.addClass('selected');
}
else {
if (tr.hasClass('selected'))
tr.removeClass('selected');
}
*/
}, },
removeRow : function (hash) { removeRow : function (rowId) {
this.cur.erase(hash); this.cur.erase(rowId);
var tr = this.getTrByHash(hash); var tr = this.getTrByRowId(rowId);
if (tr != null) { if (tr != null) {
tr.dispose(); tr.dispose();
this.rows.erase(hash); this.rows.erase(rowId);
return true; return true;
} }
return false; return false;
}, },
selectedIds : function () { clear : function () {
this.cur.empty();
this.rows.empty();
var trs = this.table.getElements('tr');
while (trs.length > 0) {
trs[trs.length - 1].dispose();
trs.pop();
}
},
selectedRowsIds : function () {
return this.cur; return this.cur;
}, },
getRowIds : function () { getRowIds : function () {
return this.rows.getKeys(); return this.rows.getKeys();
}, },
});
var TorrentsTable = new Class({
Extends: DynamicTable,
initColumns : function () {
this.newColumn('priority', 'width: 30px; cursor: pointer', '#');
this.newColumn('state_icon', 'width: 16px', '');
this.newColumn('name', 'min-width: 200px; cursor: pointer', 'QBT_TR(Name)QBT_TR');
this.newColumn('size', 'width: 100px; cursor: pointer', 'QBT_TR(Size)QBT_TR');
this.newColumn('progress', 'width: 80px; cursor: pointer', 'QBT_TR(Done)QBT_TR');
this.newColumn('num_seeds', 'width: 100px; cursor: pointer', 'QBT_TR(Seeds)QBT_TR');
this.newColumn('num_leechs', 'width: 100px; cursor: pointer', 'QBT_TR(Peers)QBT_TR');
this.newColumn('dlspeed', 'width: 100px; cursor: pointer', 'QBT_TR(Down Speed)QBT_TR');
this.newColumn('upspeed', 'width: 100px; cursor: pointer', 'QBT_TR(Up Speed)QBT_TR');
this.newColumn('eta', 'width: 100px; cursor: pointer', 'QBT_TR(ETA)QBT_TR');
this.newColumn('ratio', 'width: 100px; cursor: pointer', 'QBT_TR(Ratio)QBT_TR');
this.newColumn('label', 'width: 100px; cursor: pointer', 'QBT_TR(Label)QBT_TR');
this.columns['state_icon'].onclick = '';
this.columns['state_icon'].dataProperties[0] = 'state';
this.columns['num_seeds'].dataProperties.push('num_complete');
this.columns['num_leechs'].dataProperties.push('num_incomplete');
this.initColumnsFunctions();
},
initColumnsFunctions : function () { initColumnsFunctions : function () {
@ -659,10 +612,205 @@ var dynamicTable = new Class({
html = (Math.floor(100 * ratio) / 100).toFixed(2); //Don't round up html = (Math.floor(100 * ratio) / 100).toFixed(2); //Don't round up
td.set('html', html); td.set('html', html);
}; };
} },
applyFilter : function (row, filterName, labelName) {
var state = row['full_data'].state;
switch(filterName) {
case 'downloading':
if (state != 'downloading' && !~state.indexOf('DL'))
return false;
break;
case 'seeding':
if (state != 'uploading' && state != 'forcedUP' && state != 'stalledUP' && state != 'queuedUP' && state != 'checkingUP')
return false;
break;
case 'completed':
if (state != 'uploading' && !~state.indexOf('UP'))
return false;
break;
case 'paused':
if (state != 'pausedDL')
return false;
break;
case 'resumed':
if (~state.indexOf('paused'))
return false;
break;
case 'active':
if (state != 'downloading' && state != 'forcedDL' && state != 'uploading' && state != 'forcedUP')
return false;
break;
case 'inactive':
if (state == 'downloading' || state == 'forcedDL' || state == 'uploading' || state == 'forcedUP')
return false;
break;
}
if (labelName == LABELS_ALL)
return true;
if (labelName == LABELS_UNLABELLED && row['full_data'].label.length === 0)
return true;
if (labelName != genHash( row['full_data'].label) )
return false;
return true;
},
getFilteredAndSortedRows : function () {
var filteredRows = new Array();
var rows = this.rows.getValues();
for (i = 0; i < rows.length; i++)
if (this.applyFilter(rows[i], selected_filter, selected_label)) {
filteredRows.push(rows[i]);
filteredRows[rows[i].rowId] = rows[i];
}
filteredRows.sort(function (row1, row2) {
var column = this.columns[this.sortedColumn];
res = column.compareRows(row1, row2);
if (this.reverseSort == '0')
return res;
else
return -res;
}.bind(this));
return filteredRows;
},
setupTrEvents : function (tr) {
tr.addEvent('dblclick', function (e) {
e.stop();
this._this.selectRow(this.rowId);
var row = this._this.rows.get(this.rowId);
var state = row['full_data'].state;
if (~state.indexOf('paused'))
startFN();
else
pauseFN();
return true;
});
},
getCurrentTorrentHash : function () {
return this.getSelectedRowId();
}
});
var TorrentPeersTable = new Class({
Extends: DynamicTable,
initColumns : function () {
this.newColumn('country', 'width: 4px', '');
this.newColumn('ip', 'width: 80px', 'QBT_TR(IP)QBT_TR');
this.newColumn('port', 'width: 35px', 'QBT_TR(Port)QBT_TR');
this.newColumn('client', 'width: 110px', 'QBT_TR(Client)QBT_TR');
this.newColumn('progress', 'width: 30px', 'QBT_TR(Progress)QBT_TR');
this.newColumn('dl_speed', 'width: 30px', 'QBT_TR(Down Speed)QBT_TR');
this.newColumn('up_speed', 'width: 30px', 'QBT_TR(Up Speed)QBT_TR');
this.newColumn('downloaded', 'width: 30px', 'QBT_TR(Downloaded)QBT_TR');
this.newColumn('uploaded', 'width: 30px', 'QBT_TR(Uploaded)QBT_TR');
this.newColumn('connection', 'width: 30px', 'QBT_TR(Connection)QBT_TR');
this.newColumn('flags', 'width: 30px', 'QBT_TR(Flags)QBT_TR');
this.newColumn('relevance', 'min-width: 30px', 'QBT_TR(Relevance)QBT_TR');
this.columns['country'].dataProperties.push('country_code');
this.columns['flags'].dataProperties.push('flags_desc');
this.initColumnsFunctions();
},
initColumnsFunctions : function () {
// country
this.columns['country'].updateTd = function (td, row) {
var country = this.getRowValue(row, 0);
var country_code = this.getRowValue(row, 1);
if (!country_code) {
if (td.getChildren('img').length)
td.getChildren('img')[0].dispose();
return;
}
var img_path = 'images/flags/' + country_code + '.png';
if (td.getChildren('img').length) {
var img = td.getChildren('img')[0];
img.set('src', img_path);
img.set('alt', country);
img.set('title', country);
}
else
td.adopt(new Element('img', {
'src' : img_path,
'alt' : country,
'title' : country
}));
};
// ip
this.columns['ip'].compareRows = function (row1, row2) {
var ip1 = this.getRowValue(row1);
var ip2 = this.getRowValue(row2);
var a = ip1.split(".");
var b = ip2.split(".");
for (var i = 0; i < 4; i++){
if (a[i] != b[i])
return a[i] - b[i];
}
return 0;
};
// progress, relevance
this.columns['progress'].updateTd = function (td, row) {
var progress = this.getRowValue(row);
var progressFormated = (progress * 100).round(1);
if (progressFormated == 100.0 && progress != 1.0)
progressFormated = 99.9;
progressFormated += "%";
td.set('html', progressFormated);
};
this.columns['relevance'].updateTd = this.columns['progress'].updateTd;
// dl_speed, up_speed
this.columns['dl_speed'].updateTd = function (td, row) {
var speed = this.getRowValue(row);
if (speed == 0)
td.set('html', '');
else
td.set('html', friendlyUnit(speed, true));
};
this.columns['up_speed'].updateTd = this.columns['dl_speed'].updateTd;
// downloaded, uploaded
this.columns['downloaded'].updateTd = function (td, row) {
var downloaded = this.getRowValue(row);
td.set('html', friendlyUnit(downloaded, false));
};
this.columns['uploaded'].updateTd = this.columns['downloaded'].updateTd;
// flags
this.columns['flags'].updateTd = function (td, row) {
td.innerHTML = this.getRowValue(row, 0);
td.title = this.getRowValue(row, 1);
};
}
}); });
//dynamicTable.implement(new Options);
//dynamicTable.implement(new Events);
/*************************************************************/ /*************************************************************/

View File

@ -119,7 +119,7 @@ initializeWindows = function() {
} }
uploadLimitFN = function() { uploadLimitFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
var hash = h[0]; var hash = h[0];
new MochaUI.Window({ new MochaUI.Window({
@ -139,7 +139,7 @@ initializeWindows = function() {
}; };
toggleSequentialDownloadFN = function() { toggleSequentialDownloadFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new Request({ new Request({
url: 'command/toggleSequentialDownload', url: 'command/toggleSequentialDownload',
@ -153,7 +153,7 @@ initializeWindows = function() {
}; };
toggleFirstLastPiecePrioFN = function() { toggleFirstLastPiecePrioFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new Request({ new Request({
url: 'command/toggleFirstLastPiecePrio', url: 'command/toggleFirstLastPiecePrio',
@ -167,7 +167,7 @@ initializeWindows = function() {
}; };
setSuperSeedingFN = function(val) { setSuperSeedingFN = function(val) {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new Request({ new Request({
url: 'command/setSuperSeeding', url: 'command/setSuperSeeding',
@ -182,7 +182,7 @@ initializeWindows = function() {
}; };
setForceStartFN = function() { setForceStartFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new Request({ new Request({
url: 'command/setForceStart', url: 'command/setForceStart',
@ -213,7 +213,7 @@ initializeWindows = function() {
} }
downloadLimitFN = function() { downloadLimitFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
var hash = h[0]; var hash = h[0];
new MochaUI.Window({ new MochaUI.Window({
@ -233,7 +233,7 @@ initializeWindows = function() {
}; };
deleteFN = function() { deleteFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new MochaUI.Window({ new MochaUI.Window({
id: 'confirmDeletionPage', id: 'confirmDeletionPage',
@ -257,7 +257,7 @@ initializeWindows = function() {
}); });
pauseFN = function() { pauseFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
h.each(function(hash, index) { h.each(function(hash, index) {
new Request({ new Request({
@ -273,7 +273,7 @@ initializeWindows = function() {
}; };
startFN = function() { startFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
h.each(function(hash, index) { h.each(function(hash, index) {
new Request({ new Request({
@ -289,7 +289,7 @@ initializeWindows = function() {
}; };
recheckFN = function() { recheckFN = function() {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
h.each(function(hash, index) { h.each(function(hash, index) {
new Request({ new Request({
@ -305,7 +305,7 @@ initializeWindows = function() {
}; };
newLabelFN = function () { newLabelFN = function () {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new MochaUI.Window({ new MochaUI.Window({
id: 'newLabelPage', id: 'newLabelPage',
@ -327,7 +327,7 @@ initializeWindows = function() {
var labelName = ''; var labelName = '';
if (labelHash != 0) if (labelHash != 0)
var labelName = label_list[labelHash].name; var labelName = label_list[labelHash].name;
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new Request({ new Request({
url: 'command/setLabel', url: 'command/setLabel',
@ -353,7 +353,7 @@ initializeWindows = function() {
['pause', 'resume', 'recheck'].each(function(item) { ['pause', 'resume', 'recheck'].each(function(item) {
addClickEvent(item, function(e) { addClickEvent(item, function(e) {
new Event(e).stop(); new Event(e).stop();
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
h.each(function(hash, index) { h.each(function(hash, index) {
new Request({ new Request({
@ -377,7 +377,7 @@ initializeWindows = function() {
}); });
setPriorityFN = function(cmd) { setPriorityFN = function(cmd) {
var h = myTable.selectedIds(); var h = torrentsTable.selectedRowsIds();
if (h.length) { if (h.length) {
new Request({ new Request({
url: 'command/' + cmd, url: 'command/' + cmd,

View File

@ -278,7 +278,7 @@ var loadTorrentFilesData = function() {
// Tab changed, don't do anything // Tab changed, don't do anything
return; return;
} }
var new_hash = myTable.getCurrentTorrentHash(); var new_hash = torrentsTable.getCurrentTorrentHash();
if (new_hash == "") { if (new_hash == "") {
fTable.removeAllRows(); fTable.removeAllRows();
clearTimeout(loadTorrentFilesDataTimer); clearTimeout(loadTorrentFilesDataTimer);

View File

@ -32,7 +32,7 @@ var loadTorrentData = function() {
// Tab changed, don't do anything // Tab changed, don't do anything
return; return;
} }
var current_hash = myTable.getCurrentTorrentHash(); var current_hash = torrentsTable.getCurrentTorrentHash();
if (current_hash == "") { if (current_hash == "") {
clearData(); clearData();
clearTimeout(loadTorrentDataTimer); clearTimeout(loadTorrentDataTimer);

View File

@ -59,7 +59,7 @@ var loadTrackersData = function() {
// Tab changed, don't do anything // Tab changed, don't do anything
return; return;
} }
var new_hash = myTable.getCurrentTorrentHash(); var new_hash = torrentsTable.getCurrentTorrentHash();
if (new_hash == "") { if (new_hash == "") {
tTable.removeAllRows(); tTable.removeAllRows();
clearTimeout(loadTrackersDataTimer); clearTimeout(loadTrackersDataTimer);

View File

@ -59,7 +59,7 @@ var loadWebSeedsData = function() {
// Tab changed, don't do anything // Tab changed, don't do anything
return; return;
} }
var new_hash = myTable.getCurrentTorrentHash(); var new_hash = torrentsTable.getCurrentTorrentHash();
if (new_hash == "") { if (new_hash == "") {
wsTable.removeAllRows(); wsTable.removeAllRows();
clearTimeout(loadWebSeedsDataTimer); clearTimeout(loadWebSeedsDataTimer);

View File

@ -1,15 +1,15 @@
<table class="torrentTable" cellpadding="0" cellspacing="0" style="-webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;"> <table class="torrentTable" cellpadding="0" cellspacing="0" style="-webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;">
<thead> <thead>
<tr id="torrentTableHeader"> <tr id="torrentsTableHeader">
</tr> </tr>
</thead> </thead>
<tbody id="myTable"></tbody> <tbody id="torrentsTable"></tbody>
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
//create a context menu //create a context menu
var context_menu = new ContextMenu({ var torrents_table_context_menu = new ContextMenu({
targets : '.menu-target', targets : '.menu-target',
menu : 'contextmenu', menu : 'contextmenu',
actions : { actions : {
@ -62,5 +62,5 @@
} }
}); });
myTable.setup('myTable', context_menu); torrentsTable.setup('torrentsTable', 'torrentsTableHeader', torrents_table_context_menu);
</script> </script>