mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-02 05:38:06 -06:00
Merge pull request #2892 from glassez/bittorrent
Core classes redesign (Issue #2433).
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
81
src/gui/properties/peeraddition.cpp
Normal file
81
src/gui/properties/peeraddition.cpp
Normal 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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
191
src/gui/properties/proplistdelegate.cpp
Normal file
191
src/gui/properties/proplistdelegate.cpp
Normal 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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
130
src/gui/properties/trackersadditiondlg.cpp
Normal file
130
src/gui/properties/trackersadditiondlg.cpp
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user