Fix compilation warnings in smtp.h (cgreco)

This commit is contained in:
Christophe Dumez
2011-06-05 16:09:02 +00:00
commit 43dd012b79
741 changed files with 261428 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef DOWNLOADEDPIECESBAR_H
#define DOWNLOADEDPIECESBAR_H
#include <QWidget>
#include <QPainter>
#include <QList>
#include <QPixmap>
#include <libtorrent/bitfield.hpp>
#include <math.h>
#define BAR_HEIGHT 18
class DownloadedPiecesBar: public QWidget {
Q_OBJECT
Q_DISABLE_COPY(DownloadedPiecesBar)
private:
QPixmap pixmap;
public:
DownloadedPiecesBar(QWidget *parent): QWidget(parent) {
setFixedHeight(BAR_HEIGHT);
}
void setProgress(const libtorrent::bitfield &pieces, const libtorrent::bitfield &downloading_pieces) {
if(pieces.empty()) {
// Empty bar
QPixmap pix = QPixmap(1, 1);
pix.fill();
pixmap = pix;
} else {
const qulonglong nb_pieces = pieces.size();
// Reduce the number of pieces before creating the pixmap
// otherwise it can crash when there are too many pieces
const uint w = width();
if(nb_pieces > w) {
const uint ratio = floor(nb_pieces/(double)w);
libtorrent::bitfield scaled_pieces(ceil(nb_pieces/(double)ratio), false);
libtorrent::bitfield scaled_downloading(ceil(nb_pieces/(double)ratio), false);
uint scaled_index = 0;
for(qulonglong i=0; i<nb_pieces; i+= ratio) {
bool have = true;
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) {
if(!pieces[i]) { have = false; break; }
}
if(have) {
scaled_pieces.set_bit(scaled_index);
} else {
bool downloading = false;
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) {
if(downloading_pieces[i]) { downloading = true; break; }
}
if(downloading)
scaled_downloading.set_bit(scaled_index);
}
++scaled_index;
}
updatePixmap(scaled_pieces, scaled_downloading);
} else {
updatePixmap(pieces, downloading_pieces);
}
}
update();
}
void clear() {
pixmap = QPixmap();
update();
}
protected:
void paintEvent(QPaintEvent *) {
if(pixmap.isNull()) return;
QPainter painter(this);
painter.drawPixmap(rect(), pixmap);
}
private:
void updatePixmap(const libtorrent::bitfield &pieces, const libtorrent::bitfield &downloading_pieces) {
QPixmap pix = QPixmap(pieces.size(), 1);
//pix.fill();
QPainter painter(&pix);
for(uint i=0; i<pieces.size(); ++i) {
if(pieces[i]) {
painter.setPen(Qt::blue);
} else {
if(downloading_pieces[i]) {
painter.setPen(Qt::green);
} else {
painter.setPen(Qt::white);
}
}
painter.drawPoint(i,0);
}
pixmap = pix;
}
};
#endif // DOWNLOADEDPIECESBAR_H

109
src/properties/peer.ui Normal file
View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>addPeerDialog</class>
<widget class="QDialog" name="addPeerDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>112</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Peer addition</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>IP</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineIP"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Port</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinPort">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<number>1000</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>6881</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,103 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef PEERADDITION_H
#define PEERADDITION_H
#include <QDialog>
#include <QRegExp>
#include <QMessageBox>
#include <QHostAddress>
#include "ui_peer.h"
#include <libtorrent/session.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION < 103500
#include <libtorrent/asio/ip/tcp.hpp>
#else
#include <boost/asio/ip/tcp.hpp>
#endif
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(){}
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;
}
protected slots:
void validateInput() {
if(getIP().isEmpty()) {
QMessageBox::warning(this, tr("Invalid IP"),
tr("The IP you provided is invalid."),
QMessageBox::Ok);
} else {
accept();
}
}
};
#endif // PEERADDITION_H

View File

@@ -0,0 +1,86 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef PEERLISTDELEGATE_H
#define PEERLISTDELEGATE_H
#include <QItemDelegate>
#include <QPainter>
#include "misc.h"
class PeerListDelegate: public QItemDelegate {
Q_OBJECT
public:
enum PeerListColumns {IP, CONNECTION, CLIENT, PROGRESS, DOWN_SPEED, UP_SPEED,
TOT_DOWN, TOT_UP, IP_HIDDEN, COL_COUNT};
public:
PeerListDelegate(QObject *parent) : QItemDelegate(parent){}
~PeerListDelegate(){}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const{
painter->save();
QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option);
switch(index.column()){
case TOT_DOWN:
case TOT_UP:
QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, option.rect, 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)"));
break;
}
case PROGRESS:{
QItemDelegate::drawBackground(painter, opt, index);
qreal progress = index.data().toDouble();
QItemDelegate::drawDisplay(painter, opt, opt.rect, QString::number(progress*100., 'f', 1)+"%");
break;
}
default:
QItemDelegate::paint(painter, option, index);
}
painter->restore();
}
QWidget* createEditor(QWidget*, const QStyleOptionViewItem &, const QModelIndex &) const {
// No editor here
return 0;
}
};
#endif // PEERLISTDELEGATE_H

View File

@@ -0,0 +1,426 @@
/*
* Bittorrent Client using Qt4 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 "peerlistwidget.h"
#include "peerlistdelegate.h"
#include "reverseresolution.h"
#include "preferences.h"
#include "propertieswidget.h"
#include "geoipmanager.h"
#include "peeraddition.h"
#include "speedlimitdlg.h"
#include "iconprovider.h"
#include <QStandardItemModel>
#include <QSortFilterProxyModel>
#include <QSet>
#include <QHeaderView>
#include <QMenu>
#include <QClipboard>
#include <vector>
#include "qinisettings.h"
using namespace libtorrent;
PeerListWidget::PeerListWidget(PropertiesWidget *parent): QTreeView(parent), properties(parent), display_flags(false) {
// Load settings
loadSettings();
// Visual settings
setRootIsDecorated(false);
setItemsExpandable(false);
setAllColumnsShowFocus(true);
setSelectionMode(QAbstractItemView::ExtendedSelection);
// List Model
listModel = new QStandardItemModel(0, PeerListDelegate::COL_COUNT);
listModel->setHeaderData(PeerListDelegate::IP, Qt::Horizontal, tr("IP"));
listModel->setHeaderData(PeerListDelegate::CONNECTION, Qt::Horizontal, tr("Connection"));
listModel->setHeaderData(PeerListDelegate::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application"));
listModel->setHeaderData(PeerListDelegate::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
listModel->setHeaderData(PeerListDelegate::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed"));
listModel->setHeaderData(PeerListDelegate::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed"));
listModel->setHeaderData(PeerListDelegate::TOT_DOWN, Qt::Horizontal, tr("Downloaded", "i.e: total data downloaded"));
listModel->setHeaderData(PeerListDelegate::TOT_UP, Qt::Horizontal, tr("Uploaded", "i.e: total data uploaded"));
// Proxy model to support sorting without actually altering the underlying model
proxyModel = new QSortFilterProxyModel();
proxyModel->setDynamicSortFilter(true);
proxyModel->setSourceModel(listModel);
setModel(proxyModel);
hideColumn(PeerListDelegate::IP_HIDDEN);
// Context menu
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showPeerListMenu(QPoint)));
// List delegate
listDelegate = new PeerListDelegate(this);
setItemDelegate(listDelegate);
// Enable sorting
setSortingEnabled(true);
// IP to Hostname resolver
updatePeerHostNameResolutionState();
// SIGNAL/SLOT
connect(header(), SIGNAL(sectionClicked(int)), SLOT(handleSortColumnChanged(int)));
handleSortColumnChanged(header()->sortIndicatorSection());
}
PeerListWidget::~PeerListWidget() {
saveSettings();
delete proxyModel;
delete listModel;
delete listDelegate;
if(resolver)
delete resolver;
}
void PeerListWidget::updatePeerHostNameResolutionState() {
if(Preferences().resolvePeerHostNames()) {
if(!resolver) {
resolver = new ReverseResolution(this);
connect(resolver, SIGNAL(ip_resolved(QString,QString)), this, SLOT(handleResolved(QString,QString)));
loadPeers(properties->getCurrentTorrent(), true);
}
} else {
if(resolver) {
delete resolver;
}
}
}
void PeerListWidget::updatePeerCountryResolutionState() {
if(Preferences().resolvePeerCountries() != display_flags) {
display_flags = !display_flags;
if(display_flags) {
const QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
loadPeers(h);
}
}
}
void PeerListWidget::showPeerListMenu(QPoint) {
QMenu menu;
bool empty_menu = true;
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
QModelIndexList selectedIndexes = selectionModel()->selectedRows();
QStringList selectedPeerIPs;
foreach(const QModelIndex &index, selectedIndexes) {
int row = proxyModel->mapToSource(index).row();
QString myip = listModel->data(listModel->index(row, PeerListDelegate::IP_HIDDEN)).toString();
selectedPeerIPs << myip;
}
// 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..."));
empty_menu = false;
}
// Per Peer Speed limiting actions
QAction *upLimitAct = 0;
QAction *dlLimitAct = 0;
QAction *banAct = 0;
QAction *copyIPAct = 0;
if(!selectedPeerIPs.isEmpty()) {
copyIPAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy IP"));
menu.addSeparator();
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();
banAct = menu.addAction(IconProvider::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) {
libtorrent::asio::ip::tcp::endpoint ep = PeerAdditionDlg::askForPeerEndpoint();
if(ep != libtorrent::asio::ip::tcp::endpoint()) {
try {
h.connect_peer(ep);
QMessageBox::information(0, tr("Peer addition"), tr("The peer was added to this torrent."));
} catch(std::exception) {
QMessageBox::critical(0, tr("Peer addition"), tr("The peer could not be added to this torrent."));
}
} else {
qDebug("No peer was added");
}
return;
}
if(act == upLimitAct) {
limitUpRateSelectedPeers(selectedPeerIPs);
return;
}
if(act == dlLimitAct) {
limitDlRateSelectedPeers(selectedPeerIPs);
return;
}
if(act == banAct) {
banSelectedPeers(selectedPeerIPs);
return;
}
if(act == copyIPAct) {
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
QApplication::clipboard()->setText(selectedPeerIPs.join("\r\n"));
#else
QApplication::clipboard()->setText(selectedPeerIPs.join("\n"));
#endif
}
}
void PeerListWidget::banSelectedPeers(QStringList peer_ips) {
// Confirm first
int ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to ban permanently the selected peers?"),
tr("&Yes"), tr("&No"),
QString(), 0, 1);
if(ret) return;
foreach(const QString &ip, peer_ips) {
qDebug("Banning peer %s...", ip.toLocal8Bit().data());
QBtSession::instance()->addConsoleMessage(tr("Manually banning peer %1...").arg(ip));
QBtSession::instance()->banIP(ip);
}
// Refresh list
loadPeers(properties->getCurrentTorrent());
}
void PeerListWidget::limitUpRateSelectedPeers(QStringList peer_ips) {
if(peer_ips.empty()) return;
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
bool ok=false;
int cur_limit = -1;
#if LIBTORRENT_VERSION_MINOR > 15
libtorrent::asio::ip::tcp::endpoint first_ep = peerEndpoints.value(peer_ips.first(),
libtorrent::asio::ip::tcp::endpoint());
if(first_ep != libtorrent::asio::ip::tcp::endpoint())
cur_limit = h.get_peer_upload_limit(first_ep);
#endif
long limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Upload rate limiting"), cur_limit, Preferences().getGlobalUploadLimit()*1024.);
if(!ok) return;
foreach(const QString &ip, peer_ips) {
libtorrent::asio::ip::tcp::endpoint ep = peerEndpoints.value(ip, libtorrent::asio::ip::tcp::endpoint());
if(ep != libtorrent::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(QStringList peer_ips) {
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
bool ok=false;
int cur_limit = -1;
#if LIBTORRENT_VERSION_MINOR > 15
libtorrent::asio::ip::tcp::endpoint first_ep = peerEndpoints.value(peer_ips.first(),
libtorrent::asio::ip::tcp::endpoint());
if(first_ep != libtorrent::asio::ip::tcp::endpoint())
cur_limit = h.get_peer_download_limit(first_ep);
#endif
long limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Download rate limiting"), cur_limit, Preferences().getGlobalDownloadLimit()*1024.);
if(!ok) return;
foreach(const QString &ip, peer_ips) {
libtorrent::asio::ip::tcp::endpoint ep = peerEndpoints.value(ip, libtorrent::asio::ip::tcp::endpoint());
if(ep != libtorrent::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...");
}
}
}
void PeerListWidget::clear() {
qDebug("clearing peer list");
peerItems.clear();
peerEndpoints.clear();
missingFlags.clear();
int nbrows = listModel->rowCount();
if(nbrows > 0) {
qDebug("Cleared %d peers", nbrows);
listModel->removeRows(0, nbrows);
}
}
void PeerListWidget::loadSettings() {
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
header()->restoreState(settings.value("TorrentProperties/Peers/PeerListState").toByteArray());
}
void PeerListWidget::saveSettings() const {
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
settings.setValue("TorrentProperties/Peers/PeerListState", header()->saveState());
}
void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_resolution) {
if(!h.is_valid()) return;
boost::system::error_code ec;
std::vector<peer_info> peers;
h.get_peer_info(peers);
std::vector<peer_info>::iterator itr;
QSet<QString> old_peers_set = peerItems.keys().toSet();
for(itr = peers.begin(); itr != peers.end(); itr++) {
peer_info peer = *itr;
QString peer_ip = misc::toQString(peer.ip.address().to_string(ec));
if(ec) continue;
if(peerItems.contains(peer_ip)) {
// Update existing peer
updatePeer(peer_ip, peer);
old_peers_set.remove(peer_ip);
if(force_hostname_resolution) {
if(resolver) {
const QString host = resolver->getHostFromCache(peer.ip);
if(host.isNull()) {
resolver->resolve(peer.ip);
} else {
qDebug("Got peer IP from cache");
handleResolved(peer_ip, host);
}
}
}
} else {
// Add new peer
peerItems[peer_ip] = addPeer(peer_ip, peer);
peerEndpoints[peer_ip] = peer.ip;
}
}
// Delete peers that are gone
QSetIterator<QString> it(old_peers_set);
while(it.hasNext()) {
QString ip = it.next();
missingFlags.remove(ip);
peerEndpoints.remove(ip);
QStandardItem *item = peerItems.take(ip);
listModel->removeRow(item->row());
}
}
QStandardItem* PeerListWidget::addPeer(QString ip, peer_info peer) {
int row = listModel->rowCount();
// Adding Peer to peer list
listModel->insertRow(row);
QString host;
if(resolver) {
host = resolver->getHostFromCache(peer.ip);
}
if(host.isNull())
listModel->setData(listModel->index(row, PeerListDelegate::IP), ip);
else
listModel->setData(listModel->index(row, PeerListDelegate::IP), host);
listModel->setData(listModel->index(row, PeerListDelegate::IP_HIDDEN), ip);
// Resolve peer host name is asked
if(resolver && host.isNull())
resolver->resolve(peer.ip);
if(display_flags) {
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country);
if(!ico.isNull()) {
listModel->setData(listModel->index(row, PeerListDelegate::IP), ico, Qt::DecorationRole);
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country);
listModel->setData(listModel->index(row, PeerListDelegate::IP), country_name, Qt::ToolTipRole);
} else {
missingFlags.insert(ip);
}
}
listModel->setData(listModel->index(row, PeerListDelegate::CONNECTION), getConnectionString(peer.connection_type));
listModel->setData(listModel->index(row, PeerListDelegate::CLIENT), misc::toQStringU(peer.client));
listModel->setData(listModel->index(row, PeerListDelegate::PROGRESS), peer.progress);
listModel->setData(listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed);
listModel->setData(listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed);
listModel->setData(listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download);
listModel->setData(listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload);
return listModel->item(row, PeerListDelegate::IP);
}
void PeerListWidget::updatePeer(QString ip, peer_info peer) {
QStandardItem *item = peerItems.value(ip);
int row = item->row();
if(display_flags) {
const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country);
if(!ico.isNull()) {
listModel->setData(listModel->index(row, PeerListDelegate::IP), ico, Qt::DecorationRole);
const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country);
listModel->setData(listModel->index(row, PeerListDelegate::IP), country_name, Qt::ToolTipRole);
missingFlags.remove(ip);
}
}
listModel->setData(listModel->index(row, PeerListDelegate::CONNECTION), getConnectionString(peer.connection_type));
listModel->setData(listModel->index(row, PeerListDelegate::CLIENT), misc::toQStringU(peer.client));
listModel->setData(listModel->index(row, PeerListDelegate::PROGRESS), peer.progress);
listModel->setData(listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed);
listModel->setData(listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed);
listModel->setData(listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download);
listModel->setData(listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload);
}
void PeerListWidget::handleResolved(const QString &ip, const QString &hostname) {
QStandardItem *item = peerItems.value(ip, 0);
if(item) {
qDebug("Resolved %s -> %s", qPrintable(ip), qPrintable(hostname));
item->setData(hostname, Qt::DisplayRole);
//listModel->setData(listModel->index(item->row(), IP), hostname, Qt::DisplayRole);
}
}
void PeerListWidget::handleSortColumnChanged(int col)
{
if(col == 0) {
qDebug("Sorting by decoration");
proxyModel->setSortRole(Qt::ToolTipRole);
} else {
proxyModel->setSortRole(Qt::DisplayRole);
}
}
QString PeerListWidget::getConnectionString(int connection_type)
{
QString connection;
switch(connection_type) {
#if LIBTORRENT_VERSION_MINOR > 15
case peer_info::bittorrent_utp:
connection = "uTP";
break;
case peer_info::http_seed:
#endif
case peer_info::web_seed:
connection = "Web";
break;
default:
connection = "BT";
break;
}
return connection;
}

View File

@@ -0,0 +1,100 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef PEERLISTWIDGET_H
#define PEERLISTWIDGET_H
#include <QTreeView>
#include <QHash>
#include <QPointer>
#include <QSet>
#include <libtorrent/peer_info.hpp>
#include "qtorrenthandle.h"
#include "misc.h"
class PeerListDelegate;
class ReverseResolution;
class PropertiesWidget;
QT_BEGIN_NAMESPACE
class QSortFilterProxyModel;
class QStandardItem;
class QStandardItemModel;
QT_END_NAMESPACE
#include <boost/version.hpp>
#if BOOST_VERSION < 103500
#include <libtorrent/asio/ip/tcp.hpp>
#else
#include <boost/asio/ip/tcp.hpp>
#endif
class PeerListWidget : public QTreeView {
Q_OBJECT
Q_DISABLE_COPY(PeerListWidget)
public:
PeerListWidget(PropertiesWidget *parent);
~PeerListWidget();
public slots:
void loadPeers(const QTorrentHandle &h, bool force_hostname_resolution=false);
QStandardItem* addPeer(QString ip, libtorrent::peer_info peer);
void updatePeer(QString ip, libtorrent::peer_info peer);
void handleResolved(const QString &ip, const QString &hostname);
void updatePeerHostNameResolutionState();
void updatePeerCountryResolutionState();
void clear();
protected slots:
void loadSettings();
void saveSettings() const;
void showPeerListMenu(QPoint);
void limitUpRateSelectedPeers(QStringList peer_ips);
void limitDlRateSelectedPeers(QStringList peer_ips);
void banSelectedPeers(QStringList peer_ips);
void handleSortColumnChanged(int col);
private:
static QString getConnectionString(int connection_type);
private:
QStandardItemModel *listModel;
PeerListDelegate *listDelegate;
QSortFilterProxyModel * proxyModel;
QHash<QString, QStandardItem*> peerItems;
QHash<QString, libtorrent::asio::ip::tcp::endpoint> peerEndpoints;
QSet<QString> missingFlags;
QPointer<ReverseResolution> resolver;
PropertiesWidget* properties;
bool display_flags;
};
#endif // PEERLISTWIDGET_H

View File

@@ -0,0 +1,117 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef PIECEAVAILABILITYBAR_H
#define PIECEAVAILABILITYBAR_H
#include <QWidget>
#include <QPainter>
#include <QPixmap>
#include <QColor>
#include <numeric>
#include <cmath>
#include <algorithm>
#define BAR_HEIGHT 18
class PieceAvailabilityBar: public QWidget {
Q_OBJECT
Q_DISABLE_COPY(PieceAvailabilityBar)
private:
QPixmap pixmap;
public:
PieceAvailabilityBar(QWidget *parent): QWidget(parent) {
setFixedHeight(BAR_HEIGHT);
}
void setAvailability(const std::vector<int>& avail) {
if(avail.empty()) {
// Empty bar
QPixmap pix = QPixmap(1, 1);
pix.fill();
pixmap = pix;
} else {
// Reduce the number of pieces before creating the pixmap
// otherwise it can crash when there are too many pieces
const qulonglong nb_pieces = avail.size();
const uint w = width();
if(nb_pieces > w) {
const qulonglong ratio = floor(nb_pieces/(double)w);
std::vector<int> scaled_avail;
scaled_avail.reserve(ceil(nb_pieces/(double)ratio));
for(qulonglong i=0; i<nb_pieces; i+= ratio) {
// XXX: Do not compute the average to save cpu
scaled_avail.push_back(avail[i]);
}
updatePixmap(scaled_avail);
} else {
updatePixmap(avail);
}
}
update();
}
void clear() {
pixmap = QPixmap();
update();
}
protected:
void paintEvent(QPaintEvent *) {
if(pixmap.isNull()) return;
QPainter painter(this);
painter.drawPixmap(rect(), pixmap);
}
private:
void updatePixmap(const std::vector<int> avail) {
const int max = *std::max_element(avail.begin(), avail.end());
if(max == 0) {
QPixmap pix = QPixmap(1, 1);
pix.fill();
pixmap = pix;
return;
}
QPixmap pix = QPixmap(avail.size(), 1);
//pix.fill();
QPainter painter(&pix);
for(uint i=0; i < avail.size(); ++i) {
const uint rg = 0xff - (0xff * avail[i]/max);
painter.setPen(QColor(rg, rg, 0xff));
painter.drawPoint(i,0);
}
pixmap = pix;
}
};
#endif // PIECEAVAILABILITYBAR_H

View File

@@ -0,0 +1,22 @@
INCLUDEPATH += $$PWD
FORMS += $$PWD/propertieswidget.ui \
$$PWD/trackersadditiondlg.ui \
$$PWD/peer.ui
HEADERS += $$PWD/propertieswidget.h \
$$PWD/peerlistwidget.h \
$$PWD/proplistdelegate.h \
$$PWD/trackerlist.h \
$$PWD/downloadedpiecesbar.h \
$$PWD/peerlistdelegate.h \
$$PWD/peeraddition.h \
$$PWD/trackersadditiondlg.h \
$$PWD/pieceavailabilitybar.h \
$$PWD/proptabbar.h
SOURCES += $$PWD/propertieswidget.cpp \
$$PWD/peerlistwidget.cpp \
$$PWD/trackerlist.cpp \
$$PWD/proptabbar.cpp

View File

@@ -0,0 +1,731 @@
/*
* Bittorrent Client using Qt4 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 <QTimer>
#include <QListWidgetItem>
#include <QVBoxLayout>
#include <QStackedWidget>
#include <QSplitter>
#include <QHeaderView>
#include <QAction>
#include <QMessageBox>
#include <QMenu>
#include <QFileDialog>
#include <QDesktopServices>
#include <QInputDialog>
#include <libtorrent/version.hpp>
#include "propertieswidget.h"
#include "transferlistwidget.h"
#include "torrentpersistentdata.h"
#include "qbtsession.h"
#include "proplistdelegate.h"
#include "torrentfilesmodel.h"
#include "peerlistwidget.h"
#include "trackerlist.h"
#include "mainwindow.h"
#include "downloadedpiecesbar.h"
#include "pieceavailabilitybar.h"
#include "qinisettings.h"
#include "proptabbar.h"
#include "iconprovider.h"
#include "lineedit.h"
using namespace libtorrent;
PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList):
QWidget(parent), transferList(transferList), main_window(main_window) {
setupUi(this);
// Icons
deleteWS_button->setIcon(IconProvider::instance()->getIcon("list-remove"));
addWS_button->setIcon(IconProvider::instance()->getIcon("list-add"));
trackerUpButton->setIcon(IconProvider::instance()->getIcon("go-up"));
trackerDownButton->setIcon(IconProvider::instance()->getIcon("go-down"));
state = VISIBLE;
setEnabled(false);
// Set Properties list model
PropListModel = new TorrentFilesFilterModel();
filesList->setModel(PropListModel);
PropDelegate = new PropListDelegate(this);
filesList->setItemDelegate(PropDelegate);
filesList->setSortingEnabled(true);
// Torrent content filtering
m_contentFilerLine = new LineEdit(this);
connect(m_contentFilerLine, SIGNAL(textChanged(QString)), PropListModel, SLOT(setFilterFixedString(QString)));
contentFilterLayout->insertWidget(1, m_contentFilerLine);
// SIGNAL/SLOTS
connect(filesList, SIGNAL(clicked(const QModelIndex&)), filesList, SLOT(edit(const QModelIndex&)));
connect(selectAllButton, SIGNAL(clicked()), PropListModel, SLOT(selectAll()));
connect(selectNoneButton, SIGNAL(clicked()), PropListModel, SLOT(selectNone()));
connect(filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&)));
connect(filesList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(openDoubleClickedFile(QModelIndex)));
connect(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
connect(addWS_button, SIGNAL(clicked()), this, SLOT(askWebSeed()));
connect(deleteWS_button, SIGNAL(clicked()), this, SLOT(deleteSelectedUrlSeeds()));
connect(transferList, SIGNAL(currentTorrentChanged(QTorrentHandle)), this, SLOT(loadTorrentInfos(QTorrentHandle)));
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)));
// Downloaded pieces progress bar
downloaded_pieces = new DownloadedPiecesBar(this);
ProgressHLayout->insertWidget(1, downloaded_pieces);
// Pieces availability bar
pieces_availability = new PieceAvailabilityBar(this);
ProgressHLayout_2->insertWidget(1, pieces_availability);
// Tracker list
trackerList = new TrackerList(this);
#if LIBTORRENT_VERSION_MINOR > 14
trackerUpButton->setVisible(false);
trackerDownButton->setVisible(false);
#else
connect(trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp()));
connect(trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown()));
#endif
horizontalLayout_trackers->insertWidget(0, trackerList);
// Peers list
peersList = new PeerListWidget(this);
peerpage_layout->addWidget(peersList);
// Tab bar
m_tabBar = new PropTabBar();
verticalLayout->addLayout(m_tabBar);
connect(m_tabBar, SIGNAL(tabChanged(int)), stackedProperties, SLOT(setCurrentIndex(int)));
connect(m_tabBar, SIGNAL(visibilityToggled(bool)), SLOT(setVisibility(bool)));
// Dynamic data refresher
refreshTimer = new QTimer(this);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
refreshTimer->start(3000); // 3sec
}
PropertiesWidget::~PropertiesWidget() {
qDebug() << Q_FUNC_INFO << "ENTER";
delete refreshTimer;
delete trackerList;
delete peersList;
delete downloaded_pieces;
delete pieces_availability;
delete PropListModel;
delete PropDelegate;
delete m_tabBar;
qDebug() << Q_FUNC_INFO << "EXIT";
}
void PropertiesWidget::showPiecesAvailability(bool show) {
avail_pieces_lbl->setVisible(show);
pieces_availability->setVisible(show);
avail_average_lbl->setVisible(show);
if(show || (!show && !downloaded_pieces->isVisible()))
line_2->setVisible(show);
}
void PropertiesWidget::showPiecesDownloaded(bool show) {
downloaded_pieces_lbl->setVisible(show);
downloaded_pieces->setVisible(show);
progress_lbl->setVisible(show);
if(show || (!show && !pieces_availability->isVisible()))
line_2->setVisible(show);
}
void PropertiesWidget::setVisibility(bool visible) {
if(!visible && state == VISIBLE) {
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
stackedProperties->setVisible(false);
slideSizes = hSplitter->sizes();
hSplitter->handle(1)->setVisible(false);
hSplitter->handle(1)->setDisabled(true);
QList<int> sizes = QList<int>() << hSplitter->geometry().height()-30 << 30;
hSplitter->setSizes(sizes);
state = REDUCED;
return;
}
if(visible && state == REDUCED) {
stackedProperties->setVisible(true);
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
hSplitter->handle(1)->setDisabled(false);
hSplitter->handle(1)->setVisible(true);
hSplitter->setSizes(slideSizes);
state = VISIBLE;
// Force refresh
loadDynamicData();
}
}
void PropertiesWidget::clear() {
qDebug("Clearing torrent properties");
save_path->clear();
lbl_creationDate->clear();
pieceSize_lbl->clear();
hash_lbl->clear();
comment_text->clear();
progress_lbl->clear();
trackerList->clear();
downloaded_pieces->clear();
pieces_availability->clear();
avail_average_lbl->clear();
wasted->clear();
upTotal->clear();
dlTotal->clear();
peersList->clear();
lbl_uplimit->clear();
lbl_dllimit->clear();
lbl_elapsed->clear();
lbl_connections->clear();
reannounce_lbl->clear();
shareRatio->clear();
listWebSeeds->clear();
m_contentFilerLine->clear();
PropListModel->model()->clear();
showPiecesAvailability(false);
showPiecesDownloaded(false);
setEnabled(false);
}
QTorrentHandle PropertiesWidget::getCurrentTorrent() const {
return h;
}
void PropertiesWidget::updateSavePath(const QTorrentHandle& _h) {
if(h.is_valid() && h == _h) {
QString p;
if(h.has_metadata() && h.num_files() == 1) {
p = h.firstFileSavePath();
} else {
p = TorrentPersistentData::getSavePath(h.hash());
if(p.isEmpty())
p = h.save_path();
}
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
p = p.replace("/", "\\");
#endif
save_path->setText(p);
}
}
void PropertiesWidget::updateTorrentInfos(const QTorrentHandle& _h) {
if(h.is_valid() && h == _h) {
loadTorrentInfos(h);
}
}
void PropertiesWidget::loadTorrentInfos(const QTorrentHandle &_h) {
clear();
h = _h;
if(!h.is_valid()) {
clear();
return;
}
setEnabled(true);
try {
// Save path
updateSavePath(h);
changeSavePathButton->setEnabled(h.has_metadata());
// Hash
hash_lbl->setText(h.hash());
PropListModel->model()->clear();
if(h.has_metadata()) {
// Creation date
lbl_creationDate->setText(h.creation_date());
// Pieces size
pieceSize_lbl->setText(misc::friendlyUnit(h.piece_length()));
// Comment
comment_text->setHtml(misc::parseHtmlLinks(h.comment()));
// URL seeds
loadUrlSeeds();
// List files in torrent
PropListModel->model()->setupModelData(h.get_torrent_info());
}
} catch(invalid_handle& e) {
}
// Load dynamic data
loadDynamicData();
}
void PropertiesWidget::readSettings() {
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
// Restore splitter sizes
QStringList sizes_str = settings.value(QString::fromUtf8("TorrentProperties/SplitterSizes"), QString()).toString().split(",");
if(sizes_str.size() == 2) {
slideSizes << sizes_str.first().toInt();
slideSizes << sizes_str.last().toInt();
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
hSplitter->setSizes(slideSizes);
}
if (!filesList->header()->restoreState(settings.value("TorrentProperties/FilesListState").toByteArray())) {
filesList->header()->resizeSection(0, 400); //Default
}
const int current_tab = settings.value("TorrentProperties/CurrentTab", -1).toInt();
m_tabBar->setCurrentIndex(current_tab);
if(!settings.value("TorrentProperties/Visible", false).toBool()) {
setVisibility(false);
}
}
void PropertiesWidget::saveSettings() {
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
settings.setValue("TorrentProperties/Visible", state==VISIBLE);
// Splitter sizes
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
QList<int> sizes;
if(state == VISIBLE)
sizes = hSplitter->sizes();
else
sizes = slideSizes;
qDebug("Sizes: %d", sizes.size());
if(sizes.size() == 2) {
settings.setValue(QString::fromUtf8("TorrentProperties/SplitterSizes"), QVariant(QString::number(sizes.first())+','+QString::number(sizes.last())));
}
settings.setValue("TorrentProperties/FilesListState", filesList->header()->saveState());
// Remember current tab
settings.setValue("TorrentProperties/CurrentTab", m_tabBar->currentIndex());
}
void PropertiesWidget::reloadPreferences() {
// Take program preferences into consideration
peersList->updatePeerHostNameResolutionState();
peersList->updatePeerCountryResolutionState();
}
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 {
// Transfer infos
if(stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) {
wasted->setText(misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes()));
upTotal->setText(misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")");
dlTotal->setText(misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")");
if(h.upload_limit() <= 0)
lbl_uplimit->setText(QString::fromUtf8(""));
else
lbl_uplimit->setText(misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)"));
if(h.download_limit() <= 0)
lbl_dllimit->setText(QString::fromUtf8(""));
else
lbl_dllimit->setText(misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)"));
QString elapsed_txt = misc::userFriendlyDuration(h.active_time());
if(h.is_seed()) {
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")";
}
lbl_elapsed->setText(elapsed_txt);
if(h.connections_limit() > 0)
lbl_connections->setText(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")");
else
lbl_connections->setText(QString::number(h.num_connections()));
// Update next announce time
reannounce_lbl->setText(h.next_announce());
// Update ratio info
const qreal ratio = QBtSession::instance()->getRealRatio(h.hash());
if(ratio > QBtSession::MAX_RATIO)
shareRatio->setText(QString::fromUtf8(""));
else
shareRatio->setText(QString(QByteArray::number(ratio, 'f', 2)));
if(!h.is_seed()) {
showPiecesDownloaded(true);
// Downloaded pieces
bitfield bf(h.get_torrent_info().num_pieces(), 0);
h.downloading_pieces(bf);
downloaded_pieces->setProgress(h.pieces(), bf);
// Pieces availability
if(h.has_metadata() && !h.is_paused() && !h.is_queued() && !h.is_checking()) {
showPiecesAvailability(true);
std::vector<int> avail;
h.piece_availability(avail);
pieces_availability->setAvailability(avail);
avail_average_lbl->setText(QString::number(h.distributed_copies(), 'f', 3));
} else {
showPiecesAvailability(false);
}
// Progress
qreal progress = h.progress()*100.;
if(progress > 99.94 && progress < 100.)
progress = 99.9;
progress_lbl->setText(QString::number(progress, 'f', 1)+"%");
} else {
showPiecesAvailability(false);
showPiecesDownloaded(false);
}
return;
}
if(stackedProperties->currentIndex() == PropTabBar::TRACKERS_TAB) {
// Trackers
trackerList->loadTrackers();
return;
}
if(stackedProperties->currentIndex() == PropTabBar::PEERS_TAB) {
// Load peers
peersList->loadPeers(h);
return;
}
if(stackedProperties->currentIndex() == PropTabBar::FILES_TAB) {
// Files progress
if(h.is_valid() && h.has_metadata()) {
qDebug("Updating priorities in files tab");
filesList->setUpdatesEnabled(false);
std::vector<size_type> fp;
h.file_progress(fp);
PropListModel->model()->updateFilesPriorities(h.file_priorities());
PropListModel->model()->updateFilesProgress(fp);
filesList->setUpdatesEnabled(true);
}
}
} catch(invalid_handle e) {}
}
void PropertiesWidget::loadUrlSeeds(){
listWebSeeds->clear();
qDebug("Loading URL seeds");
const QStringList hc_seeds = h.url_seeds();
// Add url seeds
foreach(const QString &hc_seed, hc_seeds){
qDebug("Loading URL seed: %s", qPrintable(hc_seed));
new QListWidgetItem(hc_seed, listWebSeeds);
}
}
void PropertiesWidget::openDoubleClickedFile(QModelIndex index) {
if(!index.isValid()) return;
if(!h.is_valid() || !h.has_metadata()) return;
if(PropListModel->getType(index) == TorrentFileItem::TFILE) {
int i = PropListModel->getFileIndex(index);
const QDir saveDir(h.save_path());
const QString filename = h.filepath_at(i);
const QString file_path = QDir::cleanPath(saveDir.absoluteFilePath(filename));
qDebug("Trying to open file at %s", qPrintable(file_path));
#if LIBTORRENT_VERSION_MINOR > 14
// Flush data
h.flush_cache();
#endif
if(QFile::exists(file_path)) {
QDesktopServices::openUrl(QUrl::fromLocalFile(file_path));
} else {
QMessageBox::warning(this, tr("I/O Error"), tr("This file does not exist yet."));
}
} else {
// FOLDER
QStringList path_items;
path_items << index.data().toString();
QModelIndex parent = PropListModel->parent(index);
while(parent.isValid()) {
path_items.prepend(parent.data().toString());
parent = PropListModel->parent(parent);
}
const QDir saveDir(h.save_path());
const QString filename = path_items.join(QDir::separator());
const QString file_path = QDir::cleanPath(saveDir.absoluteFilePath(filename));
qDebug("Trying to open folder at %s", qPrintable(file_path));
#if LIBTORRENT_VERSION_MINOR > 14
// Flush data
h.flush_cache();
#endif
if(QFile::exists(file_path)) {
QDesktopServices::openUrl(QUrl::fromLocalFile(file_path));
} else {
QMessageBox::warning(this, tr("I/O Error"), tr("This folder does not exist yet."));
}
}
}
void PropertiesWidget::displayFilesListMenu(const QPoint&){
QMenu myFilesLlistMenu;
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0);
QAction *actRename = 0;
if(selectedRows.size() == 1) {
actRename = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
myFilesLlistMenu.addSeparator();
}
QMenu subMenu;
#if LIBTORRENT_VERSION_MINOR > 15
if(!h.status(0x0).is_seeding) {
#else
if(!static_cast<torrent_handle>(h).is_seed()) {
#endif
subMenu.setTitle(tr("Priority"));
subMenu.addAction(actionNot_downloaded);
subMenu.addAction(actionNormal);
subMenu.addAction(actionHigh);
subMenu.addAction(actionMaximum);
myFilesLlistMenu.addMenu(&subMenu);
}
// Call menu
const QAction *act = myFilesLlistMenu.exec(QCursor::pos());
if(act) {
if(act == actRename) {
renameSelectedFile();
} else {
int prio = 1;
if(act == actionHigh) {
prio = prio::HIGH;
} else {
if(act == actionMaximum) {
prio = prio::MAXIMUM;
} else {
if(act == actionNot_downloaded) {
prio = prio::IGNORED;
}
}
}
qDebug("Setting files priority");
foreach(QModelIndex index, selectedRows) {
qDebug("Setting priority(%d) for file at row %d", prio, index.row());
PropListModel->setData(PropListModel->index(index.row(), PRIORITY, index.parent()), prio);
}
// Save changes
filteredFilesChanged();
}
}
}
void PropertiesWidget::renameSelectedFile() {
const QModelIndexList selectedIndexes = filesList->selectionModel()->selectedRows(0);
Q_ASSERT(selectedIndexes.size() == 1);
const QModelIndex index = selectedIndexes.first();
// Ask for new name
bool ok;
QString new_name_last = QInputDialog::getText(this, tr("Rename the file"),
tr("New name:"), QLineEdit::Normal,
index.data().toString(), &ok);
if (ok && !new_name_last.isEmpty()) {
if(!misc::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);
return;
}
if(PropListModel->getType(index) == TorrentFileItem::TFILE) {
// 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);
old_name = old_name.replace("\\", "/");
if(old_name.endsWith(".!qB") && !new_name_last.endsWith(".!qB")) {
new_name_last += ".!qB";
}
QStringList path_items = old_name.split("/");
path_items.removeLast();
path_items << new_name_last;
QString new_name = path_items.join("/");
if(old_name == new_name) {
qDebug("Name did not change");
return;
}
new_name = QDir::cleanPath(new_name);
// Check if that name is already used
for(int i=0; i<h.num_files(); ++i) {
if(i == file_index) continue;
#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
if(h.filepath_at(i).compare(new_name, Qt::CaseSensitive) == 0) {
#else
if(h.filepath_at(i).compare(new_name, Qt::CaseInsensitive) == 0) {
#endif
// Display error message
QMessageBox::warning(this, tr("The file could not be renamed"),
tr("This name is already in use in this folder. Please use a different name."),
QMessageBox::Ok);
return;
}
}
const bool force_recheck = QFile::exists(h.save_path()+QDir::separator()+new_name);
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name));
h.rename_file(file_index, new_name);
// Force recheck
if(force_recheck) h.force_recheck();
// Rename if torrent files model too
if(new_name_last.endsWith(".!qB"))
new_name_last.chop(4);
PropListModel->setData(index, new_name_last);
} else {
// Folder renaming
QStringList path_items;
path_items << index.data().toString();
QModelIndex parent = PropListModel->parent(index);
while(parent.isValid()) {
path_items.prepend(parent.data().toString());
parent = PropListModel->parent(parent);
}
const QString old_path = path_items.join("/");
path_items.removeLast();
path_items << new_name_last;
QString new_path = path_items.join("/");
if(!new_path.endsWith("/")) new_path += "/";
// Check for overwriting
const int num_files = h.num_files();
for(int i=0; i<num_files; ++i) {
const QString current_name = h.filepath_at(i);
#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
if(current_name.startsWith(new_path, Qt::CaseSensitive)) {
#else
if(current_name.startsWith(new_path, Qt::CaseInsensitive)) {
#endif
QMessageBox::warning(this, tr("The folder could not be renamed"),
tr("This name is already in use in this folder. Please use a different name."),
QMessageBox::Ok);
return;
}
}
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);
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))
force_recheck = true;
new_name = QDir::cleanPath(new_name);
qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name));
h.rename_file(i, new_name);
}
}
// Force recheck
if(force_recheck) h.force_recheck();
// 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);
int timeout = 10;
while(!QDir().rmpath(old_folder.absolutePath()) && timeout > 0) {
// XXX: We should not sleep here (freezes the UI for 1 second)
SleeperThread::msleep(100);
--timeout;
}
}
}
}
void PropertiesWidget::askWebSeed(){
bool ok;
// Ask user for a new url seed
const QString url_seed = QInputDialog::getText(this, tr("New url seed", "New HTTP source"),
tr("New url seed:"), QLineEdit::Normal,
QString::fromUtf8("http://www."), &ok);
if(!ok) return;
qDebug("Adding %s web seed", qPrintable(url_seed));
if(!listWebSeeds->findItems(url_seed, Qt::MatchFixedString).empty()) {
QMessageBox::warning(this, tr("qBittorrent"),
tr("This url seed is already in the list."),
QMessageBox::Ok);
return;
}
h.add_url_seed(url_seed);
// Refresh the seeds list
loadUrlSeeds();
}
void PropertiesWidget::deleteSelectedUrlSeeds(){
const QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems();
bool change = false;
foreach(const QListWidgetItem *item, selectedItems){
QString url_seed = item->text();
h.remove_url_seed(url_seed);
change = true;
}
if(change){
// Refresh list
loadUrlSeeds();
}
}
bool PropertiesWidget::applyPriorities() {
qDebug("Saving files priorities");
const std::vector<int> priorities = PropListModel->model()->getFilesPriorities(h.get_torrent_info().num_files());
// Save first/last piece first option state
bool first_last_piece_first = h.first_last_piece_first();
// Prioritize the files
qDebug("prioritize files: %d", priorities[0]);
h.prioritize_files(priorities);
// Restore first/last piece first option if necessary
if(first_last_piece_first)
h.prioritize_first_last_piece(true);
return true;
}
void PropertiesWidget::on_changeSavePathButton_clicked() {
if(!h.is_valid()) return;
QString new_path;
if(h.has_metadata() && h.num_files() == 1) {
new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), h.firstFileSavePath());
} else {
const QDir saveDir(TorrentPersistentData::getSavePath(h.hash()));
new_path = QFileDialog::getExistingDirectory(this, tr("Choose save path"), saveDir.absolutePath(),
QFileDialog::DontConfirmOverwrite|QFileDialog::ShowDirsOnly|QFileDialog::HideNameFilterDetails);
}
if(!new_path.isEmpty()){
// Check if savePath exists
QString save_path_dir = new_path.replace("\\", "/");
QString new_file_name;
if(h.has_metadata() && h.num_files() == 1) {
new_file_name = misc::fileName(save_path_dir); // New file name
save_path_dir = misc::branchPath(save_path_dir, true); // Skip file name
}
QDir savePath(misc::expandPath(save_path_dir));
// Actually move storage
if(!QBtSession::instance()->useTemporaryFolder() || h.is_seed()) {
if(!savePath.exists()) savePath.mkpath(savePath.absolutePath());
h.move_storage(savePath.absolutePath());
}
// Update save_path in dialog
QString display_path;
if(h.has_metadata() && h.num_files() == 1) {
// Rename the file
Q_ASSERT(!new_file_name.isEmpty());
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
if(h.filename_at(0).compare(new_file_name, Qt::CaseInsensitive) != 0) {
#else
if(h.filename_at(0).compare(new_file_name, Qt::CaseSensitive) != 0) {
#endif
qDebug("Renaming single file to %s", qPrintable(new_file_name));
h.rename_file(0, new_file_name);
// Also rename it in the files list model
PropListModel->setData(PropListModel->index(0, 0), new_file_name);
}
display_path = h.firstFileSavePath();
} else {
display_path = savePath.absolutePath();
}
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
display_path = display_path.replace("/", "\\");
#endif
save_path->setText(display_path);
}
}
void PropertiesWidget::filteredFilesChanged() {
if(h.is_valid()) {
applyPriorities();
}
}

View File

@@ -0,0 +1,115 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef PROPERTIESWIDGET_H
#define PROPERTIESWIDGET_H
#include <QWidget>
#include "ui_propertieswidget.h"
#include "qtorrenthandle.h"
class TransferListWidget;
class TorrentFilesFilterModel;
class PropListDelegate;
class torrent_file;
class PeerListWidget;
class TrackerList;
class MainWindow;
class DownloadedPiecesBar;
class PieceAvailabilityBar;
class PropTabBar;
class LineEdit;
QT_BEGIN_NAMESPACE
class QAction;
class QTimer;
QT_END_NAMESPACE
class PropertiesWidget : public QWidget, private Ui::PropertiesWidget {
Q_OBJECT
Q_DISABLE_COPY(PropertiesWidget)
public:
enum SlideState {REDUCED, VISIBLE};
public:
PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList);
~PropertiesWidget();
QTorrentHandle getCurrentTorrent() const;
TrackerList* getTrackerList() const { return trackerList; }
PeerListWidget* getPeerList() const { return peersList; }
QTreeView* getFilesList() const { return filesList; }
protected:
QPushButton* getButtonFromIndex(int index);
bool applyPriorities();
protected slots:
void loadTorrentInfos(const QTorrentHandle &h);
void updateTorrentInfos(const QTorrentHandle &h);
void loadUrlSeeds();
void askWebSeed();
void deleteSelectedUrlSeeds();
void displayFilesListMenu(const QPoint& pos);
void on_changeSavePathButton_clicked();
void filteredFilesChanged();
void showPiecesDownloaded(bool show);
void showPiecesAvailability(bool show);
void renameSelectedFile();
public slots:
void setVisibility(bool visible);
void loadDynamicData();
void clear();
void readSettings();
void saveSettings();
void reloadPreferences();
void openDoubleClickedFile(QModelIndex);
void updateSavePath(const QTorrentHandle& h);
private:
TransferListWidget *transferList;
MainWindow *main_window;
QTorrentHandle h;
QTimer *refreshTimer;
SlideState state;
TorrentFilesFilterModel *PropListModel;
PropListDelegate *PropDelegate;
PeerListWidget *peersList;
TrackerList *trackerList;
QList<int> slideSizes;
DownloadedPiecesBar *downloaded_pieces;
PieceAvailabilityBar *pieces_availability;
PropTabBar *m_tabBar;
LineEdit *m_contentFilerLine;
};
#endif // PROPERTIESWIDGET_H

View File

@@ -0,0 +1,891 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PropertiesWidget</class>
<widget class="QWidget" name="PropertiesWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>540</width>
<height>274</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QStackedWidget" name="stackedProperties">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>520</width>
<height>373</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="ProgressHLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="downloaded_pieces_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Downloaded:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="progress_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string notr="true">0.0%</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="ProgressHLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="avail_pieces_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Availability:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="avail_average_lbl">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string notr="true">0.0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Transfer</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Uploaded:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="upTotal">
<property name="text">
<string notr="true">0 Kb</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>UP limit:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="lbl_uplimit">
<property name="text">
<string notr="true">∞</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="lbl_ratio">
<property name="text">
<string>Share ratio:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QLabel" name="shareRatio">
<property name="text">
<string notr="true">1.0</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Downloaded:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="dlTotal">
<property name="text">
<string notr="true">0 Kb</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>DL limit:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="lbl_dllimit">
<property name="text">
<string notr="true">∞</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Connections:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QLabel" name="lbl_connections">
<property name="text">
<string notr="true">0</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Wasted:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="wasted">
<property name="text">
<string notr="true">0 kb</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string extracomment="Time (duration) the torrent is active (not paused)">Time active:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="lbl_elapsed">
<property name="text">
<string notr="true">∞</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Reannounce in:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="6">
<widget class="QLabel" name="reannounce_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupTorrentInfos">
<property name="title">
<string>Information</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="savePath_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Save path:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="save_path">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">path</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="changeSavePathButton">
<property name="maximumSize">
<size>
<width>22</width>
<height>15</height>
</size>
</property>
<property name="text">
<string notr="true">...</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>14</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Created on:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="lbl_creationDate">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">date</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>14</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="hash_lbl2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Torrent hash:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="hash_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">hash</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>14</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Pieces size:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="pieceSize_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">piece size</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>14</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="comment_lbl2">
<property name="text">
<string>Comment:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QTextBrowser" name="comment_text">
<property name="html">
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:8pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;table border=&quot;0&quot; style=&quot;-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;&quot;&gt;
&lt;tr&gt;
&lt;td style=&quot;border: none;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';&quot;&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';&quot;&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';&quot;&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="openLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_trackers">
<layout class="QHBoxLayout" name="horizontalLayout_trackers">
<item>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="trackerUpButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>31</horstretch>
<verstretch>27</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>31</width>
<height>27</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>31</width>
<height>27</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="trackerDownButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>31</horstretch>
<verstretch>27</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>31</width>
<height>27</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>31</width>
<height>27</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_5">
<layout class="QVBoxLayout" name="peerpage_layout"/>
</widget>
<widget class="QWidget" name="page_3">
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QListWidget" name="listWebSeeds"/>
</item>
<item>
<layout class="QHBoxLayout" name="_19">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="deleteWS_button">
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addWS_button">
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="contentFilterLayout">
<item>
<widget class="QLabel" name="label_11">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Torrent content:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="filesList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AllEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="_6">
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="selectAllButton">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectNoneButton">
<property name="text">
<string>Select None</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
<action name="actionNormal">
<property name="text">
<string>Normal</string>
</property>
</action>
<action name="actionHigh">
<property name="text">
<string>High</string>
</property>
</action>
<action name="actionMaximum">
<property name="text">
<string>Maximum</string>
</property>
</action>
<action name="actionNot_downloaded">
<property name="text">
<string>Do not download</string>
</property>
<property name="toolTip">
<string>Do not download</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,202 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef PROPLISTDELEGATE_H
#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_WS_WIN
#include <QPlastiqueStyle>
#endif
// Defines for properties list columns
enum PropColumn {NAME, PCSIZE, PROGRESS, PRIORITY};
class PropListDelegate: public QItemDelegate {
Q_OBJECT
private:
PropertiesWidget *properties;
signals:
void filteredFilesChanged() const;
public:
PropListDelegate(PropertiesWidget* properties=0, QObject *parent=0) : QItemDelegate(parent), properties(properties){
}
~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:{
QStyleOptionProgressBarV2 newopt;
qreal progress = index.data().toDouble()*100.;
newopt.rect = opt.rect;
// We don't want to display 100% unless
// the torrent is really complete
if(progress > 99.94 && progress < 100.)
progress = 99.9;
newopt.text = QString(QByteArray::number(progress, 'f', 1))+QString::fromUtf8("%");
newopt.progress = (int)progress;
newopt.maximum = 100;
newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = true;
#ifndef Q_WS_WIN
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
#else
// XXX: To avoid having the progress text on the right of the bar
QPlastiqueStyle st;
st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0);
#endif
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();
}
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const{
QVariant value = index.data(Qt::FontRole);
QFont fnt = value.isValid() ? qvariant_cast<QFont>(value) : option.font;
QFontMetrics fontMetrics(fnt);
const QString text = index.data(Qt::DisplayRole).toString();
QRect textRect = QRect(0, 0, 0, fontMetrics.lineSpacing() * (text.count(QLatin1Char('\n')) + 1));
textRect.setHeight(textRect.height()+4);
return textRect.size();
}
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 LIBTORRENT_VERSION_MINOR > 15
if(!h.is_valid() || !h.has_metadata() || h.status(0x0).is_seeding) return 0;
#else
if(!h.is_valid() || !h.has_metadata() || static_cast<libtorrent::torrent_handle>(h).is_seed()) return 0;
#endif
}
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;
}
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 updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
qDebug("UpdateEditor Geometry called");
editor->setGeometry(option.rect);
}
};
#endif

View File

@@ -0,0 +1,129 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 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 <QButtonGroup>
#include <QPushButton>
#include <QSpacerItem>
#include <QKeySequence>
#include "proptabbar.h"
#include "iconprovider.h"
#ifdef Q_WS_MAC
#define DEFAULT_BUTTON_CSS "QPushButton {border: 1px solid #888;border-radius: 2px; padding: 4px; margin-left: 0px; margin-right: 8px;}"
#define SELECTED_BUTTON_CSS "QPushButton {border: 1px solid #888;border-radius: 2px; padding: 4px;background-color: palette(highlight); color: palette(highlighted-text); margin-left: 0px; margin-right: 8px;}"
#else
#define DEFAULT_BUTTON_CSS "QPushButton {border: 1px solid #888;border-radius: 2px; padding: 4px; margin-left: 0px; margin-right: 3px;}"
#define SELECTED_BUTTON_CSS "QPushButton {border: 1px solid #888;border-radius: 2px; padding: 4px;background-color: palette(highlight); color: palette(highlighted-text); margin-left: 0px; margin-right: 3px;}"
#endif
const int BTN_ICON_SIZE = 16;
PropTabBar::PropTabBar(QWidget *parent) :
QHBoxLayout(parent), m_currentIndex(-1)
{
m_btnGroup = new QButtonGroup(this);
setContentsMargins(0, 4, 0, 4);
// General tab
QPushButton *main_infos_button = new QPushButton(IconProvider::instance()->getIcon("document-properties"), tr("General"), parent);
main_infos_button->setShortcut(QKeySequence(QString::fromUtf8("Alt+P")));
main_infos_button->setStyleSheet(DEFAULT_BUTTON_CSS);
main_infos_button->setIconSize(QSize(BTN_ICON_SIZE, BTN_ICON_SIZE));
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);
trackers_button->setStyleSheet(DEFAULT_BUTTON_CSS);
trackers_button->setIconSize(QSize(BTN_ICON_SIZE, BTN_ICON_SIZE));
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);
peers_button->setStyleSheet(DEFAULT_BUTTON_CSS);
peers_button->setIconSize(QSize(BTN_ICON_SIZE, BTN_ICON_SIZE));
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);
urlseeds_button->setStyleSheet(DEFAULT_BUTTON_CSS);
urlseeds_button->setIconSize(QSize(BTN_ICON_SIZE, BTN_ICON_SIZE));
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);
files_button->setStyleSheet(DEFAULT_BUTTON_CSS);
files_button->setIconSize(QSize(BTN_ICON_SIZE, BTN_ICON_SIZE));
addWidget(files_button);
m_btnGroup->addButton(files_button, FILES_TAB);
// Spacer
addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
// SIGNAL/SLOT
connect(m_btnGroup, SIGNAL(buttonClicked(int)), SLOT(setCurrentIndex(int)));
// Disable buttons focus
foreach(QAbstractButton *btn, m_btnGroup->buttons()) {
btn->setFocusPolicy(Qt::NoFocus);
}
}
PropTabBar::~PropTabBar() {
delete m_btnGroup;
}
int PropTabBar::currentIndex() const
{
return m_currentIndex;
}
void PropTabBar::setCurrentIndex(int index)
{
if(index >= m_btnGroup->buttons().size())
index = 0;
// If asked to hide or if the currently selected tab is clicked
if(index < 0 || m_currentIndex == index) {
if(m_currentIndex >= 0) {
m_btnGroup->button(m_currentIndex)->setStyleSheet(DEFAULT_BUTTON_CSS);
m_currentIndex = -1;
emit visibilityToggled(false);
}
return;
}
// Unselect previous tab
if(m_currentIndex >= 0) {
m_btnGroup->button(m_currentIndex)->setStyleSheet(DEFAULT_BUTTON_CSS);
} else {
// Nothing was selected, show!
emit visibilityToggled(true);
}
// Select the new button
m_btnGroup->button(index)->setStyleSheet(SELECTED_BUTTON_CSS);
m_currentIndex = index;
// Emit the signal
emit tabChanged(index);
}

View File

@@ -0,0 +1,66 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2010 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
*/
#ifndef PROPTABBAR_H
#define PROPTABBAR_H
#include <QHBoxLayout>
QT_BEGIN_NAMESPACE
class QButtonGroup;
QT_END_NAMESPACE
class PropTabBar : public QHBoxLayout
{
Q_OBJECT
Q_DISABLE_COPY(PropTabBar)
public:
enum PropertyTab {MAIN_TAB, TRACKERS_TAB, PEERS_TAB, URLSEEDS_TAB, FILES_TAB};
public:
explicit PropTabBar(QWidget *parent = 0);
~PropTabBar();
int currentIndex() const;
signals:
void tabChanged(int index);
void visibilityToggled(bool visible);
public slots:
void setCurrentIndex(int index);
private:
QButtonGroup *m_btnGroup;
int m_currentIndex;
};
#endif // PROPTABBAR_H

View File

@@ -0,0 +1,379 @@
/*
* Bittorrent Client using Qt4 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 <QTreeWidgetItem>
#include <QStringList>
#include <QMenu>
#include <QHash>
#include <QAction>
#include <QColor>
#include <libtorrent/version.hpp>
#include <libtorrent/peer_info.hpp>
#include "trackerlist.h"
#include "propertieswidget.h"
#include "trackersadditiondlg.h"
#include "iconprovider.h"
#include "qbtsession.h"
#include "qinisettings.h"
#include "misc.h"
using namespace libtorrent;
TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) {
loadSettings();
// Graphical settings
setRootIsDecorated(false);
setAllColumnsShowFocus(true);
setItemsExpandable(false);
setSelectionMode(QAbstractItemView::ExtendedSelection);
// Context menu
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showTrackerListMenu(QPoint)));
// Set header
QStringList header;
header << tr("URL");
header << tr("Status");
header << tr("Peers");
header << tr("Message");
setHeaderItem(new QTreeWidgetItem(header));
dht_item = new QTreeWidgetItem(QStringList("** "+tr("[DHT]")+" **"));
insertTopLevelItem(0, dht_item);
setRowColor(0, QColor("grey"));
pex_item = new QTreeWidgetItem(QStringList("** "+tr("[PeX]")+" **"));
insertTopLevelItem(1, pex_item);
setRowColor(1, QColor("grey"));
lsd_item = new QTreeWidgetItem(QStringList("** "+tr("[LSD]")+" **"));
insertTopLevelItem(2, lsd_item);
setRowColor(2, QColor("grey"));
}
TrackerList::~TrackerList() {
saveSettings();
}
QList<QTreeWidgetItem*> TrackerList::getSelectedTrackerItems() const {
QList<QTreeWidgetItem*> selected_items = selectedItems();
QList<QTreeWidgetItem*> selected_trackers;
foreach(QTreeWidgetItem *item, selectedItems()) {
if(indexOfTopLevelItem(item) >= NB_STICKY_ITEM) { // Ignore STICKY ITEMS
selected_trackers << item;
}
}
return selected_trackers;
}
void TrackerList::setRowColor(int row, QColor color) {
unsigned int nbColumns = columnCount();
QTreeWidgetItem *item = topLevelItem(row);
for(unsigned int i=0; i<nbColumns; ++i) {
item->setData(i, Qt::ForegroundRole, color);
}
}
void TrackerList::moveSelectionUp() {
#if LIBTORRENT_VERSION_MINOR < 15
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) {
clear();
return;
}
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
if(selected_items.isEmpty()) return;
bool change = false;
foreach(QTreeWidgetItem *item, selected_items){
int index = indexOfTopLevelItem(item);
if(index > NB_STICKY_ITEM) {
insertTopLevelItem(index-1, takeTopLevelItem(index));
change = true;
}
}
if(!change) return;
// Restore selection
QItemSelectionModel *selection = selectionModel();
foreach(QTreeWidgetItem *item, selected_items) {
selection->select(indexFromItem(item), QItemSelectionModel::Rows|QItemSelectionModel::Select);
}
setSelectionModel(selection);
// Update torrent trackers
std::vector<announce_entry> 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);
}
h.replace_trackers(trackers);
// Reannounce
h.force_reannounce();
#endif
}
void TrackerList::moveSelectionDown() {
#if LIBTORRENT_VERSION_MINOR < 15
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) {
clear();
return;
}
QList<QTreeWidgetItem *> selected_items = getSelectedTrackerItems();
if(selected_items.isEmpty()) return;
bool change = false;
for(int i=selectedItems().size()-1; i>= 0; --i) {
int index = indexOfTopLevelItem(selected_items.at(i));
if(index < topLevelItemCount()-1) {
insertTopLevelItem(index+1, takeTopLevelItem(index));
change = true;
}
}
if(!change) return;
// Restore selection
QItemSelectionModel *selection = selectionModel();
foreach(QTreeWidgetItem *item, selected_items) {
selection->select(indexFromItem(item), QItemSelectionModel::Rows|QItemSelectionModel::Select);
}
setSelectionModel(selection);
// Update torrent trackers
std::vector<announce_entry> 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);
}
h.replace_trackers(trackers);
// Reannounce
h.force_reannounce();
#endif
}
void TrackerList::clear() {
qDeleteAll(tracker_items.values());
tracker_items.clear();
dht_item->setText(COL_PEERS, "");
dht_item->setText(COL_STATUS, "");
dht_item->setText(COL_MSG, "");
pex_item->setText(COL_PEERS, "");
pex_item->setText(COL_STATUS, "");
pex_item->setText(COL_MSG, "");
lsd_item->setText(COL_PEERS, "");
lsd_item->setText(COL_STATUS, "");
lsd_item->setText(COL_MSG, "");
}
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;
for(it=peers.begin(); it!=peers.end(); it++) {
if(it->source & peer_info::dht)
++nb_dht;
if(it->source & peer_info::lsd)
++nb_lsd;
if(it->source & peer_info::pex)
++nb_pex;
}
// load DHT information
if(QBtSession::instance()->isDHTEnabled() && h.has_metadata() && !h.priv()) {
dht_item->setText(COL_STATUS, tr("Working"));
} else {
dht_item->setText(COL_STATUS, tr("Disabled"));
}
dht_item->setText(COL_PEERS, QString::number(nb_dht));
if(h.has_metadata() && h.priv()) {
dht_item->setText(COL_MSG, tr("This torrent is private"));
}
// Load PeX Information
if(QBtSession::instance()->isPexEnabled())
pex_item->setText(COL_STATUS, tr("Working"));
else
pex_item->setText(COL_STATUS, tr("Disabled"));
pex_item->setText(COL_PEERS, QString::number(nb_pex));
// Load LSD Information
if(QBtSession::instance()->isLSDEnabled())
lsd_item->setText(COL_STATUS, tr("Working"));
else
lsd_item->setText(COL_STATUS, tr("Disabled"));
lsd_item->setText(COL_PEERS, QString::number(nb_lsd));
}
void TrackerList::loadTrackers() {
// Load trackers from torrent handle
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
loadStickyItems(h);
// Load actual trackers information
QHash<QString, TrackerInfos> trackers_data = QBtSession::instance()->getTrackersInfo(h.hash());
QStringList old_trackers_urls = tracker_items.keys();
const std::vector<announce_entry> trackers = h.trackers();
for(std::vector<announce_entry>::const_iterator it = trackers.begin(); it != trackers.end(); it++) {
QString tracker_url = misc::toQString(it->url);
QTreeWidgetItem *item = tracker_items.value(tracker_url, 0);
if(!item) {
item = new QTreeWidgetItem();
item->setText(COL_URL, tracker_url);
addTopLevelItem(item);
tracker_items[tracker_url] = item;
} else {
old_trackers_urls.removeOne(tracker_url);
}
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
QString error_message = data.last_message.trimmed();
#if LIBTORRENT_VERSION_MINOR > 14
if(it->verified) {
item->setText(COL_STATUS, tr("Working"));
item->setText(COL_MSG, "");
} else {
if(it->updating && it->fails == 0) {
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, "");
}
}
}
#else
if(data.verified) {
item->setText(COL_STATUS, tr("Working"));
item->setText(COL_MSG, "");
} else {
if(data.fail_count > 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, "");
}
}
#endif
item->setText(COL_PEERS, QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers));
}
// Remove old trackers
foreach(const QString &tracker, old_trackers_urls) {
delete tracker_items.take(tracker);
}
}
// Ask the user for new trackers and add them to the torrent
void TrackerList::askForTrackers(){
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
QStringList trackers = TrackersAdditionDlg::askForTrackers(h);
if(!trackers.empty()) {
foreach(const QString& tracker, trackers) {
if(tracker.trimmed().isEmpty()) continue;
announce_entry url(tracker.toStdString());
url.tier = 0;
h.add_tracker(url);
}
// Reannounce to new trackers
h.force_reannounce();
// Reload tracker list
loadTrackers();
}
}
void TrackerList::deleteSelectedTrackers(){
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) {
clear();
return;
}
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();
urls_to_remove << tracker_url;
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;
for(it = trackers.begin(); it != trackers.end(); it++) {
if(!urls_to_remove.contains(misc::toQString((*it).url))) {
remaining_trackers.push_back(*it);
}
}
h.replace_trackers(remaining_trackers);
h.force_reannounce();
// Reload Trackers
loadTrackers();
}
void TrackerList::showTrackerListMenu(QPoint) {
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) 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 *delAct = 0;
if(!getSelectedTrackerItems().isEmpty()) {
delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove tracker"));
}
menu.addSeparator();
QAction *reannounceAct = menu.addAction(IconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce"));
QAction *act = menu.exec(QCursor::pos());
if(act == 0) return;
if(act == addAct) {
askForTrackers();
return;
}
if(act == delAct) {
deleteSelectedTrackers();
return;
}
if(act == reannounceAct) {
properties->getCurrentTorrent().force_reannounce();
return;
}
}
void TrackerList::loadSettings() {
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
if(!header()->restoreState(settings.value("TorrentProperties/Trackers/TrackerListState").toByteArray())) {
setColumnWidth(0, 300);
}
}
void TrackerList::saveSettings() const {
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
settings.setValue("TorrentProperties/Trackers/TrackerListState", header()->saveState());
}

View File

@@ -0,0 +1,79 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef TRACKERLIST_H
#define TRACKERLIST_H
#include <QTreeWidget>
#include <QList>
#include <libtorrent/version.hpp>
#include "qtorrenthandle.h"
#include "propertieswidget.h"
enum TrackerListColumn {COL_URL, COL_STATUS, COL_PEERS, COL_MSG};
#define NB_STICKY_ITEM 3
class TrackerList: public QTreeWidget {
Q_OBJECT
Q_DISABLE_COPY(TrackerList)
private:
PropertiesWidget *properties;
QHash<QString, QTreeWidgetItem*> tracker_items;
QTreeWidgetItem* dht_item;
QTreeWidgetItem* pex_item;
QTreeWidgetItem* lsd_item;
public:
TrackerList(PropertiesWidget *properties);
~TrackerList();
protected:
QList<QTreeWidgetItem*> getSelectedTrackerItems() const;
public slots:
void setRowColor(int row, QColor color);
void moveSelectionUp();
void moveSelectionDown();
void clear();
void loadStickyItems(const QTorrentHandle &h);
void loadTrackers();
void askForTrackers();
void deleteSelectedTrackers();
void showTrackerListMenu(QPoint);
void loadSettings();
void saveSettings() const;
};
#endif // TRACKERLIST_H

View File

@@ -0,0 +1,148 @@
/*
* Bittorrent Client using Qt4 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
*/
#ifndef TRACKERSADDITION_H
#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"
class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{
Q_OBJECT
private:
QTorrentHandle h;
public:
TrackersAdditionDlg(QTorrentHandle h, QWidget *parent=0): QDialog(parent), h(h) {
setupUi(this);
// Icons
uTorrentListButton->setIcon(IconProvider::instance()->getIcon("download"));
// As a default, use torrentz.com link
list_url->setText("http://www.torrentz.com/announce_"+h.hash());
list_url->setCursorPosition(0);
}
~TrackersAdditionDlg(){}
QStringList newTrackers() const {
return trackers_list->toPlainText().trimmed().split("\n");
}
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 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();
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();
while(itr != tor_trackers.end()) {
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();
list_file.remove();
//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;
}
};
#endif

View File

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TrackersAdditionDlg</class>
<widget class="QDialog" name="TrackersAdditionDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>367</width>
<height>274</height>
</rect>
</property>
<property name="windowTitle">
<string>Trackers addition dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>List of trackers to add (one per line):</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="trackers_list">
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="html">
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;&quot;&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>µTorrent compatible list URL:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="list_url"/>
</item>
<item>
<widget class="QPushButton" name="uTorrentListButton">
<property name="minimumSize">
<size>
<width>31</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>31</width>
<height>31</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>TrackersAdditionDlg</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>TrackersAdditionDlg</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>