Merge pull request #2892 from glassez/bittorrent

Core classes redesign (Issue #2433).
This commit is contained in:
sledgehammer999
2015-06-11 23:54:05 +03:00
207 changed files with 15273 additions and 13581 deletions

View File

@@ -28,28 +28,25 @@
* Contact : chris@qbittorrent.org
*/
#include <cmath>
#include "downloadedpiecesbar.h"
//#include <QDebug>
DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent): QWidget(parent)
{
setFixedHeight(BAR_HEIGHT);
bg_color = 0xffffff;
border_color = palette().color(QPalette::Dark).rgb();
piece_color = 0x0000ff;
piece_color_dl = 0x00d000;
m_bgColor = 0xffffff;
m_borderColor = palette().color(QPalette::Dark).rgb();
m_pieceColor = 0x0000ff;
m_dlPieceColor = 0x00d000;
updatePieceColors();
}
std::vector<float> DownloadedPiecesBar::bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize)
QVector<float> DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin, int reqSize)
{
std::vector<float> result(reqSize, 0.0);
if (vecin.empty())
return result;
QVector<float> result(reqSize, 0.0);
if (vecin.isEmpty()) return result;
const float ratio = vecin.size() / (float)reqSize;
@@ -154,18 +151,18 @@ void DownloadedPiecesBar::updateImage()
// qDebug() << "updateImage";
QImage image2(width() - 2, 1, QImage::Format_RGB888);
if (pieces.empty()) {
if (m_pieces.isEmpty()) {
image2.fill(0xffffff);
image = image2;
m_image = image2;
update();
return;
}
std::vector<float> scaled_pieces = bitfieldToFloatVector(pieces, image2.width());
std::vector<float> scaled_pieces_dl = bitfieldToFloatVector(pieces_dl, image2.width());
QVector<float> scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width());
QVector<float> scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width());
// filling image
for (unsigned int x = 0; x < scaled_pieces.size(); ++x)
for (int x = 0; x < scaled_pieces.size(); ++x)
{
float pieces2_val = scaled_pieces.at(x);
float pieces2_val_dl = scaled_pieces_dl.at(x);
@@ -174,23 +171,23 @@ void DownloadedPiecesBar::updateImage()
float fill_ratio = pieces2_val + pieces2_val_dl;
float ratio = pieces2_val_dl / fill_ratio;
int mixedColor = mixTwoColors(piece_color, piece_color_dl, ratio);
mixedColor = mixTwoColors(bg_color, mixedColor, fill_ratio);
int mixedColor = mixTwoColors(m_pieceColor, m_dlPieceColor, ratio);
mixedColor = mixTwoColors(m_bgColor, mixedColor, fill_ratio);
image2.setPixel(x, 0, mixedColor);
}
else
{
image2.setPixel(x, 0, piece_colors[pieces2_val * 255]);
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
}
}
image = image2;
m_image = image2;
}
void DownloadedPiecesBar::setProgress(const libtorrent::bitfield &bf, const libtorrent::bitfield &bf_dl)
void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces)
{
pieces = libtorrent::bitfield(bf);
pieces_dl = libtorrent::bitfield(bf_dl);
m_pieces = pieces;
m_downloadedPieces = downloadedPieces;
updateImage();
update();
@@ -198,16 +195,16 @@ void DownloadedPiecesBar::setProgress(const libtorrent::bitfield &bf, const libt
void DownloadedPiecesBar::updatePieceColors()
{
piece_colors = std::vector<int>(256);
m_pieceColors = QVector<int>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
piece_colors[i] = mixTwoColors(bg_color, piece_color, ratio);
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
}
}
void DownloadedPiecesBar::clear()
{
image = QImage();
m_image = QImage();
update();
}
@@ -215,30 +212,30 @@ void DownloadedPiecesBar::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QRect imageRect(1, 1, width() - 2, height() - 2);
if (image.isNull())
if (m_image.isNull())
{
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else
{
if (image.width() != imageRect.width())
if (m_image.width() != imageRect.width())
updateImage();
painter.drawImage(imageRect, image);
painter.drawImage(imageRect, m_image);
}
QPainterPath border;
border.addRect(0, 0, width() - 1, height() - 1);
painter.setPen(border_color);
painter.setPen(m_borderColor);
painter.drawPath(border);
}
void DownloadedPiecesBar::setColors(int background, int border, int complete, int incomplete)
{
bg_color = background;
border_color = border;
piece_color = complete;
piece_color_dl = incomplete;
m_bgColor = background;
m_borderColor = border;
m_pieceColor = complete;
m_dlPieceColor = incomplete;
updatePieceColors();
updateImage();

View File

@@ -34,8 +34,8 @@
#include <QWidget>
#include <QPainter>
#include <QImage>
#include <cmath>
#include <libtorrent/bitfield.hpp>
#include <QBitArray>
#include <QVector>
#define BAR_HEIGHT 18
@@ -44,28 +44,28 @@ class DownloadedPiecesBar: public QWidget {
Q_DISABLE_COPY(DownloadedPiecesBar)
private:
QImage image;
QImage m_image;
// I used values, bacause it should be possible to change colors in runtime
// background color
int bg_color;
int m_bgColor;
// border color
int border_color;
int m_borderColor;
// complete piece color
int piece_color;
int m_pieceColor;
// incomplete piece color
int piece_color_dl;
int m_dlPieceColor;
// buffered 256 levels gradient from bg_color to piece_color
std::vector<int> piece_colors;
QVector<int> m_pieceColors;
// last used bitfields, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
libtorrent::bitfield pieces;
libtorrent::bitfield pieces_dl;
QBitArray m_pieces;
QBitArray m_downloadedPieces;
// scale bitfield vector to float vector
std::vector<float> bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize);
QVector<float> bitfieldToFloatVector(const QBitArray &vecin, int reqSize);
// mix two colors by light model, ratio <0, 1>
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
// draw new image and replace actual image
@@ -74,7 +74,7 @@ private:
public:
DownloadedPiecesBar(QWidget *parent);
void setProgress(const libtorrent::bitfield &bf, const libtorrent::bitfield &bf_dl);
void setProgress(const QBitArray &m_pieces, const QBitArray &downloadedPieces);
void updatePieceColors();
void clear();

View File

@@ -0,0 +1,81 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include <QRegExp>
#include <QMessageBox>
#include "core/bittorrent/peerinfo.h"
#include "peeraddition.h"
PeerAdditionDlg::PeerAdditionDlg(QWidget *parent)
: QDialog(parent)
{
setupUi(this);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput()));
}
QHostAddress PeerAdditionDlg::getAddress() const
{
return QHostAddress(lineIP->text());
}
ushort PeerAdditionDlg::getPort() const
{
return spinPort->value();
}
BitTorrent::PeerAddress PeerAdditionDlg::askForPeerAddress()
{
BitTorrent::PeerAddress addr;
PeerAdditionDlg dlg;
if (dlg.exec() == QDialog::Accepted) {
addr.ip = dlg.getAddress();
if (addr.ip.isNull())
qDebug("Unable to parse the provided IP.");
else
qDebug("Provided IP is correct");
addr.port = dlg.getPort();
}
return addr;
}
void PeerAdditionDlg::validateInput()
{
if (getAddress().isNull())
QMessageBox::warning(this, tr("Invalid IP"), tr("The IP you provided is invalid."), QMessageBox::Ok);
else
accept();
}

View File

@@ -1,5 +1,5 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -32,74 +32,29 @@
#define PEERADDITION_H
#include <QDialog>
#include <QRegExp>
#include <QMessageBox>
#include <QHostAddress>
#include "ui_peer.h"
#include <libtorrent/socket.hpp>
#include <libtorrent/address.hpp>
class QHostAddress;
#include <boost/version.hpp>
#if BOOST_VERSION < 103500
#include <libtorrent/asio/ip/tcp.hpp>
#else
#include <boost/asio/ip/tcp.hpp>
#endif
namespace BitTorrent
{
struct PeerAddress;
}
class PeerAdditionDlg: public QDialog, private Ui::addPeerDialog {
Q_OBJECT
class PeerAdditionDlg : public QDialog, private Ui::addPeerDialog
{
Q_OBJECT
public:
PeerAdditionDlg(QWidget *parent=0): QDialog(parent) {
setupUi(this);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput()));
}
PeerAdditionDlg(QWidget *parent = 0);
~PeerAdditionDlg() {}
QString getIP() const {
QHostAddress ip(lineIP->text());
if (!ip.isNull()) {
// QHostAddress::toString() cleans up the IP for libtorrent
return ip.toString();
}
return QString();
}
unsigned short getPort() const {
return spinPort->value();
}
static libtorrent::asio::ip::tcp::endpoint askForPeerEndpoint() {
libtorrent::asio::ip::tcp::endpoint ep;
PeerAdditionDlg dlg;
if (dlg.exec() == QDialog::Accepted) {
QString ip = dlg.getIP();
boost::system::error_code ec;
libtorrent::address addr = libtorrent::address::from_string(qPrintable(ip), ec);
if (ec) {
qDebug("Unable to parse the provided IP: %s", qPrintable(ip));
return ep;
}
qDebug("Provided IP is correct");
ep = libtorrent::asio::ip::tcp::endpoint(addr, dlg.getPort());
}
return ep;
}
QHostAddress getAddress() const;
ushort getPort() const;
static BitTorrent::PeerAddress askForPeerAddress();
protected slots:
void validateInput() {
if (getIP().isEmpty()) {
QMessageBox::warning(this, tr("Invalid IP"),
tr("The IP you provided is invalid."),
QMessageBox::Ok);
} else {
accept();
}
}
void validateInput();
};
#endif // PEERADDITION_H

View File

@@ -33,7 +33,8 @@
#include <QItemDelegate>
#include <QPainter>
#include "misc.h"
#include "core/utils/misc.h"
#include "core/utils/string.h"
class PeerListDelegate: public QItemDelegate {
Q_OBJECT
@@ -54,21 +55,21 @@ public:
case TOT_DOWN:
case TOT_UP:
QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong()));
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
break;
case DOWN_SPEED:
case UP_SPEED:{
QItemDelegate::drawBackground(painter, opt, index);
qreal speed = index.data().toDouble();
if (speed > 0.0)
QItemDelegate::drawDisplay(painter, opt, opt.rect, misc::friendlyUnit(speed)+tr("/s", "/second (i.e. per second)"));
QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::Misc::friendlyUnit(speed)+tr("/s", "/second (i.e. per second)"));
break;
}
case PROGRESS:
case RELEVANCE:{
QItemDelegate::drawBackground(painter, opt, index);
qreal progress = index.data().toDouble();
QItemDelegate::drawDisplay(painter, opt, opt.rect, misc::accurateDoubleToString(progress*100.0, 1)+"%");
QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::String::fromDouble(progress*100.0, 1)+"%");
break;
}
default:

View File

@@ -52,7 +52,7 @@ protected:
Q_ASSERT(vR.isValid());
bool res = false;
if (misc::naturalSort(vL.toString(), vR.toString(), res))
if (Utils::String::naturalSort(vL.toString(), vR.toString(), res))
return res;
return QSortFilterProxyModel::lessThan(left, right);

View File

@@ -28,28 +28,27 @@
* Contact : chris@qbittorrent.org
*/
#include "peerlistwidget.h"
#include "peerlistdelegate.h"
#include "peerlistsortmodel.h"
#include "reverseresolution.h"
#include "preferences.h"
#include "propertieswidget.h"
#include "geoipmanager.h"
#include "peeraddition.h"
#include "speedlimitdlg.h"
#include "iconprovider.h"
#include "qtorrenthandle.h"
#include "logger.h"
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QSet>
#include <QHeaderView>
#include <QMenu>
#include <QClipboard>
#include <vector>
#include <libtorrent/peer_info.hpp>
#include <QMessageBox>
using namespace libtorrent;
#include "core/net/reverseresolution.h"
#include "core/bittorrent/torrenthandle.h"
#include "core/bittorrent/peerinfo.h"
#include "core/preferences.h"
#include "core/logger.h"
#include "propertieswidget.h"
#include "geoipmanager.h"
#include "peeraddition.h"
#include "speedlimitdlg.h"
#include "guiiconprovider.h"
#include "peerlistdelegate.h"
#include "peerlistsortmodel.h"
#include "peerlistwidget.h"
PeerListWidget::PeerListWidget(PropertiesWidget *parent):
QTreeView(parent), m_properties(parent), m_displayFlags(false)
@@ -126,8 +125,8 @@ void PeerListWidget::updatePeerHostNameResolutionState()
{
if (Preferences::instance()->resolvePeerHostNames()) {
if (!m_resolver) {
m_resolver = new ReverseResolution(this);
connect(m_resolver, SIGNAL(ip_resolved(QString,QString)), SLOT(handleResolved(QString,QString)));
m_resolver = new Net::ReverseResolution(this);
connect(m_resolver, SIGNAL(ipResolved(QString,QString)), SLOT(handleResolved(QString,QString)));
loadPeers(m_properties->getCurrentTorrent(), true);
}
} else {
@@ -149,8 +148,9 @@ void PeerListWidget::showPeerListMenu(const QPoint&)
{
QMenu menu;
bool empty_menu = true;
QTorrentHandle h = m_properties->getCurrentTorrent();
if (!h.is_valid()) return;
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
if (!torrent) return;
QModelIndexList selectedIndexes = selectionModel()->selectedRows();
QStringList selectedPeerIPs;
QStringList selectedPeerIPPort;
@@ -163,55 +163,34 @@ void PeerListWidget::showPeerListMenu(const QPoint&)
}
// Add Peer Action
QAction *addPeerAct = 0;
if (!h.is_queued() && !h.is_checking()) {
addPeerAct = menu.addAction(IconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer..."));
if (!torrent->isQueued() && !torrent->isChecking()) {
addPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer..."));
empty_menu = false;
}
// Per Peer Speed limiting actions
#if LIBTORRENT_VERSION_NUM < 10000
QAction *upLimitAct = 0;
QAction *dlLimitAct = 0;
#endif
QAction *banAct = 0;
QAction *copyPeerAct = 0;
if (!selectedPeerIPs.isEmpty()) {
copyPeerAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy selected"));
copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy selected"));
menu.addSeparator();
#if LIBTORRENT_VERSION_NUM < 10000
dlLimitAct = menu.addAction(QIcon(":/icons/skin/download.png"), tr("Limit download rate..."));
upLimitAct = menu.addAction(QIcon(":/icons/skin/seeding.png"), tr("Limit upload rate..."));
menu.addSeparator();
#endif
banAct = menu.addAction(IconProvider::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;
}
if (empty_menu) return;
QAction *act = menu.exec(QCursor::pos());
if (act == 0) return;
if (act == addPeerAct) {
boost::asio::ip::tcp::endpoint ep = PeerAdditionDlg::askForPeerEndpoint();
if (ep != boost::asio::ip::tcp::endpoint()) {
try {
h.connect_peer(ep);
BitTorrent::PeerAddress addr = PeerAdditionDlg::askForPeerAddress();
if (!addr.ip.isNull()) {
if (torrent->connectPeer(addr))
QMessageBox::information(0, tr("Peer addition"), tr("The peer was added to this torrent."));
} catch(std::exception) {
else
QMessageBox::critical(0, tr("Peer addition"), tr("The peer could not be added to this torrent."));
}
} else {
}
else {
qDebug("No peer was added");
}
return;
}
#if LIBTORRENT_VERSION_NUM < 10000
if (act == upLimitAct) {
limitUpRateSelectedPeers(selectedPeerIPs);
return;
}
if (act == dlLimitAct) {
limitDlRateSelectedPeers(selectedPeerIPs);
return;
}
#endif
if (act == banAct) {
banSelectedPeers(selectedPeerIPs);
return;
@@ -237,84 +216,16 @@ void PeerListWidget::banSelectedPeers(const QStringList& peer_ips)
foreach (const QString &ip, peer_ips) {
qDebug("Banning peer %s...", ip.toLocal8Bit().data());
Logger::instance()->addMessage(tr("Manually banning peer %1...").arg(ip));
QBtSession::instance()->banIP(ip);
BitTorrent::Session::instance()->banIP(ip);
}
// Refresh list
loadPeers(m_properties->getCurrentTorrent());
}
#if LIBTORRENT_VERSION_NUM < 10000
void PeerListWidget::limitUpRateSelectedPeers(const QStringList& peer_ips)
{
if (peer_ips.empty())
return;
QTorrentHandle h = m_properties->getCurrentTorrent();
if (!h.is_valid())
return;
bool ok = false;
int cur_limit = -1;
boost::asio::ip::tcp::endpoint first_ep = m_peerEndpoints.value(peer_ips.first(),
boost::asio::ip::tcp::endpoint());
if (first_ep != boost::asio::ip::tcp::endpoint())
cur_limit = h.get_peer_upload_limit(first_ep);
long limit = SpeedLimitDialog::askSpeedLimit(&ok,
tr("Upload rate limiting"),
cur_limit,
Preferences::instance()->getGlobalUploadLimit()*1024.);
if (!ok)
return;
foreach (const QString &ip, peer_ips) {
boost::asio::ip::tcp::endpoint ep = m_peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint());
if (ep != boost::asio::ip::tcp::endpoint()) {
qDebug("Settings Upload limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data());
try {
h.set_peer_upload_limit(ep, limit);
} catch(std::exception) {
std::cerr << "Impossible to apply upload limit to peer" << std::endl;
}
} else {
qDebug("The selected peer no longer exists...");
}
}
}
void PeerListWidget::limitDlRateSelectedPeers(const QStringList& peer_ips)
{
QTorrentHandle h = m_properties->getCurrentTorrent();
if (!h.is_valid())
return;
bool ok = false;
int cur_limit = -1;
boost::asio::ip::tcp::endpoint first_ep = m_peerEndpoints.value(peer_ips.first(),
boost::asio::ip::tcp::endpoint());
if (first_ep != boost::asio::ip::tcp::endpoint())
cur_limit = h.get_peer_download_limit(first_ep);
long limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Download rate limiting"), cur_limit, Preferences::instance()->getGlobalDownloadLimit()*1024.);
if (!ok)
return;
foreach (const QString &ip, peer_ips) {
boost::asio::ip::tcp::endpoint ep = m_peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint());
if (ep != boost::asio::ip::tcp::endpoint()) {
qDebug("Settings Download limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data());
try {
h.set_peer_download_limit(ep, limit);
}catch(std::exception) {
std::cerr << "Impossible to apply download limit to peer" << std::endl;
}
} else {
qDebug("The selected peer no longer exists...");
}
}
}
#endif
void PeerListWidget::clear() {
qDebug("clearing peer list");
m_peerItems.clear();
m_peerEndpoints.clear();
m_peerAddresses.clear();
m_missingFlags.clear();
int nbrows = m_listModel->rowCount();
if (nbrows > 0) {
@@ -331,34 +242,28 @@ void PeerListWidget::saveSettings() const {
Preferences::instance()->setPeerListState(header()->saveState());
}
void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_resolution) {
if (!h.is_valid())
return;
boost::system::error_code ec;
libtorrent::torrent_status status = h.status(torrent_handle::query_pieces);
std::vector<peer_info> peers;
h.get_peer_info(peers);
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();
std::vector<peer_info>::const_iterator itr = peers.begin();
std::vector<peer_info>::const_iterator itrend = peers.end();
for ( ; itr != itrend; ++itr) {
peer_info peer = *itr;
std::string ip_str = peer.ip.address().to_string(ec);
if (ec || ip_str.empty())
continue;
QString peer_ip = misc::toQString(ip_str);
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, status, 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, status, peer);
m_peerEndpoints[peer_ip] = peer.ip;
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);
@@ -369,70 +274,70 @@ void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_reso
while(it.hasNext()) {
const QString& ip = it.next();
m_missingFlags.remove(ip);
m_peerEndpoints.remove(ip);
m_peerAddresses.remove(ip);
QStandardItem *item = m_peerItems.take(ip);
m_listModel->removeRow(item->row());
}
}
QStandardItem* PeerListWidget::addPeer(const QString& ip, const libtorrent::torrent_status &status, const peer_info& peer) {
QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer) {
int row = m_listModel->rowCount();
// Adding Peer to peer list
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.ip.port());
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 = GeoIPManager::CountryISOCodeToIcon(peer.country);
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country);
const QString country_name = GeoIPManager::CountryISOCodeToName(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), getConnectionString(peer));
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), misc::toQStringU(peer.client));
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(status, peer));
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, const libtorrent::torrent_status &status, const peer_info& peer) {
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 = GeoIPManager::CountryISOCodeToIcon(peer.country);
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country());
if (!ico.isNull()) {
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole);
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country);
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country());
m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole);
m_missingFlags.remove(ip);
}
}
m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), getConnectionString(peer));
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.ip.port());
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), misc::toQStringU(peer.client));
m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload);
m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(status, peer));
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) {
@@ -453,34 +358,11 @@ void PeerListWidget::handleSortColumnChanged(int col)
}
}
QString PeerListWidget::getConnectionString(const peer_info& peer)
{
#if LIBTORRENT_VERSION_NUM < 10000
if (peer.connection_type & peer_info::bittorrent_utp) {
#else
if (peer.flags & peer_info::utp_socket) {
#endif
return QString::fromUtf8("μTP");
}
QString connection;
switch(peer.connection_type) {
case peer_info::http_seed:
case peer_info::web_seed:
connection = "Web";
break;
default:
connection = "BT";
break;
}
return connection;
}
void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& tooltip)
void PeerListWidget::getFlags(const BitTorrent::PeerInfo &peer, QString& flags, QString& tooltip)
{
if (peer.flags & peer_info::interesting) {
if (peer.isInteresting()) {
//d = Your client wants to download, but peer doesn't want to send (interested and choked)
if (peer.flags & peer_info::remote_choked) {
if (peer.isRemoteChocked()) {
flags += "d ";
tooltip += tr("interested(local) and choked(peer)");
tooltip += ", ";
@@ -493,9 +375,9 @@ void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& to
}
}
if (peer.flags & peer_info::remote_interested) {
if (peer.isRemoteInterested()) {
//u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
if (peer.flags & peer_info::choked) {
if (peer.isChocked()) {
flags += "u ";
tooltip += tr("interested(peer) and choked(local)");
tooltip += ", ";
@@ -509,81 +391,78 @@ void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& to
}
//O = Optimistic unchoke
if (peer.flags & peer_info::optimistic_unchoke) {
if (peer.optimisticUnchoke()) {
flags += "O ";
tooltip += tr("optimistic unchoke");
tooltip += ", ";
}
//S = Peer is snubbed
if (peer.flags & peer_info::snubbed) {
if (peer.isSnubbed()) {
flags += "S ";
tooltip += tr("peer snubbed");
tooltip += ", ";
}
//I = Peer is an incoming connection
if ((peer.flags & peer_info::local_connection) == 0 ) {
if (!peer.isLocalConnection()) {
flags += "I ";
tooltip += tr("incoming connection");
tooltip += ", ";
}
//K = Peer is unchoking your client, but your client is not interested
if (((peer.flags & peer_info::remote_choked) == 0) && ((peer.flags & peer_info::interesting) == 0)) {
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.flags & peer_info::choked) == 0) && ((peer.flags & peer_info::remote_interested) == 0)) {
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.source & peer_info::pex) {
if (peer.fromPeX()) {
flags += "X ";
tooltip += tr("peer from PEX");
tooltip += ", ";
}
//H = Peer was obtained through DHT
if (peer.source & peer_info::dht) {
if (peer.fromDHT()) {
flags += "H ";
tooltip += tr("peer from DHT");
tooltip += ", ";
}
//E = Peer is using Protocol Encryption (all traffic)
if (peer.flags & peer_info::rc4_encrypted) {
if (peer.isRC4Encrypted()) {
flags += "E ";
tooltip += tr("encrypted traffic");
tooltip += ", ";
}
//e = Peer is using Protocol Encryption (handshake)
if (peer.flags & peer_info::plaintext_encrypted) {
if (peer.isPlaintextEncrypted()) {
flags += "e ";
tooltip += tr("encrypted handshake");
tooltip += ", ";
}
//P = Peer is using uTorrent uTP
#if LIBTORRENT_VERSION_NUM < 10000
if (peer.connection_type & peer_info::bittorrent_utp) {
#else
if (peer.flags & peer_info::utp_socket) {
#endif
if (peer.useUTPSocket()) {
flags += "P ";
tooltip += QString::fromUtf8("μTP");
tooltip += ", ";
}
//L = Peer is local
if (peer.source & peer_info::lsd) {
if (peer.fromLSD()) {
flags += "L";
tooltip += tr("peer from LSD");
}
@@ -594,17 +473,15 @@ void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& to
tooltip.chop(1);
}
double PeerListWidget::getPeerRelevance(const torrent_status& status, const libtorrent::peer_info &peer)
qreal PeerListWidget::getPeerRelevance(const QBitArray &allPieces, const QBitArray &peerPieces)
{
int localMissing = 0;
int remoteHaves = 0;
libtorrent::bitfield local = status.pieces;
libtorrent::bitfield remote = peer.pieces;
for (int i=0; i<local.size(); ++i) {
if (!local[i]) {
for (int i = 0; i < allPieces.size(); ++i) {
if (!allPieces[i]) {
++localMissing;
if (remote[i])
if (peerPieces[i])
++remoteHaves;
}
}
@@ -612,5 +489,5 @@ double PeerListWidget::getPeerRelevance(const torrent_status& status, const libt
if (localMissing == 0)
return 0.0;
return (double)remoteHaves/localMissing;
return static_cast<qreal>(remoteHaves) / localMissing;
}

View File

@@ -35,13 +35,15 @@
#include <QHash>
#include <QPointer>
#include <QSet>
#include <libtorrent/version.hpp>
namespace Net
{
class ReverseResolution;
}
class PeerListDelegate;
class PeerListSortModel;
class ReverseResolution;
class PropertiesWidget;
class QTorrentHandle;
QT_BEGIN_NAMESPACE
class QSortFilterProxyModel;
@@ -49,18 +51,14 @@ class QStandardItem;
class QStandardItemModel;
QT_END_NAMESPACE
namespace libtorrent
namespace BitTorrent
{
struct peer_info;
struct torrent_status;
}
#include <boost/version.hpp>
#if BOOST_VERSION < 103500
#include <libtorrent/asio/ip/tcp.hpp>
#else
#include <boost/asio/ip/tcp.hpp>
#endif
class TorrentHandle;
class PeerInfo;
struct PeerAddress;
}
class PeerListWidget : public QTreeView {
Q_OBJECT
@@ -70,9 +68,9 @@ public:
~PeerListWidget();
public slots:
void loadPeers(const QTorrentHandle &h, bool force_hostname_resolution = false);
QStandardItem* addPeer(const QString& ip, const libtorrent::torrent_status &status, const libtorrent::peer_info& peer);
void updatePeer(const QString& ip, const libtorrent::torrent_status &status, const libtorrent::peer_info& peer);
void loadPeers(BitTorrent::TorrentHandle *const torrent, bool force_hostname_resolution = false);
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 handleResolved(const QString &ip, const QString &hostname);
void updatePeerHostNameResolutionState();
void updatePeerCountryResolutionState();
@@ -82,28 +80,21 @@ protected slots:
void loadSettings();
void saveSettings() const;
void showPeerListMenu(const QPoint&);
#if LIBTORRENT_VERSION_NUM < 10000
void limitUpRateSelectedPeers(const QStringList& peer_ips);
void limitDlRateSelectedPeers(const QStringList& peer_ips);
#endif
void banSelectedPeers(const QStringList& peer_ips);
void handleSortColumnChanged(int col);
private:
static QString getConnectionString(const libtorrent::peer_info &peer);
static void getFlags(const libtorrent::peer_info& peer, QString& flags, QString& tooltip);
double getPeerRelevance(const libtorrent::torrent_status &status, const libtorrent::peer_info &peer);
static void getFlags(const BitTorrent::PeerInfo &peer, QString &flags, QString &tooltip);
qreal getPeerRelevance(const QBitArray &allPieces, const QBitArray &peerPieces);
private:
QStandardItemModel *m_listModel;
PeerListDelegate *m_listDelegate;
PeerListSortModel *m_proxyModel;
QHash<QString, QStandardItem*> m_peerItems;
QHash<QString, boost::asio::ip::tcp::endpoint> m_peerEndpoints;
QHash<QString, BitTorrent::PeerAddress> m_peerAddresses;
QSet<QString> m_missingFlags;
QPointer<ReverseResolution> m_resolver;
QPointer<Net::ReverseResolution> m_resolver;
PropertiesWidget *m_properties;
bool m_displayFlags;
};

View File

@@ -28,28 +28,25 @@
* Contact : chris@qbittorrent.org
*/
#include <cmath>
#include "pieceavailabilitybar.h"
//#include <QDebug>
PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent) :
QWidget(parent)
{
setFixedHeight(BAR_HEIGHT);
bg_color = 0xffffff;
border_color = palette().color(QPalette::Dark).rgb();
piece_color = 0x0000ff;
m_bgColor = 0xffffff;
m_borderColor = palette().color(QPalette::Dark).rgb();
m_pieceColor = 0x0000ff;
updatePieceColors();
}
std::vector<float> PieceAvailabilityBar::intToFloatVector(const std::vector<int> &vecin, int reqSize)
QVector<float> PieceAvailabilityBar::intToFloatVector(const QVector<int> &vecin, int reqSize)
{
std::vector<float> result(reqSize, 0.0);
if (vecin.empty())
return result;
QVector<float> result(reqSize, 0.0);
if (vecin.isEmpty()) return result;
const float ratio = vecin.size() / (float)reqSize;
@@ -162,27 +159,27 @@ void PieceAvailabilityBar::updateImage()
// qDebug() << "updateImageAv";
QImage image2(width() - 2, 1, QImage::Format_RGB888);
if (pieces.empty()) {
if (m_pieces.empty()) {
image2.fill(0xffffff);
image = image2;
m_image = image2;
update();
return;
}
std::vector<float> scaled_pieces = intToFloatVector(pieces, image2.width());
QVector<float> scaled_pieces = intToFloatVector(m_pieces, image2.width());
// filling image
for (unsigned int x = 0; x < scaled_pieces.size(); ++x)
for (int x = 0; x < scaled_pieces.size(); ++x)
{
float pieces2_val = scaled_pieces.at(x);
image2.setPixel(x, 0, piece_colors[pieces2_val * 255]);
image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]);
}
image = image2;
m_image = image2;
}
void PieceAvailabilityBar::setAvailability(const std::vector<int>& avail)
void PieceAvailabilityBar::setAvailability(const QVector<int> &avail)
{
pieces = std::vector<int>(avail);
m_pieces = avail;
updateImage();
update();
@@ -190,16 +187,16 @@ void PieceAvailabilityBar::setAvailability(const std::vector<int>& avail)
void PieceAvailabilityBar::updatePieceColors()
{
piece_colors = std::vector<int>(256);
m_pieceColors = QVector<int>(256);
for (int i = 0; i < 256; ++i) {
float ratio = (i / 255.0);
piece_colors[i] = mixTwoColors(bg_color, piece_color, ratio);
m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio);
}
}
void PieceAvailabilityBar::clear()
{
image = QImage();
m_image = QImage();
update();
}
@@ -207,29 +204,29 @@ void PieceAvailabilityBar::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QRect imageRect(1, 1, width() - 2, height() - 2);
if (image.isNull())
if (m_image.isNull())
{
painter.setBrush(Qt::white);
painter.drawRect(imageRect);
}
else
{
if (image.width() != imageRect.width())
if (m_image.width() != imageRect.width())
updateImage();
painter.drawImage(imageRect, image);
painter.drawImage(imageRect, m_image);
}
QPainterPath border;
border.addRect(0, 0, width() - 1, height() - 1);
painter.setPen(border_color);
painter.setPen(m_borderColor);
painter.drawPath(border);
}
void PieceAvailabilityBar::setColors(int background, int border, int available)
{
bg_color = background;
border_color = border;
piece_color = available;
m_bgColor = background;
m_borderColor = border;
m_pieceColor = available;
updatePieceColors();
updateImage();

View File

@@ -34,8 +34,6 @@
#include <QWidget>
#include <QPainter>
#include <QImage>
#include <cmath>
#include <algorithm>
#define BAR_HEIGHT 18
@@ -44,25 +42,25 @@ class PieceAvailabilityBar: public QWidget {
Q_DISABLE_COPY(PieceAvailabilityBar)
private:
QImage image;
QImage m_image;
// I used values, bacause it should be possible to change colors in runtime
// background color
int bg_color;
int m_bgColor;
// border color
int border_color;
int m_borderColor;
// complete piece color
int piece_color;
int m_pieceColor;
// buffered 256 levels gradient from bg_color to piece_color
std::vector<int> piece_colors;
QVector<int> m_pieceColors;
// last used int vector, uses to better resize redraw
// TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster
std::vector<int> pieces;
QVector<int> m_pieces;
// scale int vector to float vector
std::vector<float> intToFloatVector(const std::vector<int> &vecin, int reqSize);
QVector<float> intToFloatVector(const QVector<int> &vecin, int reqSize);
// mix two colors by light model, ratio <0, 1>
int mixTwoColors(int &rgb1, int &rgb2, float ratio);
@@ -72,7 +70,7 @@ private:
public:
PieceAvailabilityBar(QWidget *parent);
void setAvailability(const std::vector<int>& avail);
void setAvailability(const QVector<int> &avail);
void updatePieceColors();
void clear();

View File

@@ -17,8 +17,11 @@ HEADERS += $$PWD/propertieswidget.h \
$$PWD/proptabbar.h
SOURCES += $$PWD/propertieswidget.cpp \
$$PWD/proplistdelegate.cpp \
$$PWD/peerlistwidget.cpp \
$$PWD/trackerlist.cpp \
$$PWD/proptabbar.cpp \
$$PWD/downloadedpiecesbar.cpp \
$$PWD/peeraddition.cpp \
$$PWD/trackersadditiondlg.cpp \
$$PWD/pieceavailabilitybar.cpp

View File

@@ -40,11 +40,13 @@
#include <QMenu>
#include <QFileDialog>
#include <QDesktopServices>
#include <libtorrent/version.hpp>
#include "propertieswidget.h"
#include "transferlistwidget.h"
#include "torrentpersistentdata.h"
#include "qbtsession.h"
#include <QBitArray>
#include "core/bittorrent/session.h"
#include "core/preferences.h"
#include "core/utils/fs.h"
#include "core/utils/misc.h"
#include "core/utils/string.h"
#include "proplistdelegate.h"
#include "torrentcontentfiltermodel.h"
#include "torrentcontentmodel.h"
@@ -53,22 +55,20 @@
#include "mainwindow.h"
#include "downloadedpiecesbar.h"
#include "pieceavailabilitybar.h"
#include "preferences.h"
#include "proptabbar.h"
#include "iconprovider.h"
#include "guiiconprovider.h"
#include "lineedit.h"
#include "fs_utils.h"
#include "transferlistwidget.h"
#include "autoexpandabledialog.h"
using namespace libtorrent;
#include "propertieswidget.h"
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList):
QWidget(parent), transferList(transferList), main_window(main_window) {
QWidget(parent), transferList(transferList), main_window(main_window), m_torrent(0) {
setupUi(this);
// Icons
trackerUpButton->setIcon(IconProvider::instance()->getIcon("go-up"));
trackerDownButton->setIcon(IconProvider::instance()->getIcon("go-down"));
trackerUpButton->setIcon(GuiIconProvider::instance()->getIcon("go-up"));
trackerDownButton->setIcon(GuiIconProvider::instance()->getIcon("go-down"));
state = VISIBLE;
@@ -92,11 +92,11 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, Tra
connect(filesList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(openDoubleClickedFile(const QModelIndex &)));
connect(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(listWebSeeds, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayWebSeedListMenu(const QPoint&)));
connect(transferList, SIGNAL(currentTorrentChanged(QTorrentHandle)), this, SLOT(loadTorrentInfos(QTorrentHandle)));
connect(transferList, SIGNAL(currentTorrentChanged(BitTorrent::TorrentHandle *const)), this, SLOT(loadTorrentInfos(BitTorrent::TorrentHandle *const)));
connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(stackedProperties, SIGNAL(currentChanged(int)), this, SLOT(loadDynamicData()));
connect(QBtSession::instance(), SIGNAL(savePathChanged(QTorrentHandle)), this, SLOT(updateSavePath(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(metadataReceived(QTorrentHandle)), this, SLOT(updateTorrentInfos(QTorrentHandle)));
connect(BitTorrent::Session::instance(), SIGNAL(torrentSavePathChanged(BitTorrent::TorrentHandle *const)), this, SLOT(updateSavePath(BitTorrent::TorrentHandle *const)));
connect(BitTorrent::Session::instance(), SIGNAL(torrentMetadataLoaded(BitTorrent::TorrentHandle *const)), this, SLOT(updateTorrentInfos(BitTorrent::TorrentHandle *const)));
connect(filesList->header(), SIGNAL(sectionMoved(int, int, int)), this, SLOT(saveSettings()));
connect(filesList->header(), SIGNAL(sectionResized(int, int, int)), this, SLOT(saveSettings()));
connect(filesList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(saveSettings()));
@@ -111,9 +111,6 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, Tra
trackerList = new TrackerList(this);
connect(trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp()));
connect(trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown()));
connect(trackerList, SIGNAL(trackersAdded(const QStringList &, const QString &)), this, SIGNAL(trackersAdded(const QStringList &, const QString &)));
connect(trackerList, SIGNAL(trackersRemoved(const QStringList &, const QString &)), this, SIGNAL(trackersRemoved(const QStringList &, const QString &)));
connect(trackerList, SIGNAL(trackerlessChange(bool, const QString &)), this, SIGNAL(trackerlessChange(bool, const QString &)));
horizontalLayout_trackers->insertWidget(0, trackerList);
connect(trackerList->header(), SIGNAL(sectionMoved(int, int, int)), trackerList, SLOT(saveSettings()));
connect(trackerList->header(), SIGNAL(sectionResized(int, int, int)), trackerList, SLOT(saveSettings()));
@@ -233,61 +230,56 @@ void PropertiesWidget::clear() {
showPiecesDownloaded(false);
}
QTorrentHandle PropertiesWidget::getCurrentTorrent() const {
return h;
BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const
{
return m_torrent;
}
void PropertiesWidget::updateSavePath(const QTorrentHandle& _h) {
if (h.is_valid() && h == _h) {
save_path->setText(fsutils::toNativePath(h.save_path_parsed()));
void PropertiesWidget::updateSavePath(BitTorrent::TorrentHandle *const torrent)
{
if (m_torrent == torrent) {
save_path->setText(m_torrent->savePathParsed());
}
}
void PropertiesWidget::loadTrackers(const QTorrentHandle &handle)
void PropertiesWidget::loadTrackers(BitTorrent::TorrentHandle *const torrent)
{
if (handle == h)
if (torrent == m_torrent)
trackerList->loadTrackers();
}
void PropertiesWidget::updateTorrentInfos(const QTorrentHandle& _h) {
if (h.is_valid() && h == _h) {
loadTorrentInfos(h);
}
void PropertiesWidget::updateTorrentInfos(BitTorrent::TorrentHandle *const torrent)
{
if (m_torrent == torrent)
loadTorrentInfos(m_torrent);
}
void PropertiesWidget::loadTorrentInfos(const QTorrentHandle& _h)
void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent)
{
clear();
h = _h;
if (!h.is_valid())
return;
m_torrent = torrent;
if (!m_torrent) return;
try {
// Save path
updateSavePath(h);
updateSavePath(m_torrent);
// Hash
hash_lbl->setText(h.hash());
hash_lbl->setText(m_torrent->hash());
PropListModel->model()->clear();
if (h.has_metadata()) {
if (m_torrent->hasMetadata()) {
// Creation date
lbl_creationDate->setText(h.creation_date());
lbl_creationDate->setText(m_torrent->creationDate().toString());
// Piece size
pieceSize_lbl->setText(misc::friendlyUnit(h.piece_length()));
pieceSize_lbl->setText(Utils::Misc::friendlyUnit(m_torrent->pieceLength()));
// Comment
comment_text->setHtml(misc::parseHtmlLinks(h.comment()));
comment_text->setHtml(Utils::Misc::parseHtmlLinks(m_torrent->comment()));
// URL seeds
loadUrlSeeds();
// List files in torrent
#if LIBTORRENT_VERSION_NUM < 10000
PropListModel->model()->setupModelData(h.get_torrent_info());
#else
PropListModel->model()->setupModelData(*h.torrent_file());
#endif
PropListModel->model()->setupModelData(m_torrent->info());
filesList->setExpanded(PropListModel->index(0, 0), true);
// Load file priorities
PropListModel->model()->updateFilesPriorities(h.file_priorities());
PropListModel->model()->updateFilesPriorities(m_torrent->filePriorities());
}
} catch(const invalid_handle& e) { }
// Load dynamic data
loadDynamicData();
}
@@ -341,56 +333,45 @@ void PropertiesWidget::reloadPreferences() {
void PropertiesWidget::loadDynamicData() {
// Refresh only if the torrent handle is valid and if visible
if (!h.is_valid() || main_window->getCurrentTabWidget() != transferList || state != VISIBLE) return;
try {
if (!m_torrent || (main_window->getCurrentTabWidget() != transferList) || (state != VISIBLE)) return;
// Transfer infos
if (stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) {
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters
| torrent_handle::query_distributed_copies
| torrent_handle::query_pieces);
wasted->setText(misc::friendlyUnit(status.total_failed_bytes+status.total_redundant_bytes));
upTotal->setText(misc::friendlyUnit(status.all_time_upload) + " ("+misc::friendlyUnit(status.total_payload_upload)+" "+tr("this session")+")");
dlTotal->setText(misc::friendlyUnit(status.all_time_download) + " ("+misc::friendlyUnit(status.total_payload_download)+" "+tr("this session")+")");
lbl_uplimit->setText(h.upload_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)"));
lbl_dllimit->setText(h.download_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)"));
QString elapsed_txt = misc::userFriendlyDuration(status.active_time);
if (h.is_seed(status)) {
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(status.seeding_time))+")";
wasted->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize()));
upTotal->setText(Utils::Misc::friendlyUnit(m_torrent->totalUpload()) + " ("+Utils::Misc::friendlyUnit(m_torrent->totalPayloadUpload())+" "+tr("this session")+")");
dlTotal->setText(Utils::Misc::friendlyUnit(m_torrent->totalDownload()) + " ("+Utils::Misc::friendlyUnit(m_torrent->totalPayloadDownload())+" "+tr("this session")+")");
lbl_uplimit->setText(m_torrent->uploadLimit() <= 0 ? QString::fromUtf8("") : Utils::Misc::friendlyUnit(m_torrent->uploadLimit())+tr("/s", "/second (i.e. per second)"));
lbl_dllimit->setText(m_torrent->downloadLimit() <= 0 ? QString::fromUtf8("") : Utils::Misc::friendlyUnit(m_torrent->downloadLimit())+tr("/s", "/second (i.e. per second)"));
QString elapsed_txt = Utils::Misc::userFriendlyDuration(m_torrent->activeTime());
if (m_torrent->isSeed()) {
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(Utils::Misc::userFriendlyDuration(m_torrent->seedingTime()))+")";
}
lbl_elapsed->setText(elapsed_txt);
if (status.connections_limit > 0)
lbl_connections->setText(QString::number(status.num_connections)+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(status.connections_limit))+")");
if (m_torrent->connectionsLimit() > 0)
lbl_connections->setText(QString::number(m_torrent->connectionsCount())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(m_torrent->connectionsLimit()))+")");
else
lbl_connections->setText(QString::number(status.num_connections));
lbl_connections->setText(QString::number(m_torrent->connectionsLimit()));
// Update next announce time
reannounce_lbl->setText(misc::userFriendlyDuration(status.next_announce.total_seconds()));
reannounce_lbl->setText(Utils::Misc::userFriendlyDuration(m_torrent->nextAnnounce()));
// Update ratio info
const qreal ratio = QBtSession::instance()->getRealRatio(status);
shareRatio->setText(ratio > QBtSession::MAX_RATIO ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 2));
if (!h.is_seed(status) && status.has_metadata) {
const qreal ratio = m_torrent->realRatio();
shareRatio->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8("") : Utils::String::fromDouble(ratio, 2));
if (!m_torrent->isSeed() && m_torrent->hasMetadata()) {
showPiecesDownloaded(true);
// Downloaded pieces
#if LIBTORRENT_VERSION_NUM < 10000
bitfield bf(h.get_torrent_info().num_pieces(), 0);
#else
bitfield bf(h.torrent_file()->num_pieces(), 0);
#endif
h.downloading_pieces(bf);
downloaded_pieces->setProgress(status.pieces, bf);
downloaded_pieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces());
// Pieces availability
if (!h.is_paused(status) && !h.is_queued(status) && !h.is_checking(status)) {
if (!m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking()) {
showPiecesAvailability(true);
std::vector<int> avail;
h.piece_availability(avail);
pieces_availability->setAvailability(avail);
avail_average_lbl->setText(misc::accurateDoubleToString(status.distributed_copies, 3));
pieces_availability->setAvailability(m_torrent->pieceAvailability());
avail_average_lbl->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3));
} else {
showPiecesAvailability(false);
}
// Progress
qreal progress = h.progress(status)*100.;
progress_lbl->setText(misc::accurateDoubleToString(progress, 1)+"%");
qreal progress = m_torrent->progress() * 100.;
progress_lbl->setText(Utils::String::fromDouble(progress, 1)+"%");
} else {
showPiecesAvailability(false);
showPiecesDownloaded(false);
@@ -404,20 +385,15 @@ void PropertiesWidget::loadDynamicData() {
}
if (stackedProperties->currentIndex() == PropTabBar::PEERS_TAB) {
// Load peers
peersList->loadPeers(h);
peersList->loadPeers(m_torrent);
return;
}
if (stackedProperties->currentIndex() == PropTabBar::FILES_TAB) {
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters
| torrent_handle::query_distributed_copies
| torrent_handle::query_pieces);
// Files progress
if (h.is_valid() && status.has_metadata) {
if (m_torrent->hasMetadata()) {
qDebug("Updating priorities in files tab");
filesList->setUpdatesEnabled(false);
std::vector<size_type> fp;
h.file_progress(fp);
PropListModel->model()->updateFilesProgress(fp);
PropListModel->model()->updateFilesProgress(m_torrent->filesProgress());
// XXX: We don't update file priorities regularly for performance
// reasons. This means that priorities will not be updated if
// set from the Web UI.
@@ -425,25 +401,22 @@ void PropertiesWidget::loadDynamicData() {
filesList->setUpdatesEnabled(true);
}
}
} catch(const invalid_handle& e) {
qWarning() << "Caught exception in PropertiesWidget::loadDynamicData(): " << misc::toQStringU(e.what());
}
}
void PropertiesWidget::loadUrlSeeds() {
listWebSeeds->clear();
qDebug("Loading URL seeds");
const QStringList hc_seeds = h.url_seeds();
const QList<QUrl> hc_seeds = m_torrent->urlSeeds();
// Add url seeds
foreach (const QString &hc_seed, hc_seeds) {
qDebug("Loading URL seed: %s", qPrintable(hc_seed));
new QListWidgetItem(hc_seed, listWebSeeds);
foreach (const QUrl &hc_seed, hc_seeds) {
qDebug("Loading URL seed: %s", qPrintable(hc_seed.toString()));
new QListWidgetItem(hc_seed.toString(), listWebSeeds);
}
}
void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index) {
if (!index.isValid()) return;
if (!h.is_valid() || !h.has_metadata()) return;
if (!m_torrent || !m_torrent->hasMetadata()) return;
if (PropListModel->itemType(index) == TorrentContentModelItem::FileType)
openFile(index);
else
@@ -452,15 +425,15 @@ void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index) {
void PropertiesWidget::openFile(const QModelIndex &index) {
int i = PropListModel->getFileIndex(index);
const QDir saveDir(h.save_path());
const QString filename = h.filepath_at(i);
const QString file_path = fsutils::expandPath(saveDir.absoluteFilePath(filename));
const QDir saveDir(m_torrent->actualSavePath());
const QString filename = m_torrent->filePath(i);
const QString file_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(filename));
qDebug("Trying to open file at %s", qPrintable(file_path));
// Flush data
h.flush_cache();
m_torrent->flushCache();
if (QFile::exists(file_path)) {
if (file_path.startsWith("//"))
QDesktopServices::openUrl(fsutils::toNativePath("file:" + file_path));
QDesktopServices::openUrl(Utils::Fs::toNativePath("file:" + file_path));
else
QDesktopServices::openUrl(QUrl::fromLocalFile(file_path));
}
@@ -487,24 +460,24 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
if (containing_folder)
path_items.removeLast();
#endif
const QDir saveDir(h.save_path());
const QDir saveDir(m_torrent->actualSavePath());
const QString relative_path = path_items.join("/");
absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path));
absolute_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(relative_path));
}
else {
int i = PropListModel->getFileIndex(index);
const QDir saveDir(h.save_path());
const QString relative_path = h.filepath_at(i);
absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path));
int i = PropListModel->getFileIndex(index);
const QDir saveDir(m_torrent->actualSavePath());
const QString relative_path = m_torrent->filePath(i);
absolute_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(relative_path));
#if !(defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)))
if (containing_folder)
absolute_path = fsutils::folderName(absolute_path);
if (containing_folder)
absolute_path = Utils::Fs::folderName(absolute_path);
#endif
}
// Flush data
h.flush_cache();
m_torrent->flushCache();
if (!QFile::exists(absolute_path))
return;
qDebug("Trying to open folder at %s", qPrintable(absolute_path));
@@ -513,7 +486,7 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
if (containing_folder) {
// Syntax is: explorer /select, "C:\Folder1\Folder2\file_to_select"
// Dir separators MUST be win-style slashes
QProcess::startDetached("explorer.exe", QStringList() << "/select," << fsutils::toNativePath(absolute_path));
QProcess::startDetached("explorer.exe", QStringList() << "/select," << Utils::Fs::toNativePath(absolute_path));
} else {
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (containing_folder) {
@@ -523,11 +496,11 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
proc.waitForFinished();
output = proc.readLine().simplified();
if (output == "dolphin.desktop")
proc.startDetached("dolphin", QStringList() << "--select" << fsutils::toNativePath(absolute_path));
proc.startDetached("dolphin", QStringList() << "--select" << Utils::Fs::toNativePath(absolute_path));
else if (output == "nautilus-folder-handler.desktop")
proc.startDetached("nautilus", QStringList() << "--no-desktop" << fsutils::toNativePath(absolute_path));
proc.startDetached("nautilus", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(absolute_path));
else if (output == "kfmclient_dir.desktop")
proc.startDetached("konqueror", QStringList() << "--select" << fsutils::toNativePath(absolute_path));
proc.startDetached("konqueror", QStringList() << "--select" << Utils::Fs::toNativePath(absolute_path));
else
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(absolute_path).absolutePath()));
} else {
@@ -535,7 +508,7 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
if (QFile::exists(absolute_path)) {
// Hack to access samba shares with QDesktopServices::openUrl
if (absolute_path.startsWith("//"))
QDesktopServices::openUrl(fsutils::toNativePath("file:" + absolute_path));
QDesktopServices::openUrl(Utils::Fs::toNativePath("file:" + absolute_path));
else
QDesktopServices::openUrl(QUrl::fromLocalFile(absolute_path));
} else {
@@ -547,8 +520,8 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
}
void PropertiesWidget::displayFilesListMenu(const QPoint&) {
if (!h.is_valid())
return;
if (!m_torrent) return;
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0);
if (selectedRows.empty())
return;
@@ -557,13 +530,13 @@ void PropertiesWidget::displayFilesListMenu(const QPoint&) {
QAction *actOpenContainingFolder = 0;
QAction *actRename = 0;
if (selectedRows.size() == 1) {
actOpen = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("folder-documents"), tr("Open"));
actOpenContainingFolder = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("inode-directory"), tr("Open Containing Folder"));
actRename = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
actOpen = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("folder-documents"), tr("Open"));
actOpenContainingFolder = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("inode-directory"), tr("Open Containing Folder"));
actRename = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
myFilesLlistMenu.addSeparator();
}
QMenu subMenu;
if (!h.status(0x0).is_seeding) {
if (!m_torrent->isSeed()) {
subMenu.setTitle(tr("Priority"));
subMenu.addAction(actionNot_downloaded);
subMenu.addAction(actionNormal);
@@ -606,20 +579,20 @@ void PropertiesWidget::displayFilesListMenu(const QPoint&) {
}
void PropertiesWidget::displayWebSeedListMenu(const QPoint&) {
if (!h.is_valid())
return;
if (!m_torrent) return;
QMenu seedMenu;
QModelIndexList rows = listWebSeeds->selectionModel()->selectedRows();
QAction *actAdd = seedMenu.addAction(IconProvider::instance()->getIcon("list-add"), tr("New Web seed"));
QAction *actAdd = seedMenu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("New Web seed"));
QAction *actDel = 0;
QAction *actCpy = 0;
QAction *actEdit = 0;
if (rows.size()) {
actDel = seedMenu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove Web seed"));
actDel = seedMenu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove Web seed"));
seedMenu.addSeparator();
actCpy = seedMenu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy Web seed URL"));
actEdit = seedMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Edit Web seed URL"));
actCpy = seedMenu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy Web seed URL"));
actEdit = seedMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Edit Web seed URL"));
}
const QAction *act = seedMenu.exec(QCursor::pos());
@@ -648,7 +621,7 @@ void PropertiesWidget::renameSelectedFile() {
tr("New name:"), QLineEdit::Normal,
index.data().toString(), &ok).trimmed();
if (ok && !new_name_last.isEmpty()) {
if (!fsutils::isValidFileSystemName(new_name_last)) {
if (!Utils::Fs::isValidFileSystemName(new_name_last)) {
QMessageBox::warning(this, tr("The file could not be renamed"),
tr("This file name contains forbidden characters, please choose a different one."),
QMessageBox::Ok);
@@ -657,8 +630,8 @@ void PropertiesWidget::renameSelectedFile() {
if (PropListModel->itemType(index) == TorrentContentModelItem::FileType) {
// File renaming
const int file_index = PropListModel->getFileIndex(index);
if (!h.is_valid() || !h.has_metadata()) return;
QString old_name = h.filepath_at(file_index);
if (!m_torrent || !m_torrent->hasMetadata()) return;
QString old_name = m_torrent->filePath(file_index);
if (old_name.endsWith(".!qB") && !new_name_last.endsWith(".!qB")) {
new_name_last += ".!qB";
}
@@ -670,14 +643,14 @@ void PropertiesWidget::renameSelectedFile() {
qDebug("Name did not change");
return;
}
new_name = fsutils::expandPath(new_name);
new_name = Utils::Fs::expandPath(new_name);
// Check if that name is already used
for (int i=0; i<h.num_files(); ++i) {
for (int i = 0; i < m_torrent->filesCount(); ++i) {
if (i == file_index) continue;
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
if (h.filepath_at(i).compare(new_name, Qt::CaseSensitive) == 0) {
if (m_torrent->filePath(i).compare(new_name, Qt::CaseSensitive) == 0) {
#else
if (h.filepath_at(i).compare(new_name, Qt::CaseInsensitive) == 0) {
if (m_torrent->filePath(i).compare(new_name, Qt::CaseInsensitive) == 0) {
#endif
// Display error message
QMessageBox::warning(this, tr("The file could not be renamed"),
@@ -686,11 +659,11 @@ void PropertiesWidget::renameSelectedFile() {
return;
}
}
const bool force_recheck = QFile::exists(h.save_path()+"/"+new_name);
const bool force_recheck = QFile::exists(m_torrent->actualSavePath() + "/" + new_name);
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name));
h.rename_file(file_index, new_name);
m_torrent->renameFile(file_index, new_name);
// Force recheck
if (force_recheck) h.force_recheck();
if (force_recheck) m_torrent->forceRecheck();
// Rename if torrent files model too
if (new_name_last.endsWith(".!qB"))
new_name_last.chop(4);
@@ -710,9 +683,9 @@ void PropertiesWidget::renameSelectedFile() {
QString new_path = path_items.join("/");
if (!new_path.endsWith("/")) new_path += "/";
// Check for overwriting
const int num_files = h.num_files();
const int num_files = m_torrent->filesCount();
for (int i=0; i<num_files; ++i) {
const QString current_name = h.filepath_at(i);
const QString current_name = m_torrent->filePath(i);
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
if (current_name.startsWith(new_path, Qt::CaseSensitive)) {
#else
@@ -727,27 +700,27 @@ void PropertiesWidget::renameSelectedFile() {
bool force_recheck = false;
// Replace path in all files
for (int i=0; i<num_files; ++i) {
const QString current_name = h.filepath_at(i);
const QString current_name = m_torrent->filePath(i);
if (current_name.startsWith(old_path)) {
QString new_name = current_name;
new_name.replace(0, old_path.length(), new_path);
if (!force_recheck && QDir(h.save_path()).exists(new_name))
if (!force_recheck && QDir(m_torrent->actualSavePath()).exists(new_name))
force_recheck = true;
new_name = fsutils::expandPath(new_name);
new_name = Utils::Fs::expandPath(new_name);
qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name));
h.rename_file(i, new_name);
m_torrent->renameFile(i, new_name);
}
}
// Force recheck
if (force_recheck) h.force_recheck();
if (force_recheck) m_torrent->forceRecheck();
// Rename folder in torrent files model too
PropListModel->setData(index, new_name_last);
// Remove old folder
const QDir old_folder(h.save_path()+"/"+old_path);
const QDir old_folder(m_torrent->actualSavePath() + "/" + old_path);
int timeout = 10;
while(!QDir().rmpath(old_folder.absolutePath()) && timeout > 0) {
// XXX: We should not sleep here (freezes the UI for 1 second)
misc::msleep(100);
// FIXME: We should not sleep here (freezes the UI for 1 second)
Utils::Misc::msleep(100);
--timeout;
}
}
@@ -775,28 +748,23 @@ void PropertiesWidget::askWebSeed() {
QMessageBox::Ok);
return;
}
if (h.is_valid())
h.add_url_seed(url_seed);
if (m_torrent)
m_torrent->addUrlSeeds(QList<QUrl>() << url_seed);
// Refresh the seeds list
loadUrlSeeds();
}
void PropertiesWidget::deleteSelectedUrlSeeds() {
const QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems();
if (selectedItems.isEmpty())
return;
bool change = false;
foreach (const QListWidgetItem *item, selectedItems) {
QString url_seed = item->text();
try {
h.remove_url_seed(url_seed);
change = true;
} catch (invalid_handle&) {}
}
if (change) {
// Refresh list
loadUrlSeeds();
}
if (selectedItems.isEmpty()) return;
QList<QUrl> urlSeeds;
foreach (const QListWidgetItem *item, selectedItems)
urlSeeds << item->text();
m_torrent->removeUrlSeeds(urlSeeds);
// Refresh list
loadUrlSeeds();
}
void PropertiesWidget::copySelectedWebSeedsToClipboard() const {
@@ -832,31 +800,28 @@ void PropertiesWidget::editWebSeed() {
return;
}
try {
h.remove_url_seed(old_seed);
h.add_url_seed(new_seed);
loadUrlSeeds();
} catch (invalid_handle&) {}
m_torrent->removeUrlSeeds(QList<QUrl>() << old_seed);
m_torrent->addUrlSeeds(QList<QUrl>() << new_seed);
loadUrlSeeds();
}
bool PropertiesWidget::applyPriorities() {
qDebug("Saving files priorities");
const std::vector<int> priorities = PropListModel->model()->getFilesPriorities();
const QVector<int> priorities = PropListModel->model()->getFilePriorities();
// Save first/last piece first option state
bool first_last_piece_first = h.first_last_piece_first();
bool first_last_piece_first = m_torrent->hasFirstLastPiecePriority();
// Prioritize the files
qDebug("prioritize files: %d", priorities[0]);
h.prioritize_files(priorities);
m_torrent->prioritizeFiles(priorities);
// Restore first/last piece first option if necessary
if (first_last_piece_first)
h.prioritize_first_last_piece(true);
m_torrent->setFirstLastPiecePriority(true);
return true;
}
void PropertiesWidget::filteredFilesChanged() {
if (h.is_valid()) {
if (m_torrent)
applyPriorities();
}
}
void PropertiesWidget::filterText(const QString& filter) {

View File

@@ -34,7 +34,7 @@
#include <QShortcut>
#include <QWidget>
#include "ui_propertieswidget.h"
#include "qtorrenthandle.h"
#include "core/bittorrent/torrenthandle.h"
class TransferListWidget;
@@ -64,23 +64,18 @@ public:
public:
PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList);
~PropertiesWidget();
QTorrentHandle getCurrentTorrent() const;
BitTorrent::TorrentHandle *getCurrentTorrent() const;
TrackerList* getTrackerList() const { return trackerList; }
PeerListWidget* getPeerList() const { return peersList; }
QTreeView* getFilesList() const { return filesList; }
signals:
void trackersAdded(const QStringList &trackers, const QString &hash);
void trackersRemoved(const QStringList &trackers, const QString &hash);
void trackerlessChange(bool trackerless, const QString &hash);
protected:
QPushButton* getButtonFromIndex(int index);
bool applyPriorities();
protected slots:
void loadTorrentInfos(const QTorrentHandle &h);
void updateTorrentInfos(const QTorrentHandle &h);
void loadTorrentInfos(BitTorrent::TorrentHandle *const torrent);
void updateTorrentInfos(BitTorrent::TorrentHandle *const torrent);
void loadUrlSeeds();
void askWebSeed();
void deleteSelectedUrlSeeds();
@@ -102,8 +97,8 @@ public slots:
void saveSettings();
void reloadPreferences();
void openDoubleClickedFile(const QModelIndex &);
void updateSavePath(const QTorrentHandle& h);
void loadTrackers(const QTorrentHandle &handle);
void updateSavePath(BitTorrent::TorrentHandle *const torrent);
void loadTrackers(BitTorrent::TorrentHandle *const torrent);
private:
void openFile(const QModelIndex &index);
@@ -112,7 +107,7 @@ private:
private:
TransferListWidget *transferList;
MainWindow *main_window;
QTorrentHandle h;
BitTorrent::TorrentHandle *m_torrent;
QTimer *refreshTimer;
SlideState state;
TorrentContentFilterModel *PropListModel;

View File

@@ -0,0 +1,191 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include <QStyleOptionProgressBarV2>
#include <QStyleOptionViewItemV2>
#include <QStyleOptionComboBox>
#include <QComboBox>
#include <QModelIndex>
#include <QPainter>
#include <QProgressBar>
#include <QApplication>
#ifdef Q_OS_WIN
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
#include <QPlastiqueStyle>
#else
#include <QProxyStyle>
#endif
#endif
#include "core/utils/misc.h"
#include "core/utils/string.h"
#include "propertieswidget.h"
#include "proplistdelegate.h"
PropListDelegate::PropListDelegate(PropertiesWidget *properties, QObject *parent)
: QItemDelegate(parent)
, m_properties(properties)
{
}
void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
painter->save();
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
switch(index.column()) {
case PCSIZE:
QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong()));
break;
case PROGRESS:
if (index.data().toDouble() >= 0) {
QStyleOptionProgressBarV2 newopt;
qreal progress = index.data().toDouble() * 100.;
newopt.rect = opt.rect;
newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%");
newopt.progress = (int)progress;
newopt.maximum = 100;
newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = true;
#ifndef Q_OS_WIN
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
#else
// XXX: To avoid having the progress text on the right of the bar
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
QPlastiqueStyle st;
#else
QProxyStyle st("fusion");
#endif
st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
#endif
}
else {
// Do not display anything if the file is disabled (progress == 0)
QItemDelegate::drawBackground(painter, opt, index);
}
break;
case PRIORITY: {
QItemDelegate::drawBackground(painter, opt, index);
QString text = "";
switch (index.data().toInt()) {
case -1:
text = tr("Mixed", "Mixed (priorities");
break;
case 0:
text = tr("Not downloaded");
break;
case 2:
text = tr("High", "High (priority)");
break;
case 7:
text = tr("Maximum", "Maximum (priority)");
break;
default:
text = tr("Normal", "Normal (priority)");
break;
}
QItemDelegate::drawDisplay(painter, opt, option.rect, text);
}
break;
default:
QItemDelegate::paint(painter, option, index);
break;
}
painter->restore();
}
void PropListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QComboBox *combobox = static_cast<QComboBox*>(editor);
// Set combobox index
switch(index.data().toInt()) {
case 2:
combobox->setCurrentIndex(1);
break;
case 7:
combobox->setCurrentIndex(2);
break;
default:
combobox->setCurrentIndex(0);
break;
}
}
QWidget *PropListDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
{
if (index.column() != PRIORITY) return 0;
if (m_properties) {
BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent();
if (!torrent || !torrent->hasMetadata() || torrent->isSeed())
return 0;
}
if (index.data().toInt() <= 0) {
// IGNORED or MIXED
return 0;
}
QComboBox* editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus);
editor->addItem(tr("Normal", "Normal (priority)"));
editor->addItem(tr("High", "High (priority)"));
editor->addItem(tr("Maximum", "Maximum (priority)"));
return editor;
}
void PropListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QComboBox *combobox = static_cast<QComboBox*>(editor);
int value = combobox->currentIndex();
qDebug("PropListDelegate: setModelData(%d)", value);
switch(value) {
case 1:
model->setData(index, 2); // HIGH
break;
case 2:
model->setData(index, 7); // MAX
break;
default:
model->setData(index, 1); // NORMAL
}
emit filteredFilesChanged();
}
void PropListDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const
{
qDebug("UpdateEditor Geometry called");
editor->setGeometry(option.rect);
}

View File

@@ -1,5 +1,5 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -32,167 +32,42 @@
#define PROPLISTDELEGATE_H
#include <QItemDelegate>
#include <QStyleOptionProgressBarV2>
#include <QStyleOptionViewItemV2>
#include <QStyleOptionComboBox>
#include <QComboBox>
#include <QModelIndex>
#include <QPainter>
#include <QProgressBar>
#include <QApplication>
#include "misc.h"
#include "propertieswidget.h"
#ifdef Q_OS_WIN
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
#include <QPlastiqueStyle>
#else
#include <QProxyStyle>
#endif
#endif
class QPainter;
class QModelIndex;
class QStyleOptionViewItem;
class QAbstractItemModel;
class PropertiesWidget;
// Defines for properties list columns
enum PropColumn {NAME, PCSIZE, PROGRESS, PRIORITY};
enum PropColumn
{
NAME,
PCSIZE,
PROGRESS,
PRIORITY
};
class PropListDelegate: public QItemDelegate {
Q_OBJECT
private:
PropertiesWidget *properties;
signals:
void filteredFilesChanged() const;
class PropListDelegate : public QItemDelegate
{
Q_OBJECT
public:
PropListDelegate(PropertiesWidget* properties=0, QObject *parent=0) : QItemDelegate(parent), properties(properties) {
}
PropListDelegate(PropertiesWidget *properties = 0, QObject *parent = 0);
~PropListDelegate() {}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
painter->save();
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
switch(index.column()) {
case PCSIZE:
QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong()));
break;
case PROGRESS:{
if (index.data().toDouble() >= 0) {
QStyleOptionProgressBarV2 newopt;
qreal progress = index.data().toDouble()*100.;
newopt.rect = opt.rect;
newopt.text = ((progress == 100.0) ? QString("100%") : misc::accurateDoubleToString(progress, 1) + "%");
newopt.progress = (int)progress;
newopt.maximum = 100;
newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = true;
#ifndef Q_OS_WIN
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
#else
// XXX: To avoid having the progress text on the right of the bar
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
QPlastiqueStyle st;
#else
QProxyStyle st("fusion");
#endif
st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
#endif
} else {
// Do not display anything if the file is disabled (progress == -1)
QItemDelegate::drawBackground(painter, opt, index);
}
break;
}
case PRIORITY: {
QItemDelegate::drawBackground(painter, opt, index);
QString text = "";
switch(index.data().toInt()) {
case -1:
text = tr("Mixed", "Mixed (priorities");
break;
case 0:
text = tr("Not downloaded");
break;
case 2:
text = tr("High", "High (priority)");
break;
case 7:
text = tr("Maximum", "Maximum (priority)");
break;
default:
text = tr("Normal", "Normal (priority)");
break;
}
QItemDelegate::drawDisplay(painter, opt, option.rect, text);
break;
}
default:
QItemDelegate::paint(painter, option, index);
break;
}
painter->restore();
}
void setEditorData(QWidget *editor, const QModelIndex &index) const {
QComboBox *combobox = static_cast<QComboBox*>(editor);
// Set combobox index
switch(index.data().toInt()) {
case 2:
combobox->setCurrentIndex(1);
break;
case 7:
combobox->setCurrentIndex(2);
break;
default:
combobox->setCurrentIndex(0);
break;
}
}
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const {
if (index.column() != PRIORITY) return 0;
if (properties) {
QTorrentHandle h = properties->getCurrentTorrent();
if (!h.is_valid() || !h.has_metadata() || h.status(0x0).is_seeding)
return 0;
}
if (index.data().toInt() <= 0) {
// IGNORED or MIXED
return 0;
}
QComboBox* editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus);
editor->addItem(tr("Normal", "Normal (priority)"));
editor->addItem(tr("High", "High (priority)"));
editor->addItem(tr("Maximum", "Maximum (priority)"));
return editor;
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const;
public slots:
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
QComboBox *combobox = static_cast<QComboBox*>(editor);
int value = combobox->currentIndex();
qDebug("PropListDelegate: setModelData(%d)", value);
switch(value) {
case 1:
model->setData(index, 2); // HIGH
break;
case 2:
model->setData(index, 7); // MAX
break;
default:
model->setData(index, 1); // NORMAL
}
emit filteredFilesChanged();
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
qDebug("UpdateEditor Geometry called");
editor->setGeometry(option.rect);
}
signals:
void filteredFilesChanged() const;
private:
PropertiesWidget *m_properties;
};
#endif

View File

@@ -34,7 +34,7 @@
#include <QKeySequence>
#include "proptabbar.h"
#include "iconprovider.h"
#include "guiiconprovider.h"
PropTabBar::PropTabBar(QWidget *parent) :
QHBoxLayout(parent), m_currentIndex(-1)
@@ -42,24 +42,24 @@ PropTabBar::PropTabBar(QWidget *parent) :
setSpacing(2);
m_btnGroup = new QButtonGroup(this);
// General tab
QPushButton *main_infos_button = new QPushButton(IconProvider::instance()->getIcon("document-properties"), tr("General"), parent);
QPushButton *main_infos_button = new QPushButton(GuiIconProvider::instance()->getIcon("document-properties"), tr("General"), parent);
main_infos_button->setShortcut(QKeySequence(QString::fromUtf8("Alt+P")));
addWidget(main_infos_button);
m_btnGroup->addButton(main_infos_button, MAIN_TAB);
// Trackers tab
QPushButton *trackers_button = new QPushButton(IconProvider::instance()->getIcon("network-server"), tr("Trackers"), parent);
QPushButton *trackers_button = new QPushButton(GuiIconProvider::instance()->getIcon("network-server"), tr("Trackers"), parent);
addWidget(trackers_button);
m_btnGroup->addButton(trackers_button, TRACKERS_TAB);
// Peers tab
QPushButton *peers_button = new QPushButton(IconProvider::instance()->getIcon("edit-find-user"), tr("Peers"), parent);
QPushButton *peers_button = new QPushButton(GuiIconProvider::instance()->getIcon("edit-find-user"), tr("Peers"), parent);
addWidget(peers_button);
m_btnGroup->addButton(peers_button, PEERS_TAB);
// URL seeds tab
QPushButton *urlseeds_button = new QPushButton(IconProvider::instance()->getIcon("network-server"), tr("HTTP Sources"), parent);
QPushButton *urlseeds_button = new QPushButton(GuiIconProvider::instance()->getIcon("network-server"), tr("HTTP Sources"), parent);
addWidget(urlseeds_button);
m_btnGroup->addButton(urlseeds_button, URLSEEDS_TAB);
// Files tab
QPushButton *files_button = new QPushButton(IconProvider::instance()->getIcon("inode-directory"), tr("Content"), parent);
QPushButton *files_button = new QPushButton(GuiIconProvider::instance()->getIcon("inode-directory"), tr("Content"), parent);
addWidget(files_button);
m_btnGroup->addButton(files_button, FILES_TAB);
// Spacer

View File

@@ -36,18 +36,19 @@
#include <QColor>
#include <QDebug>
#include <QUrl>
#include <libtorrent/version.hpp>
#include <libtorrent/peer_info.hpp>
#include "trackerlist.h"
#include <QMessageBox>
#include "core/bittorrent/session.h"
#include "core/bittorrent/torrenthandle.h"
#include "core/bittorrent/peerinfo.h"
#include "core/bittorrent/trackerentry.h"
#include "core/preferences.h"
#include "core/utils/misc.h"
#include "propertieswidget.h"
#include "trackersadditiondlg.h"
#include "iconprovider.h"
#include "qbtsession.h"
#include "preferences.h"
#include "misc.h"
#include "guiiconprovider.h"
#include "autoexpandabledialog.h"
using namespace libtorrent;
#include "trackerlist.h"
TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) {
// Graphical settings
@@ -108,8 +109,8 @@ void TrackerList::setRowColor(int row, QColor color) {
}
void TrackerList::moveSelectionUp() {
QTorrentHandle h = properties->getCurrentTorrent();
if (!h.is_valid()) {
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) {
clear();
return;
}
@@ -131,23 +132,23 @@ void TrackerList::moveSelectionUp() {
}
setSelectionModel(selection);
// Update torrent trackers
std::vector<announce_entry> trackers;
for (int i=NB_STICKY_ITEM; i<topLevelItemCount(); ++i) {
QList<BitTorrent::TrackerEntry> trackers;
for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i) {
QString tracker_url = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
announce_entry e(tracker_url.toStdString());
e.tier = i-NB_STICKY_ITEM;
trackers.push_back(e);
BitTorrent::TrackerEntry e(tracker_url);
e.setTier(i - NB_STICKY_ITEM);
trackers.append(e);
}
h.replace_trackers(trackers);
torrent->replaceTrackers(trackers);
// Reannounce
if (!h.is_paused())
h.force_reannounce();
loadTrackers();
if (!torrent->isPaused())
torrent->forceReannounce();
}
void TrackerList::moveSelectionDown() {
QTorrentHandle h = properties->getCurrentTorrent();
if (!h.is_valid()) {
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) {
clear();
return;
}
@@ -169,18 +170,18 @@ void TrackerList::moveSelectionDown() {
}
setSelectionModel(selection);
// Update torrent trackers
std::vector<announce_entry> trackers;
for (int i=NB_STICKY_ITEM; i<topLevelItemCount(); ++i) {
QList<BitTorrent::TrackerEntry> trackers;
for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i) {
QString tracker_url = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString();
announce_entry e(tracker_url.toStdString());
e.tier = i-NB_STICKY_ITEM;
trackers.push_back(e);
BitTorrent::TrackerEntry e(tracker_url);
e.setTier(i - NB_STICKY_ITEM);
trackers.append(e);
}
h.replace_trackers(trackers);
torrent->replaceTrackers(trackers);
// Reannounce
if (!h.is_paused())
h.force_reannounce();
loadTrackers();
if (!torrent->isPaused())
torrent->forceReannounce();
}
void TrackerList::clear() {
@@ -197,29 +198,29 @@ void TrackerList::clear() {
lsd_item->setText(COL_MSG, "");
}
void TrackerList::loadStickyItems(const QTorrentHandle &h) {
void TrackerList::loadStickyItems(BitTorrent::TorrentHandle *const torrent) {
QString working = tr("Working");
QString disabled = tr("Disabled");
// load DHT information
if (QBtSession::instance()->isDHTEnabled() && !h.priv())
if (BitTorrent::Session::instance()->isDHTEnabled() && !torrent->isPrivate())
dht_item->setText(COL_STATUS, working);
else
dht_item->setText(COL_STATUS, disabled);
// Load PeX Information
if (QBtSession::instance()->isPexEnabled() && !h.priv())
if (BitTorrent::Session::instance()->isPexEnabled() && !torrent->isPrivate())
pex_item->setText(COL_STATUS, working);
else
pex_item->setText(COL_STATUS, disabled);
// Load LSD Information
if (QBtSession::instance()->isLSDEnabled() && !h.priv())
if (BitTorrent::Session::instance()->isLSDEnabled() && !torrent->isPrivate())
lsd_item->setText(COL_STATUS, working);
else
lsd_item->setText(COL_STATUS, disabled);
if (h.priv()) {
if (torrent->isPrivate()) {
QString privateMsg = tr("This torrent is private");
dht_item->setText(COL_MSG, privateMsg);
pex_item->setText(COL_MSG, privateMsg);
@@ -229,16 +230,12 @@ void TrackerList::loadStickyItems(const QTorrentHandle &h) {
// XXX: libtorrent should provide this info...
// Count peers from DHT, LSD, PeX
uint nb_dht = 0, nb_lsd = 0, nb_pex = 0;
std::vector<peer_info> peers;
h.get_peer_info(peers);
std::vector<peer_info>::iterator it = peers.begin();
std::vector<peer_info>::iterator end = peers.end();
for ( ; it != end; ++it) {
if (it->source & peer_info::dht)
foreach (const BitTorrent::PeerInfo &peer, torrent->peers()) {
if (peer.fromDHT())
++nb_dht;
if (it->source & peer_info::lsd)
if (peer.fromLSD())
++nb_lsd;
if (it->source & peer_info::pex)
if (peer.fromPeX())
++nb_pex;
}
dht_item->setText(COL_PEERS, QString::number(nb_dht));
@@ -248,47 +245,47 @@ void TrackerList::loadStickyItems(const QTorrentHandle &h) {
void TrackerList::loadTrackers() {
// Load trackers from torrent handle
QTorrentHandle h = properties->getCurrentTorrent();
if (!h.is_valid()) return;
loadStickyItems(h);
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) return;
loadStickyItems(torrent);
// Load actual trackers information
QHash<QString, TrackerInfos> trackers_data = QBtSession::instance()->getTrackersInfo(h.hash());
QHash<QString, BitTorrent::TrackerInfo> trackers_data = torrent->trackerInfos();
QStringList old_trackers_urls = tracker_items.keys();
const std::vector<announce_entry> trackers = h.trackers();
std::vector<announce_entry>::const_iterator it = trackers.begin();
std::vector<announce_entry>::const_iterator end = trackers.end();
for ( ; it != end; ++it) {
QString tracker_url = misc::toQString(it->url);
QTreeWidgetItem *item = tracker_items.value(tracker_url, 0);
foreach (const BitTorrent::TrackerEntry &entry, torrent->trackers()) {
QString trackerUrl = entry.url();
QTreeWidgetItem *item = tracker_items.value(trackerUrl, 0);
if (!item) {
item = new QTreeWidgetItem();
item->setText(COL_URL, tracker_url);
item->setText(COL_URL, trackerUrl);
addTopLevelItem(item);
tracker_items[tracker_url] = item;
tracker_items[trackerUrl] = item;
} else {
old_trackers_urls.removeOne(tracker_url);
old_trackers_urls.removeOne(trackerUrl);
}
item->setText(COL_TIER, QString::number(it->tier));
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
QString error_message = data.last_message.trimmed();
if (it->verified) {
item->setText(COL_STATUS, tr("Working"));
item->setText(COL_MSG, "");
} else {
if (it->updating && it->fails == 0) {
item->setText(COL_TIER, QString::number(entry.tier()));
BitTorrent::TrackerInfo data = trackers_data.value(trackerUrl);
QString error_message = data.lastMessage.trimmed();
switch (entry.status()) {
case BitTorrent::TrackerEntry::Working:
item->setText(COL_STATUS, tr("Working"));
item->setText(COL_MSG, "");
break;
case BitTorrent::TrackerEntry::Updating:
item->setText(COL_STATUS, tr("Updating..."));
item->setText(COL_MSG, "");
} else {
if (it->fails > 0) {
item->setText(COL_STATUS, tr("Not working"));
item->setText(COL_MSG, error_message);
} else {
item->setText(COL_STATUS, tr("Not contacted yet"));
item->setText(COL_MSG, "");
}
}
break;
case BitTorrent::TrackerEntry::NotWorking:
item->setText(COL_STATUS, tr("Not working"));
item->setText(COL_MSG, error_message);
break;
case BitTorrent::TrackerEntry::NotContacted:
item->setText(COL_STATUS, tr("Not contacted yet"));
item->setText(COL_MSG, "");
break;
}
item->setText(COL_PEERS, QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers));
item->setText(COL_PEERS, QString::number(trackers_data.value(trackerUrl).numPeers));
}
// Remove old trackers
foreach (const QString &tracker, old_trackers_urls) {
@@ -298,12 +295,13 @@ void TrackerList::loadTrackers() {
// Ask the user for new trackers and add them to the torrent
void TrackerList::askForTrackers() {
QTorrentHandle h = properties->getCurrentTorrent();
if (!h.is_valid()) return;
QString hash = h.hash();
QStringList trackers = TrackersAdditionDlg::askForTrackers(h);
QBtSession::instance()->addTrackersAndUrlSeeds(hash, trackers, QStringList());
loadTrackers();
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) return;
QList<BitTorrent::TrackerEntry> trackers;
foreach (const QString &tracker, TrackersAdditionDlg::askForTrackers(torrent))
trackers << tracker;
torrent->addTrackers(trackers);
}
void TrackerList::copyTrackerUrl() {
@@ -320,14 +318,15 @@ void TrackerList::copyTrackerUrl() {
void TrackerList::deleteSelectedTrackers() {
QTorrentHandle h = properties->getCurrentTorrent();
if (!h.is_valid()) {
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) {
clear();
return;
}
QString hash = h.hash();
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
if (selected_items.isEmpty()) return;
QStringList urls_to_remove;
foreach (QTreeWidgetItem *item, selected_items) {
QString tracker_url = item->data(COL_URL, Qt::DisplayRole).toString();
@@ -335,32 +334,26 @@ void TrackerList::deleteSelectedTrackers() {
tracker_items.remove(tracker_url);
delete item;
}
// Iterate of trackers and remove selected ones
std::vector<announce_entry> remaining_trackers;
std::vector<announce_entry> trackers = h.trackers();
std::vector<announce_entry>::iterator it = trackers.begin();
std::vector<announce_entry>::iterator itend = trackers.end();
for ( ; it != itend; ++it) {
if (!urls_to_remove.contains(misc::toQString((*it).url))) {
remaining_trackers.push_back(*it);
// Iterate of trackers and remove selected ones
QList<BitTorrent::TrackerEntry> remaining_trackers;
QList<BitTorrent::TrackerEntry> trackers = torrent->trackers();
foreach (const BitTorrent::TrackerEntry &entry, trackers) {
if (!urls_to_remove.contains(entry.url())) {
remaining_trackers.push_back(entry);
}
}
h.replace_trackers(remaining_trackers);
if (!urls_to_remove.empty())
emit trackersRemoved(urls_to_remove, hash);
if (remaining_trackers.empty())
emit trackerlessChange(true, hash);
if (!h.is_paused())
h.force_reannounce();
// Reload Trackers
loadTrackers();
torrent->replaceTrackers(remaining_trackers);
if (!torrent->isPaused())
torrent->forceReannounce();
}
void TrackerList::editSelectedTracker() {
try {
QTorrentHandle h = properties->getCurrentTorrent();
QString hash = h.hash();
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) return;
QString hash = torrent->hash();
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
if (selected_items.isEmpty())
@@ -381,91 +374,78 @@ void TrackerList::editSelectedTracker() {
if (new_tracker_url == tracker_url)
return;
std::vector<announce_entry> trackers = h.trackers();
std::vector<announce_entry>::iterator it = trackers.begin();
std::vector<announce_entry>::iterator itend = trackers.end();
QList<BitTorrent::TrackerEntry> trackers = torrent->trackers();
bool match = false;
for ( ; it != itend; ++it) {
if (new_tracker_url == QUrl(misc::toQString(it->url))) {
for (int i = 0; i < trackers.size(); ++i) {
BitTorrent::TrackerEntry &entry = trackers[i];
if (new_tracker_url == QUrl(entry.url())) {
QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL already exists."));
return;
}
if (tracker_url == QUrl(misc::toQStringU(it->url)) && !match) {
announce_entry new_entry(new_tracker_url.toString().toUtf8().constData());
new_entry.tier = it->tier;
if (tracker_url == QUrl(entry.url()) && !match) {
BitTorrent::TrackerEntry new_entry(new_tracker_url.toString());
new_entry.setTier(entry.tier());
match = true;
*it = new_entry;
emit trackersRemoved(QStringList(tracker_url.toString()), hash);
emit trackersAdded(QStringList(new_tracker_url.toString()), hash);
entry = new_entry;
}
}
h.replace_trackers(trackers);
if (!h.is_paused()) {
h.force_reannounce();
h.force_dht_announce();
torrent->replaceTrackers(trackers);
if (!torrent->isPaused()) {
torrent->forceReannounce();
torrent->forceDHTAnnounce();
}
} catch(invalid_handle&) {
return;
}
loadTrackers();
}
#if LIBTORRENT_VERSION_NUM >= 10000
void TrackerList::reannounceSelected() {
try {
QTorrentHandle h = properties->getCurrentTorrent();
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) return;
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
if (selected_items.isEmpty())
return;
if (selected_items.isEmpty()) return;
std::vector<announce_entry> trackers = h.trackers();
for (size_t i = 0; i < trackers.size(); ++i) {
QList<BitTorrent::TrackerEntry> trackers = torrent->trackers();
for (int i = 0; i < trackers.size(); ++i) {
foreach (QTreeWidgetItem* w, selected_items) {
if (w->text(COL_URL) == misc::toQString(trackers[i].url)) {
h.force_reannounce(0, i);
if (w->text(COL_URL) == trackers[i].url()) {
torrent->forceReannounce(i);
break;
}
}
}
} catch(invalid_handle&) {
return;
}
loadTrackers();
loadTrackers();
}
#endif
void TrackerList::showTrackerListMenu(QPoint) {
QTorrentHandle h = properties->getCurrentTorrent();
if (!h.is_valid()) return;
BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent();
if (!torrent) return;
//QList<QTreeWidgetItem*> selected_items = getSelectedTrackerItems();
QMenu menu;
// Add actions
QAction *addAct = menu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add a new tracker..."));
QAction *addAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("Add a new tracker..."));
QAction *copyAct = 0;
QAction *delAct = 0;
QAction *editAct = 0;
if (!getSelectedTrackerItems().isEmpty()) {
delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove tracker"));
copyAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy tracker url"));
editAct = menu.addAction(IconProvider::instance()->getIcon("edit-rename"),tr("Edit selected tracker URL"));
delAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove tracker"));
copyAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy tracker url"));
editAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"),tr("Edit selected tracker URL"));
}
#if LIBTORRENT_VERSION_NUM >= 10000
QAction *reannounceSelAct = NULL;
#endif
QAction *reannounceAct = NULL;
if (!h.is_paused()) {
if (!torrent->isPaused()) {
#if LIBTORRENT_VERSION_NUM >= 10000
reannounceSelAct = menu.addAction(IconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to selected trackers"));
reannounceSelAct = menu.addAction(GuiIconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to selected trackers"));
#endif
menu.addSeparator();
reannounceAct = menu.addAction(IconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to all trackers"));
reannounceAct = menu.addAction(GuiIconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to all trackers"));
}
QAction *act = menu.exec(QCursor::pos());
if (act == 0) return;
@@ -488,7 +468,7 @@ void TrackerList::showTrackerListMenu(QPoint) {
}
#endif
if (act == reannounceAct) {
properties->getCurrentTorrent().force_reannounce();
properties->getCurrentTorrent()->forceReannounce();
return;
}
if (act == editAct) {

View File

@@ -37,12 +37,16 @@
#include <QClipboard>
#include <libtorrent/version.hpp>
#include "qtorrenthandle.h"
#include "propertieswidget.h"
enum TrackerListColumn {COL_TIER, COL_URL, COL_STATUS, COL_PEERS, COL_MSG};
#define NB_STICKY_ITEM 3
namespace BitTorrent
{
class TorrentHandle;
}
class TrackerList: public QTreeWidget {
Q_OBJECT
Q_DISABLE_COPY(TrackerList)
@@ -60,11 +64,6 @@ public:
TrackerList(PropertiesWidget *properties);
~TrackerList();
signals:
void trackersAdded(const QStringList &trackers, const QString &hash);
void trackersRemoved(const QStringList &trackers, const QString &hash);
void trackerlessChange(bool trackerless, const QString &hash);
protected:
QList<QTreeWidgetItem*> getSelectedTrackerItems() const;
@@ -75,7 +74,7 @@ public slots:
void moveSelectionDown();
void clear();
void loadStickyItems(const QTorrentHandle &h);
void loadStickyItems(BitTorrent::TorrentHandle *const torrent);
void loadTrackers();
void askForTrackers();
void copyTrackerUrl();

View File

@@ -0,0 +1,130 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include <QStringList>
#include <QMessageBox>
#include <QFile>
#include <QUrl>
#include "core/utils/misc.h"
#include "core/utils/fs.h"
#include "core/net/downloadmanager.h"
#include "core/net/downloadhandler.h"
#include "core/bittorrent/trackerentry.h"
#include "core/bittorrent/torrenthandle.h"
#include "guiiconprovider.h"
#include "trackersadditiondlg.h"
TrackersAdditionDlg::TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent)
: QDialog(parent)
, m_torrent(torrent)
{
setupUi(this);
// Icons
uTorrentListButton->setIcon(GuiIconProvider::instance()->getIcon("download"));
}
QStringList TrackersAdditionDlg::newTrackers() const
{
return trackers_list->toPlainText().trimmed().split("\n");
}
void TrackersAdditionDlg::on_uTorrentListButton_clicked()
{
uTorrentListButton->setEnabled(false);
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(QString("http://www.torrentz.com/announce_%1").arg(m_torrent->hash()));
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(parseUTorrentList(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(getTrackerError(QString, QString)));
//Just to show that it takes times
setCursor(Qt::WaitCursor);
}
void TrackersAdditionDlg::parseUTorrentList(const QString &, const QString &path)
{
QFile list_file(path);
if (!list_file.open(QFile::ReadOnly)) {
QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok);
setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true);
Utils::Fs::forceRemove(path);
return;
}
// Load from torrent handle
QList<BitTorrent::TrackerEntry> existingTrackers = m_torrent->trackers();
// Load from current user list
QStringList tmp = trackers_list->toPlainText().split("\n");
foreach (const QString &user_url, tmp) {
BitTorrent::TrackerEntry userTracker(user_url);
if (!existingTrackers.contains(userTracker))
existingTrackers << userTracker;
}
// Add new trackers to the list
if (!trackers_list->toPlainText().isEmpty() && !trackers_list->toPlainText().endsWith("\n"))
trackers_list->insertPlainText("\n");
int nb = 0;
while (!list_file.atEnd()) {
const QByteArray line = list_file.readLine().trimmed();
if (line.isEmpty()) continue;
BitTorrent::TrackerEntry newTracker(line);
if (!existingTrackers.contains(newTracker)) {
trackers_list->insertPlainText(line + "\n");
++nb;
}
}
// Clean up
list_file.close();
Utils::Fs::forceRemove(path);
//To restore the cursor ...
setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true);
// Display information message if necessary
if (nb == 0)
QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok);
}
void TrackersAdditionDlg::getTrackerError(const QString &, const QString &error)
{
//To restore the cursor ...
setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true);
QMessageBox::warning(this, tr("Download error"), tr("The trackers list could not be downloaded, reason: %1").arg(error), QMessageBox::Ok);
}
QStringList TrackersAdditionDlg::askForTrackers(BitTorrent::TorrentHandle *const torrent)
{
QStringList trackers;
TrackersAdditionDlg dlg(torrent);
if (dlg.exec() == QDialog::Accepted)
return dlg.newTrackers();
return trackers;
}

View File

@@ -1,5 +1,5 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
@@ -32,118 +32,33 @@
#define TRACKERSADDITION_H
#include <QDialog>
#include <QStringList>
#include <QMessageBox>
#include <QFile>
#include <QUrl>
#include "iconprovider.h"
#include "misc.h"
#include "ui_trackersadditiondlg.h"
#include "downloadthread.h"
#include "qtorrenthandle.h"
#include "fs_utils.h"
class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{
Q_OBJECT
class QString;
class QStringList;
private:
QTorrentHandle h;
namespace BitTorrent
{
class TorrentHandle;
}
class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg
{
Q_OBJECT
public:
TrackersAdditionDlg(QTorrentHandle h, QWidget *parent=0): QDialog(parent), h(h) {
setupUi(this);
// Icons
uTorrentListButton->setIcon(IconProvider::instance()->getIcon("download"));
}
TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent = 0);
~TrackersAdditionDlg() {}
QStringList newTrackers() const {
return trackers_list->toPlainText().trimmed().split("\n");
}
QStringList newTrackers() const;
static QStringList askForTrackers(BitTorrent::TorrentHandle *const torrent);
public slots:
void on_uTorrentListButton_clicked() {
uTorrentListButton->setEnabled(false);
DownloadThread *d = new DownloadThread(this);
connect(d, SIGNAL(downloadFinished(QString,QString)), SLOT(parseUTorrentList(QString,QString)));
connect(d, SIGNAL(downloadFailure(QString,QString)), SLOT(getTrackerError(QString,QString)));
//Just to show that it takes times
setCursor(Qt::WaitCursor);
d->downloadUrl("http://www.torrentz.com/announce_"+h.hash());
}
void on_uTorrentListButton_clicked();
void parseUTorrentList(const QString &, const QString &path);
void getTrackerError(const QString &, const QString &error);
void parseUTorrentList(QString, QString path) {
QFile list_file(path);
if (!list_file.open(QFile::ReadOnly)) {
QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok);
setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true);
sender()->deleteLater();
fsutils::forceRemove(path);
return;
}
QList<QUrl> existingTrackers;
// Load from torrent handle
std::vector<libtorrent::announce_entry> tor_trackers = h.trackers();
std::vector<libtorrent::announce_entry>::iterator itr = tor_trackers.begin();
std::vector<libtorrent::announce_entry>::iterator itrend = tor_trackers.end();
while(itr != itrend) {
existingTrackers << QUrl(misc::toQString(itr->url));
++itr;
}
// Load from current user list
QStringList tmp = trackers_list->toPlainText().split("\n");
foreach (const QString &user_url_str, tmp) {
QUrl user_url(user_url_str);
if (!existingTrackers.contains(user_url))
existingTrackers << user_url;
}
// Add new trackers to the list
if (!trackers_list->toPlainText().isEmpty() && !trackers_list->toPlainText().endsWith("\n"))
trackers_list->insertPlainText("\n");
int nb = 0;
while (!list_file.atEnd()) {
const QByteArray line = list_file.readLine().trimmed();
if (line.isEmpty()) continue;
QUrl url(line);
if (!existingTrackers.contains(url)) {
trackers_list->insertPlainText(line + "\n");
++nb;
}
}
// Clean up
list_file.close();
fsutils::forceRemove(path);
//To restore the cursor ...
setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true);
// Display information message if necessary
if (nb == 0) {
QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok);
}
sender()->deleteLater();
}
void getTrackerError(const QString&, const QString &error) {
//To restore the cursor ...
setCursor(Qt::ArrowCursor);
uTorrentListButton->setEnabled(true);
QMessageBox::warning(this, tr("Download error"), tr("The trackers list could not be downloaded, reason: %1").arg(error), QMessageBox::Ok);
sender()->deleteLater();
}
public:
static QStringList askForTrackers(QTorrentHandle h) {
QStringList trackers;
TrackersAdditionDlg dlg(h);
if (dlg.exec() == QDialog::Accepted) {
return dlg.newTrackers();
}
return trackers;
}
private:
BitTorrent::TorrentHandle *const m_torrent;
};
#endif