|
|
|
/*
|
|
|
|
* 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 <QAction>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QFileDialog>
|
|
|
|
#include <QInputDialog>
|
|
|
|
#include "propertieswidget.h"
|
|
|
|
#include "TransferListWidget.h"
|
|
|
|
#include "torrentPersistentData.h"
|
|
|
|
#include "realprogressbar.h"
|
|
|
|
#include "realprogressbarthread.h"
|
|
|
|
#include "bittorrent.h"
|
|
|
|
#include "PropListDelegate.h"
|
|
|
|
#include "TrackersAdditionDlg.h"
|
|
|
|
#include "TorrentFilesModel.h"
|
|
|
|
#include "peerlistwidget.h"
|
|
|
|
|
|
|
|
#ifdef Q_WS_MAC
|
|
|
|
#define DEFAULT_BUTTON_CSS ""
|
|
|
|
#define SELECTED_BUTTON_CSS "background-color: rgb(255, 208, 105);}"
|
|
|
|
#else
|
|
|
|
#define DEFAULT_BUTTON_CSS "QPushButton {border: 1px solid rgb(85, 81, 91);border-radius: 3px;padding: 2px; margin-left: 3px; margin-right: 3px;}"
|
|
|
|
#define SELECTED_BUTTON_CSS "QPushButton {border: 1px solid rgb(85, 81, 91);border-radius: 3px;padding: 2px;background-color: rgb(255, 208, 105); margin-left: 3px; margin-right: 3px;}"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
PropertiesWidget::PropertiesWidget(QWidget *parent, TransferListWidget *transferList, bittorrent* BTSession): QWidget(parent), transferList(transferList), BTSession(BTSession) {
|
|
|
|
setupUi(this);
|
|
|
|
state = VISIBLE;
|
|
|
|
|
|
|
|
// Buttons stylesheet
|
|
|
|
trackers_button->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
peers_button->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
url_seeds_button->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
files_button->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
main_infos_button->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
|
|
|
|
// Set Properties list model
|
|
|
|
PropListModel = new TorrentFilesModel();
|
|
|
|
filesList->setModel(PropListModel);
|
|
|
|
PropDelegate = new PropListDelegate(0);
|
|
|
|
filesList->setItemDelegate(PropDelegate);
|
|
|
|
|
|
|
|
// QActions
|
|
|
|
actionIgnored = new QAction(tr("Ignored"), this);
|
|
|
|
actionNormal = new QAction(tr("Normal"), this);
|
|
|
|
actionMaximum = new QAction(tr("Maximum"), this);
|
|
|
|
actionHigh = new QAction(tr("High"), this);
|
|
|
|
|
|
|
|
// SIGNAL/SLOTS
|
|
|
|
connect(filesList, SIGNAL(clicked(const QModelIndex&)), filesList, SLOT(edit(const QModelIndex&)));
|
|
|
|
connect(collapseAllButton, SIGNAL(clicked()), filesList, SLOT(collapseAll()));
|
|
|
|
connect(expandAllButton, SIGNAL(clicked()), filesList, SLOT(expandAll()));
|
|
|
|
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(actionNormal, SIGNAL(triggered()), this, SLOT(normalSelection()));
|
|
|
|
connect(actionHigh, SIGNAL(triggered()), this, SLOT(highSelection()));
|
|
|
|
connect(actionMaximum, SIGNAL(triggered()), this, SLOT(maximumSelection()));
|
|
|
|
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(incrementalDownload, SIGNAL(stateChanged(int)), this, SLOT(setIncrementalDownload(int)));
|
|
|
|
connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
|
|
|
|
connect(stackedProperties, SIGNAL(currentChanged(int)), this, SLOT(loadDynamicData()));
|
|
|
|
|
|
|
|
// Downloaded pieces progress bar
|
|
|
|
progressBar = new RealProgressBar(this);
|
|
|
|
progressBar->setForegroundColor(Qt::blue);
|
|
|
|
ProgressHLayout->insertWidget(1, progressBar);
|
|
|
|
// Peers list
|
|
|
|
peersList = new PeerListWidget(this);
|
|
|
|
peerpage_layout->addWidget(peersList);
|
|
|
|
// Pointers init
|
|
|
|
progressBarUpdater = 0;
|
|
|
|
// Dynamic data refresher
|
|
|
|
refreshTimer = new QTimer(this);
|
|
|
|
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
|
|
|
|
refreshTimer->start(10000); // 10sec
|
|
|
|
}
|
|
|
|
|
|
|
|
PropertiesWidget::~PropertiesWidget() {
|
|
|
|
saveSettings();
|
|
|
|
delete refreshTimer;
|
|
|
|
if(progressBarUpdater)
|
|
|
|
delete progressBarUpdater;
|
|
|
|
delete peersList;
|
|
|
|
delete progressBar;
|
|
|
|
delete PropListModel;
|
|
|
|
delete PropDelegate;
|
|
|
|
// Delete QActions
|
|
|
|
delete actionIgnored;
|
|
|
|
delete actionNormal;
|
|
|
|
delete actionMaximum;
|
|
|
|
delete actionHigh;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::reduce() {
|
|
|
|
if(state == VISIBLE) {
|
|
|
|
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
|
|
|
slideSizes = hSplitter->sizes();
|
|
|
|
stackedProperties->setVisible(false);
|
|
|
|
QList<int> sizes;
|
|
|
|
sizes << hSplitter->geometry().height()-30 << 30;
|
|
|
|
hSplitter->setSizes(sizes);
|
|
|
|
hSplitter->handle(1)->setVisible(false);
|
|
|
|
hSplitter->handle(1)->setDisabled(true);
|
|
|
|
state = REDUCED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::slide() {
|
|
|
|
if(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();
|
|
|
|
hash_lbl->clear();
|
|
|
|
comment_lbl->clear();
|
|
|
|
incrementalDownload->setChecked(false);
|
|
|
|
trackersURLS->clear();
|
|
|
|
trackerURL->clear();
|
|
|
|
progressBar->setProgress(QRealArray());
|
|
|
|
wasted->clear();
|
|
|
|
upTotal->clear();
|
|
|
|
dlTotal->clear();
|
|
|
|
peersList->clear();
|
|
|
|
lbl_uplimit->clear();
|
|
|
|
lbl_dllimit->clear();
|
|
|
|
lbl_elapsed->clear();
|
|
|
|
lbl_connections->clear();
|
|
|
|
shareRatio->clear();
|
|
|
|
listWebSeeds->clear();
|
|
|
|
PropListModel->clear();
|
|
|
|
setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
const QTorrentHandle& PropertiesWidget::getCurrentTorrent() const {
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::loadTorrentInfos(QTorrentHandle &_h) {
|
|
|
|
h = _h;
|
|
|
|
if(!h.is_valid()) {
|
|
|
|
clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setEnabled(true);
|
|
|
|
if(progressBarUpdater) {
|
|
|
|
delete progressBarUpdater;
|
|
|
|
progressBarUpdater = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Save path
|
|
|
|
save_path->setText(TorrentPersistentData::getSavePath(h.hash()));
|
|
|
|
// Creation date
|
|
|
|
lbl_creationDate->setText(h.creation_date());
|
|
|
|
// Hash
|
|
|
|
hash_lbl->setText(h.hash());
|
|
|
|
// Comment
|
|
|
|
comment_lbl->setText(h.comment());
|
|
|
|
// Sequential download
|
|
|
|
incrementalDownload->setChecked(TorrentPersistentData::isSequentialDownload(h.hash()));
|
|
|
|
// Trackers
|
|
|
|
loadTrackers();
|
|
|
|
// URL seeds
|
|
|
|
loadUrlSeeds();
|
|
|
|
// downloaded pieces updater
|
|
|
|
progressBarUpdater = new RealProgressBarThread(progressBar, h);
|
|
|
|
progressBarUpdater->start();
|
|
|
|
// List files in torrent
|
|
|
|
PropListModel->clear();
|
|
|
|
PropListModel->setupModelData(h.get_torrent_info());
|
|
|
|
std::vector<int> files_priority = loadFilesPriorities();
|
|
|
|
PropListModel->updateFilesPriorities(files_priority);
|
|
|
|
// Expand first item if possible
|
|
|
|
filesList->expand(PropListModel->index(0, 0));
|
|
|
|
} catch(invalid_handle e) {
|
|
|
|
|
|
|
|
}
|
|
|
|
// Load dynamic data
|
|
|
|
loadDynamicData();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::readSettings() {
|
|
|
|
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
|
|
|
QVariantList contentColsWidths = settings.value(QString::fromUtf8("TorrentProperties/filesColsWidth"), QVariantList()).toList();
|
|
|
|
if(contentColsWidths.empty()) {
|
|
|
|
filesList->header()->resizeSection(0, filesList->width()/2.);
|
|
|
|
} else {
|
|
|
|
for(int i=0; i<contentColsWidths.size(); ++i) {
|
|
|
|
filesList->setColumnWidth(i, contentColsWidths.at(i).toInt());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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(!settings.value("TorrentProperties/Visible", false).toBool()) {
|
|
|
|
reduce();
|
|
|
|
} else {
|
|
|
|
main_infos_button->setStyleSheet(SELECTED_BUTTON_CSS);
|
|
|
|
//setEnabled(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::saveSettings() {
|
|
|
|
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
|
|
|
settings.setValue("TorrentProperties/Visible", state==VISIBLE);
|
|
|
|
QVariantList contentColsWidths;
|
|
|
|
for(int i=0; i<PropListModel->columnCount(); ++i) {
|
|
|
|
contentColsWidths.append(filesList->columnWidth(i));
|
|
|
|
}
|
|
|
|
settings.setValue(QString::fromUtf8("TorrentProperties/filesColsWidth"), contentColsWidths);
|
|
|
|
// Splitter sizes
|
|
|
|
QSplitter *hSplitter = static_cast<QSplitter*>(parentWidget());
|
|
|
|
QList<int> sizes;
|
|
|
|
if(state == VISIBLE)
|
|
|
|
sizes = hSplitter->sizes();
|
|
|
|
else
|
|
|
|
sizes = slideSizes;
|
|
|
|
if(state == VISIBLE)
|
|
|
|
qDebug("Visible");
|
|
|
|
qDebug("Sizes: %d", sizes.size());
|
|
|
|
if(sizes.size() == 2) {
|
|
|
|
settings.setValue(QString::fromUtf8("TorrentProperties/SplitterSizes"), QString::number(sizes.first())+','+QString::number(sizes.last()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::reloadPreferences() {
|
|
|
|
// Take program preferences into consideration
|
|
|
|
peersList->updatePeerHostNameResolutionState();
|
|
|
|
peersList->updatePeerCountryResolutionState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::loadDynamicData() {
|
|
|
|
if(!h.is_valid()) return;
|
|
|
|
if(state != VISIBLE) return;
|
|
|
|
try {
|
|
|
|
// Transfer infos
|
|
|
|
if(stackedProperties->currentIndex() == 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")+")");
|
|
|
|
lbl_uplimit->setText(misc::friendlyUnit(h.upload_limit()));
|
|
|
|
lbl_dllimit->setText(misc::friendlyUnit(h.download_limit()));
|
|
|
|
QString elapsed_txt = misc::userFriendlyDuration(h.active_time());
|
|
|
|
if(h.is_seed()) {
|
|
|
|
elapsed_txt += " ("+tr("Seeding for %1", "e.g. Seeding for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")";
|
|
|
|
}
|
|
|
|
lbl_elapsed->setText(elapsed_txt);
|
|
|
|
lbl_connections->setText(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")");
|
|
|
|
// Update ratio info
|
|
|
|
float ratio;
|
|
|
|
if(h.total_payload_download() == 0){
|
|
|
|
if(h.total_payload_upload() == 0)
|
|
|
|
ratio = 1.;
|
|
|
|
else
|
|
|
|
ratio = 10.; // Max ratio
|
|
|
|
}else{
|
|
|
|
ratio = (double)h.total_payload_upload()/(double)h.total_payload_download();
|
|
|
|
if(ratio > 10.){
|
|
|
|
ratio = 10.;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
shareRatio->setText(QString(QByteArray::number(ratio, 'f', 1)));
|
|
|
|
// Downloaded pieces
|
|
|
|
if(progressBarUpdater)
|
|
|
|
progressBarUpdater->refresh();
|
|
|
|
// Progress
|
|
|
|
progress_lbl->setText(QString::number(h.progress()*100., 'f', 1)+"%");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(stackedProperties->currentIndex() == PEERS_TAB) {
|
|
|
|
// Load peers
|
|
|
|
peersList->loadPeers(h);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(stackedProperties->currentIndex() == FILES_TAB) {
|
|
|
|
// Files progress
|
|
|
|
std::vector<size_type> fp;
|
|
|
|
h.file_progress(fp);
|
|
|
|
PropListModel->updateFilesProgress(fp);
|
|
|
|
}
|
|
|
|
} catch(invalid_handle e) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::setIncrementalDownload(int checkboxState) {
|
|
|
|
if(!h.is_valid()) return;
|
|
|
|
h.set_sequential_download(checkboxState == Qt::Checked);
|
|
|
|
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(){
|
|
|
|
QStringList already_added;
|
|
|
|
listWebSeeds->clear();
|
|
|
|
QVariantList url_seeds = TorrentPersistentData::getUrlSeeds(h.hash());
|
|
|
|
foreach(const QVariant &var_url_seed, url_seeds){
|
|
|
|
QString url_seed = var_url_seed.toString();
|
|
|
|
if(!url_seed.isEmpty()) {
|
|
|
|
new QListWidgetItem(url_seed, listWebSeeds);
|
|
|
|
already_added << url_seed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Load the hard-coded url seeds
|
|
|
|
QStringList hc_seeds = h.url_seeds();
|
|
|
|
// Add hard coded url seeds
|
|
|
|
foreach(const QString &hc_seed, hc_seeds){
|
|
|
|
if(!already_added.contains(hc_seed)){
|
|
|
|
new QListWidgetItem(hc_seed, listWebSeeds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tab buttons */
|
|
|
|
QPushButton* PropertiesWidget::getButtonFromIndex(int index) {
|
|
|
|
switch(index) {
|
|
|
|
case TRACKERS_TAB:
|
|
|
|
return trackers_button;
|
|
|
|
case PEERS_TAB:
|
|
|
|
return peers_button;
|
|
|
|
case URLSEEDS_TAB:
|
|
|
|
return url_seeds_button;
|
|
|
|
case FILES_TAB:
|
|
|
|
return files_button;
|
|
|
|
default:
|
|
|
|
return main_infos_button;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::on_main_infos_button_clicked() {
|
|
|
|
if(state == VISIBLE && stackedProperties->currentIndex() == MAIN_TAB) {
|
|
|
|
reduce();
|
|
|
|
} else {
|
|
|
|
slide();
|
|
|
|
getButtonFromIndex(stackedProperties->currentIndex())->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
stackedProperties->setCurrentIndex(MAIN_TAB);
|
|
|
|
main_infos_button->setStyleSheet(SELECTED_BUTTON_CSS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::on_trackers_button_clicked() {
|
|
|
|
if(state == VISIBLE && stackedProperties->currentIndex() == TRACKERS_TAB) {
|
|
|
|
reduce();
|
|
|
|
} else {
|
|
|
|
slide();
|
|
|
|
getButtonFromIndex(stackedProperties->currentIndex())->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
stackedProperties->setCurrentIndex(TRACKERS_TAB);
|
|
|
|
trackers_button->setStyleSheet(SELECTED_BUTTON_CSS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::on_peers_button_clicked() {
|
|
|
|
if(state == VISIBLE && stackedProperties->currentIndex() == PEERS_TAB) {
|
|
|
|
reduce();
|
|
|
|
} else {
|
|
|
|
slide();
|
|
|
|
getButtonFromIndex(stackedProperties->currentIndex())->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
stackedProperties->setCurrentIndex(PEERS_TAB);
|
|
|
|
peers_button->setStyleSheet(SELECTED_BUTTON_CSS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::on_url_seeds_button_clicked() {
|
|
|
|
if(state == VISIBLE && stackedProperties->currentIndex() == URLSEEDS_TAB) {
|
|
|
|
reduce();
|
|
|
|
} else {
|
|
|
|
slide();
|
|
|
|
getButtonFromIndex(stackedProperties->currentIndex())->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
stackedProperties->setCurrentIndex(URLSEEDS_TAB);
|
|
|
|
url_seeds_button->setStyleSheet(SELECTED_BUTTON_CSS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::on_files_button_clicked() {
|
|
|
|
if(state == VISIBLE && stackedProperties->currentIndex() == FILES_TAB) {
|
|
|
|
reduce();
|
|
|
|
} else {
|
|
|
|
slide();
|
|
|
|
getButtonFromIndex(stackedProperties->currentIndex())->setStyleSheet(DEFAULT_BUTTON_CSS);
|
|
|
|
stackedProperties->setCurrentIndex(FILES_TAB);
|
|
|
|
files_button->setStyleSheet(SELECTED_BUTTON_CSS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<int> PropertiesWidget::loadFilesPriorities(){
|
|
|
|
std::vector<int> fp;
|
|
|
|
QVariantList files_priority = TorrentPersistentData::getFilesPriority(h.hash());
|
|
|
|
if(files_priority.empty()) {
|
|
|
|
for(int i=0; i<h.num_files(); ++i) {
|
|
|
|
fp.push_back(1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
foreach(const QVariant &var_prio, files_priority) {
|
|
|
|
int priority = var_prio.toInt();
|
|
|
|
if( priority < 0 || priority > 7){
|
|
|
|
// Normal priority as default
|
|
|
|
priority = 1;
|
|
|
|
}
|
|
|
|
fp.push_back(priority);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::displayFilesListMenu(const QPoint&){
|
|
|
|
//if(h.get_torrent_info().num_files() == 1) return;
|
|
|
|
QMenu myFilesLlistMenu(this);
|
|
|
|
//QModelIndex index;
|
|
|
|
// Enable/disable pause/start action given the DL state
|
|
|
|
//QModelIndexList selectedIndexes = filesList->selectionModel()->selectedIndexes();
|
|
|
|
myFilesLlistMenu.setTitle(tr("Priority"));
|
|
|
|
myFilesLlistMenu.addAction(actionIgnored);
|
|
|
|
myFilesLlistMenu.addAction(actionNormal);
|
|
|
|
myFilesLlistMenu.addAction(actionHigh);
|
|
|
|
myFilesLlistMenu.addAction(actionMaximum);
|
|
|
|
// Call menu
|
|
|
|
myFilesLlistMenu.exec(QCursor::pos());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::ignoreSelection(){
|
|
|
|
QModelIndexList selectedIndexes = filesList->selectionModel()->selectedIndexes();
|
|
|
|
foreach(const QModelIndex &index, selectedIndexes){
|
|
|
|
if(index.column() == PRIORITY){
|
|
|
|
if(PropListModel->data(index) != QVariant(IGNORED)){
|
|
|
|
PropListModel->setData(index, QVariant(IGNORED));
|
|
|
|
filteredFilesChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::normalSelection(){
|
|
|
|
QModelIndexList selectedIndexes = filesList->selectionModel()->selectedIndexes();
|
|
|
|
foreach(const QModelIndex &index, selectedIndexes){
|
|
|
|
if(index.column() == PRIORITY){
|
|
|
|
if(PropListModel->data(index) != QVariant(NORMAL)){
|
|
|
|
PropListModel->setData(index, QVariant(NORMAL));
|
|
|
|
filteredFilesChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::highSelection(){
|
|
|
|
QModelIndexList selectedIndexes = filesList->selectionModel()->selectedIndexes();
|
|
|
|
foreach(const QModelIndex &index, selectedIndexes){
|
|
|
|
if(index.column() == PRIORITY){
|
|
|
|
if(PropListModel->data(index) != QVariant(HIGH)){
|
|
|
|
PropListModel->setData(index, QVariant(HIGH));
|
|
|
|
filteredFilesChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::maximumSelection(){
|
|
|
|
QModelIndexList selectedIndexes = filesList->selectionModel()->selectedIndexes();
|
|
|
|
foreach(const QModelIndex &index, selectedIndexes){
|
|
|
|
if(index.column() == PRIORITY){
|
|
|
|
if(PropListModel->data(index) != QVariant(MAXIMUM)){
|
|
|
|
PropListModel->setData(index, QVariant(MAXIMUM));
|
|
|
|
filteredFilesChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::askWebSeed(){
|
|
|
|
bool ok;
|
|
|
|
// Ask user for a new url seed
|
|
|
|
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", url_seed.toLocal8Bit().data());
|
|
|
|
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);
|
|
|
|
TorrentPersistentData::saveUrlSeeds(h);
|
|
|
|
// Refresh the seeds list
|
|
|
|
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(){
|
|
|
|
QList<QListWidgetItem *> selectedItems = listWebSeeds->selectedItems();
|
|
|
|
bool change = false;
|
|
|
|
foreach(QListWidgetItem *item, selectedItems){
|
|
|
|
QString url_seed = item->text();
|
|
|
|
h.remove_url_seed(url_seed);
|
|
|
|
change = true;
|
|
|
|
}
|
|
|
|
if(change){
|
|
|
|
// Save them to disk
|
|
|
|
TorrentPersistentData::saveUrlSeeds(h);
|
|
|
|
// Refresh list
|
|
|
|
loadUrlSeeds();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
|
|
|
qDebug("Saving pieces priorities");
|
|
|
|
std::vector<int> priorities = PropListModel->getFilesPriorities(h.get_torrent_info().num_files());
|
|
|
|
h.prioritize_files(priorities);
|
|
|
|
TorrentPersistentData::saveFilesPriority(h);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PropertiesWidget::on_changeSavePathButton_clicked() {
|
|
|
|
QString dir;
|
|
|
|
QDir saveDir(h.save_path());
|
|
|
|
if(saveDir.exists()){
|
|
|
|
dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), h.save_path());
|
|
|
|
}else{
|
|
|
|
dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
|
|
|
|
}
|
|
|
|
if(!dir.isNull()){
|
|
|
|
// Check if savePath exists
|
|
|
|
QDir savePath(dir);
|
|
|
|
if(!savePath.exists()){
|
|
|
|
if(!savePath.mkpath(savePath.path())){
|
|
|
|
QMessageBox::critical(0, tr("Save path creation error"), tr("Could not create the save path"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Save savepath
|
|
|
|
TorrentPersistentData::saveSavePath(h.hash(), savePath.path());
|
|
|
|
// Actually move storage
|
|
|
|
if(!BTSession->useTemporaryFolder() || h.is_seed())
|
|
|
|
h.move_storage(savePath.path());
|
|
|
|
// Update save_path in dialog
|
|
|
|
save_path->setText(savePath.path());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PropertiesWidget::filteredFilesChanged() {
|
|
|
|
if(h.is_valid()) {
|
|
|
|
savePiecesPriorities();
|
|
|
|
transferList->updateTorrentSizeAndProgress(h.hash());
|
|
|
|
}
|
|
|
|
}
|