- Huge forward porting of all previous properties features to the new properties panel (probably very buggy but most of the code should be there and it compiles)

This commit is contained in:
Christophe Dumez 2009-11-09 08:56:21 +00:00
parent 6ecde51e45
commit 9bc90fc7b2
12 changed files with 572 additions and 2125 deletions

View file

@ -30,15 +30,25 @@
#include <QTimer>
#include <QListWidgetItem>
#include <QStandardItemModel>
#include <QStandardItem>
#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 "arborescence.h"
#include "bittorrent.h"
#include "PropListDelegate.h"
#include "TrackersAdditionDlg.h"
#define DEFAULT_BUTTON_CSS "QPushButton {border: 1px solid rgb(85, 81, 91);border-radius: 3px;padding: 2px;}"
#define SELECTED_BUTTON_CSS "QPushButton {border: 1px solid rgb(85, 81, 91);border-radius: 3px;padding: 2px;background-color: rgb(255, 208, 105);}"
@ -54,15 +64,51 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, TransferListWidget *transfer
setEnabled(false);
}
// Set Properties list model
PropListModel = new QStandardItemModel(0,5);
PropListModel->setHeaderData(NAME, Qt::Horizontal, tr("File name"));
PropListModel->setHeaderData(SIZE, Qt::Horizontal, tr("Size"));
PropListModel->setHeaderData(PROGRESS, Qt::Horizontal, tr("Progress"));
PropListModel->setHeaderData(PRIORITY, Qt::Horizontal, tr("Priority"));
filesList->setModel(PropListModel);
filesList->hideColumn(INDEX);
PropDelegate = new PropListDelegate(0);
filesList->setItemDelegate(PropDelegate);
// QActions
actionIgnored = new QAction(this);
actionNormal = new QAction(this);
actionMaximum = new QAction(this);
actionHigh = new QAction(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(PropListModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updatePriorities(QStandardItem*)));
connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged()));
// Downloaded pieces progress bar
progressBar = new RealProgressBar(this);
progressBar->setForegroundColor(Qt::blue);
progressBarVbox = new QVBoxLayout(RealProgressBox);
progressBarVbox->addWidget(progressBar);
// Pointers init
progressBarUpdater = 0;
arb = 0;
// Dynamic data refresher
refreshTimer = new QTimer(this);
connect(refreshTimer, SIGNAL(timeout()), this, SLOT(loadDynamicData()));
@ -77,6 +123,14 @@ PropertiesWidget::~PropertiesWidget() {
delete progressBarUpdater;
delete progressBar;
delete progressBarVbox;
delete PropListModel;
if(arb)
delete arb;
// Delete QActions
delete actionIgnored;
delete actionNormal;
delete actionMaximum;
delete actionHigh;
}
void PropertiesWidget::reduce() {
@ -116,9 +170,14 @@ void PropertiesWidget::loadTorrentInfos(QTorrentHandle &_h) {
return;
}
setEnabled(true);
if(progressBarUpdater)
if(progressBarUpdater) {
delete progressBarUpdater;
progressBarUpdater = 0;
progressBarUpdater = 0;
}
if(arb != 0) {
delete arb;
arb = 0;
}
try {
// Save path
save_path->setText(TorrentPersistentData::getSavePath(h.hash()));
@ -140,6 +199,13 @@ void PropertiesWidget::loadTorrentInfos(QTorrentHandle &_h) {
// downloaded pieces updater
progressBarUpdater = new RealProgressBarThread(progressBar, h);
progressBarUpdater->start();
// Create arborescence (Tree representation of files in the torrent)
std::vector<size_type> fp;
h.file_progress(fp);
std::vector<int> files_priority = loadFilesPriorities();
// List files in torrent
arborescence *arb = new arborescence(h.get_torrent_info(), fp, files_priority);
addFilesToTree(arb->getRoot(), PropListModel->invisibleRootItem());
} catch(invalid_handle e) {
}
@ -293,3 +359,428 @@ void PropertiesWidget::on_files_button_clicked() {
files_button->setStyleSheet(SELECTED_BUTTON_CSS);
}
}
// priority is the new priority of given item
void PropertiesWidget::updateParentsPriority(QStandardItem *item, int priority) {
QStandardItem *parent = item->parent();
if(!parent) return;
// Check if children have different priorities
// then folder must have NORMAL priority
unsigned int rowCount = parent->rowCount();
for(unsigned int i=0; i<rowCount; ++i) {
if(parent->child(i, PRIORITY)->text().toInt() != priority) {
QStandardItem *grandFather = parent->parent();
if(!grandFather) {
grandFather = PropListModel->invisibleRootItem();
}
QStandardItem *parentPrio = grandFather->child(parent->row(), PRIORITY);
if(parentPrio->text().toInt() != NORMAL) {
parentPrio->setText(misc::toQString(NORMAL));
setItemColor(parentPrio->index(), "green");
// Recursively update ancesters of this parent too
updateParentsPriority(grandFather->child(parent->row()), priority);
}
return;
}
}
// All the children have the same priority
// Parent folder should have the same priority too
QStandardItem *grandFather = parent->parent();
if(!grandFather) {
grandFather = PropListModel->invisibleRootItem();
}
QStandardItem *parentPrio = grandFather->child(parent->row(), PRIORITY);
if(parentPrio->text().toInt() != priority) {
parentPrio->setText(misc::toQString(priority));
if(priority == IGNORED)
setItemColor(parentPrio->index(), "red");
else
setItemColor(parentPrio->index(), "green");
// Recursively update ancesters of this parent too
updateParentsPriority(grandFather->child(parent->row()), priority);
}
}
void PropertiesWidget::updateChildrenPriority(QStandardItem *item, int priority) {
QStandardItem *parent = item->parent();
if(!parent) {
parent = PropListModel->invisibleRootItem();
}
parent = parent->child(item->row());
unsigned int rowCount = parent->rowCount();
for(unsigned int i=0; i<rowCount; ++i) {
QStandardItem * childPrio = parent->child(i, PRIORITY);
if(childPrio->text().toInt() != priority) {
childPrio->setText(misc::toQString(priority));
if(priority == IGNORED)
setItemColor(childPrio->index(), "red");
else
setItemColor(childPrio->index(), "green");
// recursively update children of this child too
updateChildrenPriority(parent->child(i), priority);
}
}
}
void PropertiesWidget::updatePriorities(QStandardItem *item) {
qDebug("Priority changed");
// First we disable the signal/slot on item edition
// temporarily so that it doesn't mess with our manual updates
disconnect(PropListModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updatePriorities(QStandardItem*)));
QStandardItem *parent = item->parent();
if(!parent) {
parent = PropListModel->invisibleRootItem();
}
int priority = parent->child(item->row(), PRIORITY)->text().toInt();
if(priority == IGNORED)
setItemColor(item->index(), "red");
else
setItemColor(item->index(), "green");
// Update parents priorities
updateParentsPriority(item, priority);
// If this is not a directory, then there are
// no children to update
if(parent->child(item->row(), INDEX)->text().toInt() == -1) {
// Updating children
qDebug("Priority changed for a folder to %d", priority);
updateChildrenPriority(item, priority);
}
// Reconnect the signal/slot on item edition so that we
// get future updates
connect(PropListModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updatePriorities(QStandardItem*)));
}
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;
}
bool PropertiesWidget::allFiltered() const {
unsigned int nbRows = PropListModel->rowCount();
for(unsigned int i=0; i<nbRows; ++i){
if(PropListModel->data(PropListModel->index(i, PRIORITY)).toInt() != IGNORED)
return false;
}
return true;
}
void PropertiesWidget::getPriorities(QStandardItem *parent, int *priorities) {
qDebug("In getPriorities");
unsigned int nbRows = parent->rowCount();
for(unsigned int i=0; i<nbRows; ++i){
QStandardItem *item = parent->child(i, INDEX);
int index = item->text().toInt();
if(index < 0) {
getPriorities(parent->child(i, NAME), priorities);
} else {
item = parent->child(i, PRIORITY);
priorities[index] = item->text().toInt();
qDebug("File at index %d has priority %d", index, priorities[index]);
}
}
}
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));
transferList->updateTorrentSizeAndProgress(h.hash());
setItemColor(index, "red");
}
}
}
}
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));
transferList->updateTorrentSizeAndProgress(h.hash());
setItemColor(index, "green");
}
}
}
}
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));
transferList->updateTorrentSizeAndProgress(h.hash());
setItemColor(index, "green");
}
}
}
}
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));
transferList->updateTorrentSizeAndProgress(h.hash());
setItemColor(index, "green");
}
}
}
}
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());
}
}
void PropertiesWidget::setItemColor(QModelIndex index, QString color){
for(int i=0; i<PropListModel->columnCount(); ++i){
PropListModel->setData(index.sibling(index.row(), i), QVariant(QColor(color)), Qt::ForegroundRole);
}
}
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())
transferList->updateTorrentSizeAndProgress(h.hash());
}
void PropertiesWidget::addFilesToTree(torrent_file *root, QStandardItem *parent) {
QList<QStandardItem*> child;
// Name
QStandardItem *first;
if(root->isDir()) {
first = new QStandardItem(QIcon(":/Icons/oxygen/folder.png"), root->name());
} else {
first = new QStandardItem(QIcon(":/Icons/oxygen/file.png"), root->name());
}
child << first;
// Size
child << new QStandardItem(misc::toQString(root->getSize()));
// Progress
child << new QStandardItem(misc::toQString(root->getProgress()));
// Prio
child << new QStandardItem(misc::toQString(root->getPriority()));
// INDEX
child << new QStandardItem(misc::toQString(root->getIndex()));
// Add the child to the tree
parent->appendRow(child);
// Set row color
if(root->getPriority() == IGNORED)
setItemColor(first->index(), "red");
else
setItemColor(first->index(), "green");
// Add childs
foreach(torrent_file *childFile, root->getChildren()) {
addFilesToTree(childFile, first);
}
}