Browse Source

- New tracker list (displays tracker status and error/warning messages)

adaptive-webui-19844
Christophe Dumez 15 years ago
parent
commit
ccdb2a82c3
  1. 1
      Changelog
  2. 7
      src/misc.h
  3. 251
      src/propertiesWidget.ui
  4. 174
      src/propertieswidget.cpp
  5. 8
      src/propertieswidget.h
  6. 5
      src/qtorrenthandle.cpp
  7. 1
      src/qtorrenthandle.h
  8. 5
      src/src.pro
  9. 196
      src/trackerlist.h
  10. 26
      src/trackersadditiondlg.h

1
Changelog

@ -14,6 +14,7 @@
- FEATURE: Support peer manual ban - FEATURE: Support peer manual ban
- FEATURE: Display total amounts transferred in status bar - FEATURE: Display total amounts transferred in status bar
- FEATURE: Announce to all trackers specified for a torrent (µTorrent behavior) - FEATURE: Announce to all trackers specified for a torrent (µTorrent behavior)
- FEATURE: Display trackers status as well as error/warning messages
- FEATURE: Dropped Qt 4.3 support (Qt >= 4.4 is now required) - FEATURE: Dropped Qt 4.3 support (Qt >= 4.4 is now required)
- FEATURE: Added per-torrent super seeding mode (libtorrent >= v0.15 only) - FEATURE: Added per-torrent super seeding mode (libtorrent >= v0.15 only)
- FEATURE: Support for storing symbolic links in .torrent files (libtorrent >= v0.15 only) - FEATURE: Support for storing symbolic links in .torrent files (libtorrent >= v0.15 only)

7
src/misc.h

@ -223,13 +223,6 @@ public:
return qBtPath; return qBtPath;
} }
static void fixTrackersTiers(std::vector<announce_entry> trackers) {
unsigned int nbTrackers = trackers.size();
for(unsigned int i=0; i<nbTrackers; ++i) {
trackers[i].tier = i;
}
}
// Insertion sort, used instead of bubble sort because it is // Insertion sort, used instead of bubble sort because it is
// approx. 5 times faster. // approx. 5 times faster.
template <class T> static void insertSort(QList<QPair<int, T> > &list, const QPair<int, T>& value, Qt::SortOrder sortOrder) { template <class T> static void insertSort(QList<QPair<int, T> > &list, const QPair<int, T>& value, Qt::SortOrder sortOrder) {

251
src/propertiesWidget.ui

@ -479,255 +479,8 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="page_2"> <widget class="QWidget" name="page_trackers">
<layout class="QVBoxLayout" name="verticalLayout_8"> <layout class="QVBoxLayout" name="verticalLayout_trackers"/>
<item>
<layout class="QHBoxLayout" name="_20">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="_21">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="trackersURLS">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="_22">
<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="removeTracker_button">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/Icons/oxygen/list-remove.png</normaloff>:/Icons/oxygen/list-remove.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addTracker_button">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/Icons/oxygen/list-add.png</normaloff>:/Icons/oxygen/list-add.png</iconset>
</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>
</item>
<item>
<layout class="QVBoxLayout" name="_23">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<spacer>
<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="riseTracker_button">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/Icons/uparrow.png</normaloff>:/Icons/uparrow.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="lowerTracker_button">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/Icons/downarrow.png</normaloff>:/Icons/downarrow.png</iconset>
</property>
</widget>
</item>
<item>
<spacer>
<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>
</item>
<item>
<layout class="QHBoxLayout" name="_24">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="text">
<string>Current tracker:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="trackerURL">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</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>
<widget class="QWidget" name="page_5"> <widget class="QWidget" name="page_5">
<layout class="QVBoxLayout" name="peerpage_layout"/> <layout class="QVBoxLayout" name="peerpage_layout"/>

174
src/propertieswidget.cpp

@ -45,9 +45,9 @@
#include "realprogressbarthread.h" #include "realprogressbarthread.h"
#include "bittorrent.h" #include "bittorrent.h"
#include "PropListDelegate.h" #include "PropListDelegate.h"
#include "TrackersAdditionDlg.h"
#include "TorrentFilesModel.h" #include "TorrentFilesModel.h"
#include "peerlistwidget.h" #include "peerlistwidget.h"
#include "trackerlist.h"
#ifdef Q_WS_MAC #ifdef Q_WS_MAC
#define DEFAULT_BUTTON_CSS "" #define DEFAULT_BUTTON_CSS ""
@ -86,10 +86,6 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, TransferListWidget *transfer
connect(collapseAllButton, SIGNAL(clicked()), filesList, SLOT(collapseAll())); connect(collapseAllButton, SIGNAL(clicked()), filesList, SLOT(collapseAll()));
connect(expandAllButton, SIGNAL(clicked()), filesList, SLOT(expandAll())); connect(expandAllButton, SIGNAL(clicked()), filesList, SLOT(expandAll()));
connect(filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&))); connect(filesList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFilesListMenu(const QPoint&)));
connect(addTracker_button, SIGNAL(clicked()), this, SLOT(askForTracker()));
connect(removeTracker_button, SIGNAL(clicked()), this, SLOT(deleteSelectedTrackers()));
connect(riseTracker_button, SIGNAL(clicked()), this, SLOT(riseSelectedTracker()));
connect(lowerTracker_button, SIGNAL(clicked()), this, SLOT(lowerSelectedTracker()));
connect(actionIgnored, SIGNAL(triggered()), this, SLOT(ignoreSelection())); connect(actionIgnored, SIGNAL(triggered()), this, SLOT(ignoreSelection()));
connect(actionNormal, SIGNAL(triggered()), this, SLOT(normalSelection())); connect(actionNormal, SIGNAL(triggered()), this, SLOT(normalSelection()));
connect(actionHigh, SIGNAL(triggered()), this, SLOT(highSelection())); connect(actionHigh, SIGNAL(triggered()), this, SLOT(highSelection()));
@ -105,6 +101,9 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, TransferListWidget *transfer
progressBar = new RealProgressBar(this); progressBar = new RealProgressBar(this);
progressBar->setForegroundColor(Qt::blue); progressBar->setForegroundColor(Qt::blue);
ProgressHLayout->insertWidget(1, progressBar); ProgressHLayout->insertWidget(1, progressBar);
// Tracker list
trackerList = new TrackerList(this);
verticalLayout_trackers->addWidget(trackerList);
// Peers list // Peers list
peersList = new PeerListWidget(this); peersList = new PeerListWidget(this);
peerpage_layout->addWidget(peersList); peerpage_layout->addWidget(peersList);
@ -113,7 +112,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, TransferListWidget *transfer
// Dynamic data refresher // Dynamic data refresher
refreshTimer = new QTimer(this); refreshTimer = new QTimer(this);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData())); connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
refreshTimer->start(10000); // 10sec refreshTimer->start(3000); // 3sec
} }
PropertiesWidget::~PropertiesWidget() { PropertiesWidget::~PropertiesWidget() {
@ -121,6 +120,7 @@ PropertiesWidget::~PropertiesWidget() {
delete refreshTimer; delete refreshTimer;
if(progressBarUpdater) if(progressBarUpdater)
delete progressBarUpdater; delete progressBarUpdater;
delete trackerList;
delete peersList; delete peersList;
delete progressBar; delete progressBar;
delete PropListModel; delete PropListModel;
@ -166,8 +166,7 @@ void PropertiesWidget::clear() {
hash_lbl->clear(); hash_lbl->clear();
comment_lbl->clear(); comment_lbl->clear();
incrementalDownload->setChecked(false); incrementalDownload->setChecked(false);
trackersURLS->clear(); trackerList->clear();
trackerURL->clear();
progressBar->setProgress(QRealArray()); progressBar->setProgress(QRealArray());
wasted->clear(); wasted->clear();
upTotal->clear(); upTotal->clear();
@ -214,8 +213,6 @@ void PropertiesWidget::loadTorrentInfos(QTorrentHandle &_h) {
comment_lbl->setText(h.comment()); comment_lbl->setText(h.comment());
// Sequential download // Sequential download
incrementalDownload->setChecked(TorrentPersistentData::isSequentialDownload(h.hash())); incrementalDownload->setChecked(TorrentPersistentData::isSequentialDownload(h.hash()));
// Trackers
loadTrackers();
// URL seeds // URL seeds
loadUrlSeeds(); loadUrlSeeds();
// downloaded pieces updater // downloaded pieces updater
@ -328,6 +325,11 @@ void PropertiesWidget::loadDynamicData() {
progress_lbl->setText(QString::number(h.progress()*100., 'f', 1)+"%"); progress_lbl->setText(QString::number(h.progress()*100., 'f', 1)+"%");
return; return;
} }
if(stackedProperties->currentIndex() == TRACKERS_TAB) {
// Trackers
trackerList->loadTrackers();
return;
}
if(stackedProperties->currentIndex() == PEERS_TAB) { if(stackedProperties->currentIndex() == PEERS_TAB) {
// Load peers // Load peers
peersList->loadPeers(h); peersList->loadPeers(h);
@ -348,41 +350,6 @@ void PropertiesWidget::setIncrementalDownload(int checkboxState) {
TorrentPersistentData::saveSequentialStatus(h); TorrentPersistentData::saveSequentialStatus(h);
} }
void PropertiesWidget::loadTrackers() {
if(!h.is_valid()) return;
//Trackers
std::vector<announce_entry> trackers = h.trackers();
trackersURLS->clear();
QHash<QString, QString> errors = BTSession->getTrackersErrors(h.hash());
unsigned int nbTrackers = trackers.size();
for(unsigned int i=0; i<nbTrackers; ++i){
QString current_tracker = misc::toQString(trackers[i].url);
QListWidgetItem *item = new QListWidgetItem(current_tracker, trackersURLS);
// IsThere any errors ?
if(errors.contains(current_tracker)) {
item->setForeground(QBrush(QColor("red")));
// Set tooltip
QString msg="";
unsigned int i=0;
foreach(QString word, errors[current_tracker].split(" ")) {
if(i > 0 && i%5!=1) msg += " ";
msg += word;
if(i> 0 && i%5==0) msg += "\n";
++i;
}
item->setToolTip(msg);
} else {
item->setForeground(QBrush(QColor("green")));
}
}
QString tracker = h.current_tracker().trimmed();
if(!tracker.isEmpty()){
trackerURL->setText(tracker);
}else{
trackerURL->setText(tr("None - Unreachable?"));
}
}
void PropertiesWidget::loadUrlSeeds(){ void PropertiesWidget::loadUrlSeeds(){
QStringList already_added; QStringList already_added;
listWebSeeds->clear(); listWebSeeds->clear();
@ -578,30 +545,6 @@ void PropertiesWidget::askWebSeed(){
loadUrlSeeds(); loadUrlSeeds();
} }
// Ask the user for a new tracker
// and add it to the download list
// if it is not already in it
void PropertiesWidget::askForTracker(){
TrackersAddDlg *dlg = new TrackersAddDlg(this);
connect(dlg, SIGNAL(TrackersToAdd(QStringList)), this, SLOT(addTrackerList(QStringList)));
}
void PropertiesWidget::addTrackerList(QStringList myTrackers) {
// Add the trackers to the list
std::vector<announce_entry> trackers = h.trackers();
foreach(const QString& tracker, myTrackers) {
announce_entry new_tracker(misc::toString(tracker.trimmed().toLocal8Bit().data()));
new_tracker.tier = 0; // Will be fixed a bit later
trackers.push_back(new_tracker);
misc::fixTrackersTiers(trackers);
}
h.replace_trackers(trackers);
h.force_reannounce();
// Reload Trackers
loadTrackers();
BTSession->saveTrackerFile(h.hash());
}
void PropertiesWidget::deleteSelectedUrlSeeds(){ void PropertiesWidget::deleteSelectedUrlSeeds(){
QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems(); QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems();
bool change = false; bool change = false;
@ -618,99 +561,6 @@ void PropertiesWidget::deleteSelectedUrlSeeds(){
} }
} }
void PropertiesWidget::deleteSelectedTrackers(){
QList<QListWidgetItem *> selectedItems = trackersURLS->selectedItems();
if(!selectedItems.size()) return;
std::vector<announce_entry> trackers = h.trackers();
unsigned int nbTrackers = trackers.size();
if(nbTrackers == (unsigned int) selectedItems.size()){
QMessageBox::warning(this, tr("qBittorrent"),
tr("Trackers list can't be empty."),
QMessageBox::Ok);
return;
}
foreach(QListWidgetItem *item, selectedItems){
QString url = item->text();
for(unsigned int i=0; i<nbTrackers; ++i){
if(misc::toQString(trackers.at(i).url) == url){
trackers.erase(trackers.begin()+i);
break;
}
}
}
h.replace_trackers(trackers);
h.force_reannounce();
// Reload Trackers
loadTrackers();
BTSession->saveTrackerFile(h.hash());
}
void PropertiesWidget::riseSelectedTracker(){
unsigned int i = 0;
std::vector<announce_entry> trackers = h.trackers();
QList<QListWidgetItem *> selectedItems = trackersURLS->selectedItems();
bool change = false;
unsigned int nbTrackers = trackers.size();
foreach(QListWidgetItem *item, selectedItems){
QString url = item->text();
for(i=0; i<nbTrackers; ++i){
if(misc::toQString(trackers.at(i).url) == url){
qDebug("Asked to rise %s", trackers.at(i).url.c_str());
qDebug("its tier was %d and will become %d", trackers[i].tier, trackers[i].tier-1);
if(i > 0){
announce_entry tmp = trackers[i];
trackers[i] = trackers[i-1];
trackers[i-1] = tmp;
change = true;
}
break;
}
}
}
if(change){
misc::fixTrackersTiers(trackers);
h.replace_trackers(trackers);
h.force_reannounce();
// Reload Trackers
loadTrackers();
trackersURLS->item(i-1)->setSelected(true);
BTSession->saveTrackerFile(h.hash());
}
}
void PropertiesWidget::lowerSelectedTracker(){
unsigned int i = 0;
std::vector<announce_entry> trackers = h.trackers();
QList<QListWidgetItem *> selectedItems = trackersURLS->selectedItems();
bool change = false;
unsigned int nbTrackers = trackers.size();
foreach(QListWidgetItem *item, selectedItems){
QString url = item->text();
for(i=0; i<nbTrackers; ++i){
if(misc::toQString(trackers.at(i).url) == url){
qDebug("Asked to lower %s", trackers.at(i).url.c_str());
qDebug("its tier was %d and will become %d", trackers[i].tier, trackers[i].tier+1);
if(i < nbTrackers-1){
announce_entry tmp = trackers[i];
trackers[i] = trackers[i+1];
trackers[i+1] = tmp;
change = true;
}
break;
}
}
}
if(change){
misc::fixTrackersTiers(trackers);
h.replace_trackers(trackers);
h.force_reannounce();
// Reload Trackers
loadTrackers();
trackersURLS->item(i+1)->setSelected(true);
BTSession->saveTrackerFile(h.hash());
}
}
bool PropertiesWidget::savePiecesPriorities() { bool PropertiesWidget::savePiecesPriorities() {
qDebug("Saving pieces priorities"); qDebug("Saving pieces priorities");
std::vector<int> priorities = PropListModel->getFilesPriorities(h.get_torrent_info().num_files()); std::vector<int> priorities = PropListModel->getFilesPriorities(h.get_torrent_info().num_files());

8
src/propertieswidget.h

@ -46,6 +46,7 @@ class PropListDelegate;
class QAction; class QAction;
class torrent_file; class torrent_file;
class PeerListWidget; class PeerListWidget;
class TrackerList;
enum Tab {MAIN_TAB, TRACKERS_TAB, PEERS_TAB, URLSEEDS_TAB, FILES_TAB}; enum Tab {MAIN_TAB, TRACKERS_TAB, PEERS_TAB, URLSEEDS_TAB, FILES_TAB};
enum SlideState {REDUCED, VISIBLE}; enum SlideState {REDUCED, VISIBLE};
@ -64,6 +65,7 @@ private:
TorrentFilesModel *PropListModel; TorrentFilesModel *PropListModel;
PropListDelegate *PropDelegate; PropListDelegate *PropDelegate;
PeerListWidget *peersList; PeerListWidget *peersList;
TrackerList *trackerList;
QAction *actionIgnored; QAction *actionIgnored;
QAction *actionNormal; QAction *actionNormal;
QAction *actionMaximum; QAction *actionMaximum;
@ -79,7 +81,6 @@ protected slots:
void loadTorrentInfos(QTorrentHandle &h); void loadTorrentInfos(QTorrentHandle &h);
void loadDynamicData(); void loadDynamicData();
void setIncrementalDownload(int checkboxState); void setIncrementalDownload(int checkboxState);
void loadTrackers();
void loadUrlSeeds(); void loadUrlSeeds();
void on_main_infos_button_clicked(); void on_main_infos_button_clicked();
void on_trackers_button_clicked(); void on_trackers_button_clicked();
@ -92,13 +93,8 @@ protected slots:
void maximumSelection(); void maximumSelection();
void askWebSeed(); void askWebSeed();
void deleteSelectedUrlSeeds(); void deleteSelectedUrlSeeds();
void askForTracker();
void deleteSelectedTrackers();
void lowerSelectedTracker();
void riseSelectedTracker();
void displayFilesListMenu(const QPoint& pos); void displayFilesListMenu(const QPoint& pos);
void on_changeSavePathButton_clicked(); void on_changeSavePathButton_clicked();
void addTrackerList(QStringList myTrackers);
void filteredFilesChanged(); void filteredFilesChanged();
public slots: public slots:

5
src/qtorrenthandle.cpp

@ -522,6 +522,11 @@ void QTorrentHandle::set_peer_download_limit(asio::ip::tcp::endpoint ip, int lim
h.set_peer_download_limit(ip, limit); h.set_peer_download_limit(ip, limit);
} }
void QTorrentHandle::add_tracker(announce_entry const& url) {
Q_ASSERT(h.is_valid());
h.add_tracker(url);
}
// //
// Operators // Operators
// //

1
src/qtorrenthandle.h

@ -153,6 +153,7 @@ class QTorrentHandle {
void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) const; void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) const;
void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const; void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const;
void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const; void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const;
void add_tracker(announce_entry const& url);
// //
// Operators // Operators

5
src/src.pro

@ -173,7 +173,7 @@ HEADERS += GUI.h \
json.h \ json.h \
eventmanager.h \ eventmanager.h \
filterParserThread.h \ filterParserThread.h \
TrackersAdditionDlg.h \ trackersadditiondlg.h \
searchTab.h \ searchTab.h \
console_imp.h \ console_imp.h \
ico.h \ ico.h \
@ -195,7 +195,8 @@ HEADERS += GUI.h \
geoip.h \ geoip.h \
peeraddition.h \ peeraddition.h \
deletionconfirmationdlg.h \ deletionconfirmationdlg.h \
statusbar.h statusbar.h \
trackerlist.h
FORMS += MainWindow.ui \ FORMS += MainWindow.ui \
options.ui \ options.ui \
about.ui \ about.ui \

196
src/trackerlist.h

@ -0,0 +1,196 @@
/*
* 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 <QTreeWidgetItem>
#include <QStringList>
#include <QMenu>
#include <QHash>
#include <QAction>
#include "propertieswidget.h"
#include "trackersadditiondlg.h"
#include "misc.h"
#include "bittorrent.h"
enum TrackerListColumn {COL_URL, COL_STATUS, COL_MSG};
class TrackerList: public QTreeWidget {
Q_OBJECT
private:
PropertiesWidget *properties;
QHash<QString, QTreeWidgetItem*> tracker_items;
public:
TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) {
// 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("Message");
setHeaderItem(new QTreeWidgetItem(header));
}
~TrackerList() {
}
public slots:
void loadTrackers() {
QStringList old_trackers_urls = tracker_items.keys();
// Load trackers from torrent handle
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
QHash<QString, QString> errors = properties->getBTSession()->getTrackersErrors(h.hash());
std::vector<announce_entry> trackers = h.trackers();
std::vector<announce_entry>::iterator it;
for(it = trackers.begin(); it != trackers.end(); it++) {
QStringList item_list;
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);
}
if((*it).verified) {
item->setText(COL_STATUS, tr("Working"));
} else {
if((*it).updating) {
item->setText(COL_STATUS, tr("Updating..."));
} else {
if((*it).fails > 0) {
item->setText(COL_STATUS, tr("Not working"));
} else {
item->setText(COL_STATUS, tr("Not contacted yet"));
}
}
}
item->setText(COL_MSG, errors.value(tracker_url, ""));
}
// 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 askForTrackers(){
QStringList trackers = TrackersAdditionDlg::asForTrackers();
if(!trackers.empty()) {
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) return;
foreach(const QString& tracker, trackers) {
announce_entry url(tracker.toStdString());
url.tier = 0;
h.add_tracker(url);
}
// Reannounce to new trackers
h.force_reannounce();
// Reload tracker list
loadTrackers();
// XXX: I don't think this is necessary now
//BTSession->saveTrackerFile(h.hash());
}
}
void deleteSelectedTrackers(){
QTorrentHandle h = properties->getCurrentTorrent();
if(!h.is_valid()) {
clear();
return;
}
QList<QTreeWidgetItem *> selected_items = selectedItems();
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> trackers = h.trackers();
std::vector<announce_entry>::iterator it = trackers.begin();
while(it != trackers.end()) {
int index = urls_to_remove.indexOf(misc::toQString((*it).url));
if(index >= 0) {
trackers.erase(it);
urls_to_remove.removeAt(index);
} else {
it++;
}
}
h.replace_trackers(trackers);
h.force_reannounce();
// Reload Trackers
loadTrackers();
//XXX: I don't think this is necessary
//BTSession->saveTrackerFile(h.hash());
}
void showTrackerListMenu(QPoint) {
QList<QTreeWidgetItem*> selected_items = selectedItems();
QMenu menu;
// Add actions
QAction *addAct = menu.addAction(QIcon(":/Icons/oxygen/list-add.png"), tr("Add a new tracker"));
QAction *delAct = 0;
if(!selectedItems().isEmpty()) {
delAct = menu.addAction(QIcon(":/Icons/oxygen/list-remove.png"), "Remove tracker");
}
QAction *act = menu.exec(QCursor::pos());
if(act == addAct) {
askForTrackers();
return;
}
if(act == delAct) {
deleteSelectedTrackers();
return;
}
}
};
#endif // TRACKERLIST_H

26
src/TrackersAdditionDlg.h → src/trackersadditiondlg.h

@ -35,27 +35,27 @@
#include <QStringList> #include <QStringList>
#include "ui_trackersAdd.h" #include "ui_trackersAdd.h"
class TrackersAddDlg : public QDialog, private Ui::TrackersAdditionDlg{ class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{
Q_OBJECT Q_OBJECT
public: public:
TrackersAddDlg(QWidget *parent): QDialog(parent){ TrackersAdditionDlg(QWidget *parent=0): QDialog(parent){
setupUi(this); setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
show();
} }
~TrackersAddDlg(){} ~TrackersAdditionDlg(){}
signals: QStringList newTrackers() const {
void TrackersToAdd(QStringList trackers); return trackers_list->toPlainText().trimmed().split("\n");
}
public slots: static QStringList asForTrackers() {
void on_buttonBox_accepted() { QStringList trackers;
QStringList trackers = trackers_list->toPlainText().trimmed().split("\n"); TrackersAdditionDlg dlg;
if(trackers.size()) { if(dlg.exec() == QDialog::Accepted) {
emit TrackersToAdd(trackers); return dlg.newTrackers();
} }
return trackers;
} }
}; };
Loading…
Cancel
Save