diff --git a/Changelog b/Changelog index f341278eb..006cf93f4 100644 --- a/Changelog +++ b/Changelog @@ -39,6 +39,7 @@ - FEATURE: Added an option to set the max number of uploads per torrent - FEATURE: Added an option to automatically delete torrents when they reach a given ratio (>= 1.0) - FEATURE: Added an option to display current transfer speeds in title bar + - FEATURE: Torrent content is now displayed as a tree - I18N: Added Hungarian translation - I18N: Added Brazilian translation - BUGFIX: Progress of paused torrents is now correct on restart diff --git a/TODO b/TODO index d156b8896..00c0fd2b2 100644 --- a/TODO +++ b/TODO @@ -49,11 +49,12 @@ - Fix all (or almost all) opened bugs in bug tracker - Keep documention up to date - Windows port (Chris - Peerkoel) +- debug new torrent content selection * beta 7 - update doc for plugins (and add screenies) - update doc for options - - Review torrent content selection - * check the one in ktorrent + - See bug about negative ETA + - Fix search engines saving - Translations update (IN PROGRESS) - Wait that http://pastebin.ca/690649 is fixed @@ -99,6 +100,7 @@ beta6->beta7 changelog: - FEATURE: Articles in a RSS feed are now ordered by date (newer at the top) - FEATURE: Read articles in a feed are not resetted when the feed is refreshed anymore - FEATURE: Allow to install plugins from their url +- FEATURE: torrent content is now displayed as a tree - BUGFIX: In torrent content, it is now easier to filter all torrents using right click menu - BUGFIX: Updated man page / README / INSTALL - BUGFIX: Paused torrents could be displayed as connected for a sec after checking diff --git a/src/Icons/file.png b/src/Icons/file.png new file mode 100644 index 000000000..3bf4121aa Binary files /dev/null and b/src/Icons/file.png differ diff --git a/src/Icons/folder.png b/src/Icons/folder.png new file mode 100644 index 000000000..c8ea7e73e Binary files /dev/null and b/src/Icons/folder.png differ diff --git a/src/PropListDelegate.h b/src/PropListDelegate.h index a4202ca08..2ae5151e4 100644 --- a/src/PropListDelegate.h +++ b/src/PropListDelegate.h @@ -38,6 +38,7 @@ #define SIZE 1 #define PROGRESS 2 #define PRIORITY 3 +#define INDEX 4 #define IGNORED 0 #define NORMAL 1 @@ -115,7 +116,6 @@ class PropListDelegate: public QItemDelegate { QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const { if(index.column() != PRIORITY) return 0; - if(onlyOneItem(index)) return 0; QComboBox* editor = new QComboBox(parent); editor->setFocusPolicy(Qt::StrongFocus); editor->addItem(tr("Ignored")); @@ -128,6 +128,7 @@ class PropListDelegate: public QItemDelegate { void setEditorData(QWidget *editor, const QModelIndex &index) const { unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt(); QComboBox *combobox = static_cast(editor); + qDebug("Set Editor data: Prio is %d", val); switch(val){ case IGNORED: combobox->setCurrentIndex(0); @@ -156,28 +157,11 @@ class PropListDelegate: public QItemDelegate { return textRect.size(); } - bool onlyOneItem(const QModelIndex& index) const { - const QAbstractItemModel *model = index.model(); - unsigned int nbRows = model->rowCount(); - if(nbRows == 1) return true; - for(unsigned int i=0; idata(model->index(i, PRIORITY)).toInt()) return false; - } - return true; - } - public slots: void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *combobox = static_cast(editor); int value = combobox->currentIndex(); qDebug("Setting combobox value in index: %d", value); - QString color; - if(value) { - color = QString::fromUtf8("green"); - } else { - color = QString::fromUtf8("red"); - } unsigned short old_val = index.model()->data(index, Qt::DisplayRole).toInt(); switch(value){ case 0: @@ -215,14 +199,12 @@ class PropListDelegate: public QItemDelegate { *filteredFilesChanged = true; } } - for(int i=0; icolumnCount(); ++i){ - model->setData(model->index(index.row(), i), QVariant(QColor(color)), Qt::ForegroundRole); - } } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { editor->setGeometry(option.rect); } + }; #endif diff --git a/src/addTorrentDialog.ui b/src/addTorrentDialog.ui index b4e1a0986..ce4cfeb86 100644 --- a/src/addTorrentDialog.ui +++ b/src/addTorrentDialog.ui @@ -5,20 +5,29 @@ 0 0 - 440 - 389 + 511 + 441 Torrent addition dialog - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + @@ -48,12 +57,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -108,12 +126,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + diff --git a/src/arborescence.h b/src/arborescence.h new file mode 100644 index 000000000..452280ba1 --- /dev/null +++ b/src/arborescence.h @@ -0,0 +1,251 @@ +/* + * 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. + * + * Contact : chris@qbittorrent.org + */ + +#ifndef ARBORESCENCE_H +#define ARBORESCENCE_H + +#include +#include +#include +#include "misc.h" + +class file { + private: + file *parent; + bool is_dir; + QString rel_path; + QList children; + size_type size; + float progress; + int priority; + int index; // Index in torrent_info + + public: + file(file *parent, QString path, bool dir, size_type size=0, float progress=0., int priority=1, int index=-1): parent(parent), is_dir(dir), size(size), progress(progress), priority(priority), index(index){ + rel_path = QDir::cleanPath(path); + if(parent) { + parent->updateProgress(); + parent->updatePriority(priority); + } + } + + ~file() { + qDeleteAll(children); + } + + QString path() const { + return rel_path; + } + + QString name() const { + return rel_path.split(QDir::separator()).last(); + } + + void updateProgress() { + Q_ASSERT(is_dir); + float sum = 0; + file *child; + foreach(child, children) { + sum += child->getProgress(); + } + progress = sum / (float)children.size(); + } + + void updatePriority(int prio) { + Q_ASSERT(is_dir); + file *child; + foreach(child, children) { + if(child->getPriority() != prio) return; + } + priority = prio; + } + + int getPriority() const { + return priority; + } + + size_type getSize() const { + return size; + } + + float getProgress() const { + return progress; + } + + int getIndex() const { + return index; + } + + bool isDir() const { + return is_dir; + } + + bool hasChildren() const { + return (!children.isEmpty()); + } + + QList getChildren() const { + return children; + } + + file* getChild(QString fileName) const { + Q_ASSERT(is_dir); + file* f; + foreach(f, children) { + if(f->name() == fileName) return f; + } + return 0; + } + + void addBytes(size_type b) { + size += b; + if(parent) + parent->addBytes(b); + } + + file* addChild(QString fileName, bool dir, size_type size=0, float progress=0., int priority=1, int index = -1) { + Q_ASSERT(is_dir); + qDebug("Adding a new child of size: %ld", (long)size); + file *f = new file(this, QDir::cleanPath(rel_path+QDir::separator()+fileName), dir, size, progress, priority, index); + children << f; + if(size) { + addBytes(size); + } + return f; + } + + bool removeFromFS(QString saveDir) { + QString full_path = saveDir + QDir::separator() + rel_path; + if(!QFile::exists(full_path)) { + qDebug("%s does not exist, no need to remove it", full_path.toUtf8().data()); + return true; + } + bool success = true; + file *f; + foreach(f, children) { + success = success && f->removeFromFS(saveDir); + success = false; + } + if(is_dir) { + qDebug("trying to remove directory: %s", full_path.toUtf8().data()); + QDir dir(full_path); + dir.rmdir(full_path); + } else { + qDebug("trying to remove file: %s", full_path.toUtf8().data()); + success = success && QFile::remove(full_path); + } + return success; + } +}; + +class arborescence { + private: + file *root; + + public: + arborescence(torrent_info t) { + torrent_info::file_iterator fi = t.begin_files(); + if(t.num_files() > 1) { + root = new file(0, misc::toQString(t.name()), true); + } else { + // XXX: Will crash if there is no file in torrent + root = new file(0, misc::toQString(t.name()), false, fi->size, 0); + return; + } + int i = 0; + while(fi != t.end_files()) { + QString path = QDir::cleanPath(misc::toQString(fi->path.string())); + addFile(path, fi->size, i); + fi++; + ++i; + } + qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize()); + Q_ASSERT(root->getSize() == t.total_size()); + } + + arborescence(torrent_info t, std::vector fp, int *prioritiesTab) { + torrent_info::file_iterator fi = t.begin_files(); + if(t.num_files() > 1) { + root = new file(0, misc::toQString(t.name()), true); + } else { + // XXX: Will crash if there is no file in torrent + root = new file(0, misc::toQString(t.name()), false, fi->size, fp[0], prioritiesTab[0], 0); + return; + } + int i = 0; + while(fi != t.end_files()) { + QString path = QDir::cleanPath(misc::toQString(fi->path.string())); + addFile(path, fi->size, i, fp[i], prioritiesTab[i]); + fi++; + ++i; + } + qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize()); + Q_ASSERT(root->getSize() == t.total_size()); + } + + ~arborescence() { + delete root; + } + + file* getRoot() const { + return root; + } + + bool removeFromFS(QString saveDir) { + if(!QFile::exists(saveDir+QDir::separator()+root->path())) return true; + bool success = root->removeFromFS(saveDir); + QDir root_dir(root->path()); + root_dir.rmdir(saveDir+QDir::separator()+root->path()); + return success; + } + + protected: + void addFile(QString path, size_type file_size, int index, float progress=0., int priority=1) { + Q_ASSERT(root->isDir()); + path = QDir::cleanPath(path); + Q_ASSERT(path.startsWith(root->path())); + QString relative_path = path.remove(0, root->path().size()); + if(relative_path.at(0) ==QDir::separator()) + relative_path.remove(0, 1); + QStringList fileNames = relative_path.split(QDir::separator()); + QString fileName; + file *dad = root; + unsigned int nb_i = 0; + unsigned int size = fileNames.size(); + foreach(fileName, fileNames) { + ++nb_i; + if(fileName == ".") continue; + file* child = dad->getChild(fileName); + if(!child) { + if(nb_i != size) { + // Folder + child = dad->addChild(fileName, true); + } else { + // File + child = dad->addChild(fileName, false, file_size, progress, priority, index); + } + } + dad = child; + } + } +}; + +#endif diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index b5709fdfc..26b3366c5 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -208,9 +208,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) { } QString savePath = h.save_path(); QString fileName = h.name(); - QStringList files_path; + arborescence *files_arb = 0; if(permanent){ - files_path = h.files_path(); + files_arb = new arborescence(h.get_torrent_info()); } // Remove it from session s->remove_torrent(h.get_torrent_handle()); @@ -241,11 +241,11 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) { std::cerr << "Error: Torrent " << hash.toStdString() << " is neither in finished or unfinished list\n"; } } - if(permanent) { + if(permanent && files_arb != 0) { // Remove from Hard drive qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName)); // Deleting in a thread to avoid GUI freeze - deleter->deleteTorrent(savePath, files_path); + deleter->deleteTorrent(savePath, files_arb); } } diff --git a/src/deleteThread.h b/src/deleteThread.h index af7c70c35..37e990082 100644 --- a/src/deleteThread.h +++ b/src/deleteThread.h @@ -28,17 +28,17 @@ #include #include -#include "misc.h" +#include "arborescence.h" class subDeleteThread : public QThread { Q_OBJECT private: QString save_path; - QStringList files_path; + arborescence *arb; bool abort; public: - subDeleteThread(QObject *parent, QString save_path, QStringList files_path) : QThread(parent), save_path(save_path), files_path(files_path), abort(false){} + subDeleteThread(QObject *parent, QString saveDir, arborescence *arb) : QThread(parent), save_path(saveDir), arb(arb), abort(false){} ~subDeleteThread(){ abort = true; @@ -52,10 +52,11 @@ class subDeleteThread : public QThread { protected: void run(){ - if(misc::removeTorrentSavePath(save_path, files_path)) + if(arb->removeFromFS(save_path)) emit deletionSuccessST(this); else emit deletionFailureST(this); + delete arb; } }; @@ -63,7 +64,7 @@ class deleteThread : public QThread { Q_OBJECT private: - QList > torrents_list; + QList > torrents_list; QMutex mutex; QWaitCondition condition; bool abort; @@ -81,9 +82,10 @@ class deleteThread : public QThread { wait(); } - void deleteTorrent(QString save_path, QStringList files_path){ + void deleteTorrent(QString saveDir, arborescence *arb){ + qDebug("deleteThread called"); QMutexLocker locker(&mutex); - torrents_list << QPair(save_path, files_path); + torrents_list << QPair(saveDir, arb); if(!isRunning()){ start(); }else{ @@ -98,7 +100,7 @@ class deleteThread : public QThread { return; mutex.lock(); if(torrents_list.size() != 0){ - QPair torrent = torrents_list.takeFirst(); + QPair torrent = torrents_list.takeFirst(); mutex.unlock(); subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second); subThreads << st; diff --git a/src/icons.qrc b/src/icons.qrc index c042536d8..d589fe75d 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -1,17 +1,18 @@ + Icons/locale.png Icons/time.png Icons/connection.png - Icons/locale.png Icons/newmsg.png + Icons/stare.png Icons/url.png + Icons/qbittorrent32.png Icons/button_ok.png - Icons/stare.png Icons/log.png - Icons/qbittorrent32.png + Icons/qbittorrent16.png Icons/downarrow.png Icons/description.png - Icons/qbittorrent16.png + Icons/button_cancel.png Icons/systemtray.png Icons/unhappy.png Icons/filter.png @@ -27,7 +28,7 @@ Icons/sphere2.png Icons/smile.png Icons/loading.png - Icons/button_cancel.png + Icons/file.png Icons/qbittorrent22.png Icons/proxy.png Icons/add_folder.png @@ -46,25 +47,26 @@ Icons/star.png Icons/configure.png Icons/download.png + Icons/folder.png + Icons/flags/netherlands.png Icons/flags/portugal.png Icons/flags/france.png Icons/flags/ukraine.png - Icons/flags/united_kingdom.png Icons/flags/germany.png + Icons/flags/united_kingdom.png Icons/flags/russia.png - Icons/flags/netherlands.png + Icons/flags/south_korea.png Icons/flags/slovakia.png Icons/flags/spain.png Icons/flags/finland.png - Icons/flags/spain_catalunya.png Icons/flags/poland.png + Icons/flags/spain_catalunya.png Icons/flags/hungary.png Icons/flags/norway.png Icons/flags/denmark.png Icons/flags/italy.png Icons/flags/china.png Icons/flags/brazil.png - Icons/flags/south_korea.png Icons/flags/turkey.png Icons/flags/sweden.png Icons/flags/bulgaria.png diff --git a/src/misc.h b/src/misc.h index c199bb8dd..84da99f7e 100644 --- a/src/misc.h +++ b/src/misc.h @@ -34,7 +34,6 @@ #include #include -#include "qtorrenthandle.h" using namespace libtorrent; /* Miscellaneaous functions that can be useful */ @@ -205,60 +204,6 @@ class misc : public QObject{ // return true; // } - // safe function to remove a torrent from hard-drive - static bool removeTorrentSavePath(QString savePath, QStringList filesPath) { - bool success = true; - QDir saveDir(savePath); - QString path; - // Check how many file there are - if(filesPath.size() == 1){ - // Only one file, not in a folder - path = filesPath.first(); - if(QFile::exists(path)) { - if(QFile::remove(path)){ - qDebug("Deleted only file in torrent at %s", path.toUtf8().data()); - } else { - std::cerr << "Could not delete only file in torrent at " << path.toUtf8().data() << '\n'; - success = false; - } - }else{ - // File didn't exist, nothing to do - qDebug("Only file %s did not exist, nothing to delete", path.toUtf8().data()); - } - // Try to remove parent folder if empty (and not save_dir) - QFileInfo fi(path); - QDir parentFolder = fi.absoluteDir(); - while(parentFolder != saveDir) { - qDebug("trying to remove parent folder: %s", parentFolder.absolutePath().toUtf8().data()); - if(!saveDir.rmdir(parentFolder.absolutePath())) break; - parentFolder.cdUp(); - } - return success; - } - // Torrent has several files in a subFolder - foreach(path, filesPath) { - if(QFile::exists(path)) { - if(QFile::remove(path)){ - qDebug("Deleted file in torrent at %s", path.toUtf8().data()); - } else { - std::cerr << "Could not delete file in torrent at " << path.toUtf8().data() << '\n'; - success = false; - } - } else { - qDebug("File %s did not exist, nothing to delete", path.toUtf8().data()); - } - // Try to remove parent folder if empty (and not save_dir) - QFileInfo fi(path); - QDir parentFolder = fi.absoluteDir(); - while(parentFolder != saveDir) { - qDebug("trying to remove parent folder: %s", parentFolder.absolutePath().toUtf8().data()); - if(!saveDir.rmdir(parentFolder.absolutePath())) break; - parentFolder.cdUp(); - } - } - return success; - } - static QString findFileInDir(QString dir_path, QString fileName) { QDir dir(dir_path); if(dir.exists(fileName)) { diff --git a/src/properties.ui b/src/properties.ui index 1e2c90ac0..5d4a9560a 100644 --- a/src/properties.ui +++ b/src/properties.ui @@ -13,12 +13,21 @@ Torrent Properties - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + @@ -29,12 +38,6 @@ Main infos - - 9 - - - 6 - @@ -81,28 +84,55 @@ Torrent infos - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -189,12 +219,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -276,9 +315,7 @@ - - 0 - 0 + 0 0 @@ -304,28 +341,55 @@ Current session - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -402,12 +466,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -481,18 +554,25 @@ Trackers - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 7 - 0 + 0 0 @@ -518,20 +598,38 @@ Tracker - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -552,18 +650,25 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + - - 7 - 0 + 0 0 @@ -572,12 +677,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -648,12 +762,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -724,12 +847,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -759,12 +891,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -827,12 +968,21 @@ Url seeds - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + @@ -851,12 +1001,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -931,12 +1090,21 @@ Torrent content - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + @@ -972,12 +1140,6 @@ QAbstractItemView::ExtendedSelection - - 1 - - - false - @@ -1010,12 +1172,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + diff --git a/src/properties_imp.cpp b/src/properties_imp.cpp index 27f38258c..17c9c182c 100644 --- a/src/properties_imp.cpp +++ b/src/properties_imp.cpp @@ -23,6 +23,7 @@ #include "misc.h" #include "PropListDelegate.h" #include "bittorrent.h" +#include "arborescence.h" #include #include @@ -31,7 +32,7 @@ #include // Constructor -properties::properties(QWidget *parent, bittorrent *BTSession, QTorrentHandle &h): QDialog(parent), h(h), BTSession(BTSession), changedFilteredfiles(false), hash(h.hash()) { +properties::properties(QWidget *parent, bittorrent *BTSession, QTorrentHandle &h): QDialog(parent), h(h), BTSession(BTSession), changedFilteredfiles(false), hash(h.hash()), editParentsOnly(false) { setupUi(this); lbl_priorities->setText(tr("Priorities:")+"
  • "+tr("Ignored: file is not downloaded at all")+"
  • "+tr("Normal: normal priority. Download order is dependent on availability")+"
  • "+tr("High: higher than normal priority. Pieces are preferred over pieces with the same availability, but not over pieces with lower availability")+"
  • "+tr("Maximum: maximum priority, availability is disregarded, the piece is preferred over any other piece with lower priority")+"
"); // set icons @@ -43,12 +44,13 @@ properties::properties(QWidget *parent, bittorrent *BTSession, QTorrentHandle &h deleteWS_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png"))); setAttribute(Qt::WA_DeleteOnClose); // Set Properties list model - PropListModel = new QStandardItemModel(0,4); + 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, &changedFilteredfiles); filesList->setItemDelegate(PropDelegate); connect(filesList, SIGNAL(clicked(const QModelIndex&)), filesList, SLOT(edit(const QModelIndex&))); @@ -96,16 +98,14 @@ properties::properties(QWidget *parent, bittorrent *BTSession, QTorrentHandle &h loadTrackersErrors(); std::vector fp; h.file_progress(fp); + int *prioritiesTab = loadPiecesPriorities(); // List files in torrent - unsigned int nbFiles = h.num_files(); - for(unsigned int i=0; irowCount(); - PropListModel->insertRow(row); - PropListModel->setData(PropListModel->index(row, NAME), QVariant(h.file_at(i))); - PropListModel->setData(PropListModel->index(row, SIZE), QVariant((qlonglong)h.filesize_at(i))); - PropListModel->setData(PropListModel->index(row, PROGRESS), QVariant((double)fp[i])); - } - loadPiecesPriorities(); + arborescence *arb = new arborescence(h.get_torrent_info(), fp, prioritiesTab); + addFilesToTree(arb->getRoot(), PropListModel->invisibleRootItem()); + delete arb; + delete prioritiesTab; + connect(PropListModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updateChildrenPriority(QStandardItem*))); + filesList->expandAll(); // List web seeds loadWebSeedsFromFile(); loadWebSeeds(); @@ -127,6 +127,91 @@ properties::~properties(){ delete PropListModel; } +void properties::addFilesToTree(file *root, QStandardItem *parent) { + QList child; + // Name + QStandardItem *first; + if(root->isDir()) { + first = new QStandardItem(QIcon(":/Icons/folder.png"), root->name()); + } else { + first = new QStandardItem(QIcon(":/Icons/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())); + // TODO: row Color? + // Add the child to the tree + parent->appendRow(child); + // Add childs + file *childFile; + foreach(childFile, root->getChildren()) { + addFilesToTree(childFile, first); + } +} + +void properties::updateChildrenPriority(QStandardItem *item) { + qDebug("Priority changed"); + QStandardItem *parent = item->parent(); + int row = item->row(); + if(!parent) { + parent = PropListModel->invisibleRootItem(); + } + bool is_dir = (parent->child(row, INDEX)->text().toInt() == -1); + int priority = parent->child(row, PRIORITY)->text().toInt(); + // Update parent priority + if(item->parent()) { + bool parentUpdate = true; + unsigned int rowCount = parent->rowCount(); + for(unsigned int i=0; ichild(i, PRIORITY)->text().toInt() != priority) { + // Check if parent priority is NORMAL + QStandardItem *grandFather = parent->parent(); + if(!grandFather) { + grandFather = PropListModel->invisibleRootItem(); + } + QStandardItem *parentPrio = grandFather->child(parent->row(), PRIORITY); + editParentsOnly = true; + parentPrio->setText(misc::toQString(NORMAL)); + editParentsOnly = false; + parentUpdate = false; + break; + } + } + if(parentUpdate) { + QStandardItem *grandFather = parent->parent(); + if(!grandFather) { + grandFather = PropListModel->invisibleRootItem(); + } + QStandardItem *parentPrio = grandFather->child(parent->row(), PRIORITY); + editParentsOnly = true; + parentPrio->setText(misc::toQString(priority)); + editParentsOnly = false; + } + } + if(editParentsOnly) return; + if(!is_dir) return; + // Updating children + qDebug("Priority changed for a folder to %d", priority); + parent = parent->child(row); + unsigned int rowCount = parent->rowCount(); + qDebug("The folder has %d children", rowCount); + for(unsigned int i=0; ichild(i, PRIORITY); + int child_prio = child->text().toInt(); + qDebug("Child priority is %d", child_prio); + if(child_prio != priority) { + child->setText(misc::toQString(priority)); + } + } +} + void properties::loadTrackersErrors(){ // Tracker Errors QList > errors = BTSession->getTrackersErrors(hash); @@ -151,8 +236,9 @@ void properties::loadWebSeeds(){ } } -void properties::loadPiecesPriorities(){ +int* properties::loadPiecesPriorities(){ unsigned int nbFiles = h.num_files(); + int *prioritiesTab = new int[nbFiles]; QString fileName = h.name(); QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".priorities"); has_filtered_files = false; @@ -160,16 +246,18 @@ void properties::loadPiecesPriorities(){ // Read saved file if(!pieces_file.open(QIODevice::ReadOnly | QIODevice::Text)){ qDebug("Could not find pieces file"); - setAllPiecesState(NORMAL); - return; + for(unsigned int i=0; i pieces_priority_list = pieces_text.split('\n'); if((unsigned int)pieces_priority_list.size() != nbFiles+1){ std::cerr << "Error: Corrupted pieces file\n"; - setAllPiecesState(NORMAL); - return; + for(unsigned int i=0; isetData(PropListModel->index(i, PRIORITY), QVariant(priority)); + prioritiesTab[i] = priority; } + return prioritiesTab; } bool properties::allFiltered() const { @@ -196,9 +282,23 @@ bool properties::allFiltered() const { return true; } + +void properties::getPriorities(QStandardItem *parent, int *priorities) { + unsigned int nbRows = parent->rowCount(); + for(unsigned int i=0; ichild(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(); + } + } +} + void properties::displayFilesListMenu(const QPoint& pos){ - unsigned int nbRows = PropListModel->rowCount(); - if(nbRows == 1) return; + if(h.get_torrent_info().num_files() == 1) return; QMenu myFilesLlistMenu(this); QModelIndex index; // Enable/disable pause/start action given the DL state @@ -453,24 +553,18 @@ void properties::lowerSelectedTracker(){ } void properties::updateInfos(){ - std::vector fp; - try{ - h.file_progress(fp); - unsigned int nbFiles = h.num_files(); - for(unsigned int i=0; isetData(PropListModel->index(i, PROGRESS), QVariant((double)fp[i])); + // Update current tracker + try { + QString tracker = h.current_tracker().trimmed(); + if(!tracker.isEmpty()){ + trackerURL->setText(tracker); + }else{ + trackerURL->setText(tr("None - Unreachable?")); } }catch(invalid_handle e){ // torrent was removed, closing properties close(); } - // Update current tracker - QString tracker = h.current_tracker().trimmed(); - if(!tracker.isEmpty()){ - trackerURL->setText(tracker); - }else{ - trackerURL->setText(tr("None - Unreachable?")); - } } // Set the color of a row in data model @@ -563,21 +657,22 @@ bool properties::savePiecesPriorities() { QFile pieces_file(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".priorities")); // First, remove old file pieces_file.remove(); - // Write new files + int *priorities = new int[h.get_torrent_info().num_files()]; + getPriorities(PropListModel->invisibleRootItem(), priorities); + // Ok, we have priorities, save them if(!pieces_file.open(QIODevice::WriteOnly | QIODevice::Text)){ std::cerr << "Error: Could not save pieces priorities\n"; return true; } - unsigned int nbRows = PropListModel->rowCount(); - for(unsigned int i=0; iitem(i, PRIORITY); - unsigned short priority = item->text().toInt(); - if(!priority) { + unsigned int nbFiles = h.get_torrent_info().num_files(); + for(unsigned int i=0; i #include + using namespace libtorrent; class QString; diff --git a/src/search.qrc b/src/search.qrc index 474061e7e..d29ade29a 100644 --- a/src/search.qrc +++ b/src/search.qrc @@ -2,11 +2,11 @@ search_engine/nova2.py search_engine/novaprinter.py + search_engine/engines/piratebay.png search_engine/engines/isohunt.py - search_engine/engines/torrentreactor.png search_engine/engines/btjunkie.py + search_engine/engines/torrentreactor.png search_engine/engines/mininova.png - search_engine/engines/piratebay.png search_engine/engines/torrentreactor.py search_engine/engines/mininova.py search_engine/engines/piratebay.py diff --git a/src/src.pro b/src/src.pro index 3fb450d93..35f439256 100644 --- a/src/src.pro +++ b/src/src.pro @@ -136,7 +136,8 @@ HEADERS += GUI.h misc.h options_imp.h about_imp.h \ rss.h rss_imp.h FinishedTorrents.h \ allocationDlg.h FinishedListDelegate.h \ qtorrenthandle.h downloadingTorrents.h \ - engineSelectDlg.h pluginSource.h + engineSelectDlg.h pluginSource.h \ + arborescence.h FORMS += MainWindow.ui options.ui about.ui \ properties.ui createtorrent.ui preview.ui \ login.ui downloadFromURL.ui addTorrentDialog.ui \ diff --git a/src/torrentAddition.h b/src/torrentAddition.h index 450e2a12b..ee18d5ac0 100644 --- a/src/torrentAddition.h +++ b/src/torrentAddition.h @@ -37,6 +37,7 @@ #include "misc.h" #include "PropListDelegate.h" #include "ui_addTorrentDialog.h" +#include "arborescence.h" using namespace libtorrent; @@ -55,19 +56,22 @@ class torrentAdditionDialog : public QDialog, private Ui_addTorrentDialog{ QString from_url; QStandardItemModel *PropListModel; PropListDelegate *PropDelegate; + unsigned int nbFiles; + bool editParentsOnly; public: - torrentAdditionDialog(QWidget *parent) : QDialog(parent) { + torrentAdditionDialog(QWidget *parent) : QDialog(parent), editParentsOnly(false){ setupUi(this); setAttribute(Qt::WA_DeleteOnClose); // Set Properties list model - PropListModel = new QStandardItemModel(0,4); + 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")); torrentContentList->setModel(PropListModel); torrentContentList->hideColumn(PROGRESS); + torrentContentList->hideColumn(INDEX); PropDelegate = new PropListDelegate(); torrentContentList->setItemDelegate(PropDelegate); connect(torrentContentList, SIGNAL(clicked(const QModelIndex&)), torrentContentList, SLOT(edit(const QModelIndex&))); @@ -100,6 +104,7 @@ class torrentAdditionDialog : public QDialog, private Ui_addTorrentDialog{ entry e = bdecode(std::istream_iterator(in), std::istream_iterator()); // Getting torrent file informations torrent_info t(e); + nbFiles = t.num_files(); // Setting file name fileName = misc::toQString(t.name()); hash = misc::toQString(t.info_hash()); @@ -112,15 +117,11 @@ class torrentAdditionDialog : public QDialog, private Ui_addTorrentDialog{ } fileNameLbl->setText(QString::fromUtf8("
")+newFileName+QString::fromUtf8("
")); // List files in torrent - unsigned int nbFiles = t.num_files(); - for(unsigned int i=0; irowCount(); - PropListModel->insertRow(row); - PropListModel->setData(PropListModel->index(row, NAME), QVariant(misc::toQString(t.file_at(i).path.leaf()))); - PropListModel->setData(PropListModel->index(row, SIZE), QVariant((qlonglong)t.file_at(i).size)); - PropListModel->setData(PropListModel->index(i, PRIORITY), QVariant(NORMAL)); - setRowColor(i, QString::fromUtf8("green")); - } + arborescence *arb = new arborescence(t); + addFilesToTree(arb->getRoot(), PropListModel->invisibleRootItem()); + delete arb; + connect(PropListModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(updateChildrenPriority(QStandardItem*))); + torrentContentList->expandAll(); }catch (invalid_torrent_file&){ // Raised by torrent_info constructor // Display warning to tell user we can't decode the torrent file if(!from_url.isNull()){ @@ -157,7 +158,93 @@ class torrentAdditionDialog : public QDialog, private Ui_addTorrentDialog{ show(); } + void addFilesToTree(file *root, QStandardItem *parent) { + QList child; + // Name + QStandardItem *first; + if(root->isDir()) { + first = new QStandardItem(QIcon(":/Icons/folder.png"), root->name()); + } else { + first = new QStandardItem(QIcon(":/Icons/file.png"), root->name()); + } + child << first; + // Size + child << new QStandardItem(misc::toQString(root->getSize())); + // Hidden progress + child << new QStandardItem(""); + // Prio + child << new QStandardItem(misc::toQString(NORMAL)); + // INDEX + child << new QStandardItem(misc::toQString(root->getIndex())); + // TODO: row Color? + // Add the child to the tree + parent->appendRow(child); + // Add childs + file *childFile; + foreach(childFile, root->getChildren()) { + addFilesToTree(childFile, first); + } + } + public slots: + + void updateChildrenPriority(QStandardItem *item) { + qDebug("Priority changed"); + QStandardItem *parent = item->parent(); + int row = item->row(); + if(!parent) { + parent = PropListModel->invisibleRootItem(); + } + bool is_dir = (parent->child(row, INDEX)->text().toInt() == -1); + int priority = parent->child(row, PRIORITY)->text().toInt(); + // Update parent priority + if(item->parent()) { + bool parentUpdate = true; + unsigned int rowCount = parent->rowCount(); + for(unsigned int i=0; ichild(i, PRIORITY)->text().toInt() != priority) { + // Check if parent priority is NORMAL + QStandardItem *grandFather = parent->parent(); + if(!grandFather) { + grandFather = PropListModel->invisibleRootItem(); + } + QStandardItem *parentPrio = grandFather->child(parent->row(), PRIORITY); + editParentsOnly = true; + parentPrio->setText(misc::toQString(NORMAL)); + editParentsOnly = false; + parentUpdate = false; + break; + } + } + if(parentUpdate) { + QStandardItem *grandFather = parent->parent(); + if(!grandFather) { + grandFather = PropListModel->invisibleRootItem(); + } + QStandardItem *parentPrio = grandFather->child(parent->row(), PRIORITY); + editParentsOnly = true; + parentPrio->setText(misc::toQString(priority)); + editParentsOnly = false; + } + } + if(editParentsOnly) return; + if(!is_dir) return; + // Updating children + qDebug("Priority changed for a folder to %d", priority); + parent = parent->child(row); + unsigned int rowCount = parent->rowCount(); + qDebug("The folder has %d children", rowCount); + for(unsigned int i=0; ichild(i, PRIORITY); + int child_prio = child->text().toInt(); + qDebug("Child priority is %d", child_prio); + if(child_prio != priority) { + child->setText(misc::toQString(priority)); + } + } + } + void on_browseButton_clicked(){ QString dir; QDir saveDir(savePathTxt->text()); @@ -192,8 +279,7 @@ class torrentAdditionDialog : public QDialog, private Ui_addTorrentDialog{ } void displayFilesListMenu(const QPoint& pos){ - unsigned int nbRows = PropListModel->rowCount(); - if(nbRows == 1) return; + if(nbFiles == 1) return; QMenu myFilesLlistMenu(this); QModelIndex index; // Enable/disable pause/start action given the DL state @@ -260,23 +346,37 @@ class torrentAdditionDialog : public QDialog, private Ui_addTorrentDialog{ } } + void getPriorities(QStandardItem *parent, int *priorities) { + unsigned int nbRows = parent->rowCount(); + for(unsigned int i=0; ichild(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(); + } + } + } + void savePiecesPriorities(){ qDebug("Saving pieces priorities"); QFile pieces_file(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".priorities")); // First, remove old file pieces_file.remove(); - // Write new files + int *priorities = new int[nbFiles]; + getPriorities(PropListModel->invisibleRootItem(), priorities); + // Ok, we have priorities, save them if(!pieces_file.open(QIODevice::WriteOnly | QIODevice::Text)){ std::cerr << "Error: Could not save pieces priorities\n"; return; } - unsigned int nbRows = PropListModel->rowCount(); - for(unsigned int i=0; iitem(i, PRIORITY); - unsigned short priority = item->text().toInt(); - pieces_file.write((misc::toQByteArray(priority)+misc::toQByteArray("\n"))); + for(unsigned int i=0; i