From ff3575c7ac2d18c665ab848ff2be585189c45e70 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Thu, 12 Apr 2007 19:51:39 +0000 Subject: [PATCH] - Implemented files prioritizing in a torrent (Part I: Torrent Properties), still need to work on torrent addition dialog --- Changelog | 1 + TODO | 6 +-- src/PropListDelegate.h | 88 ++++++++++++++++++++++--------- src/bittorrent.cpp | 51 +++++++++--------- src/properties.ui | 59 +++------------------ src/properties_imp.cpp | 116 ++++++++++++----------------------------- src/properties_imp.h | 9 ++-- 7 files changed, 135 insertions(+), 195 deletions(-) diff --git a/Changelog b/Changelog index 9e6b5d827..243064083 100644 --- a/Changelog +++ b/Changelog @@ -1,6 +1,7 @@ * Unknown - Christophe Dumez - v0.10.0 - FEATURE: Added UPnP / NAT-PMP port forwarding support - FEATURE: Added RSS support + - FEATURE: Support files prioritizing in a torrent - FEATURE: Finished torrents are now moved to another tab for seeding - FEATURE: Display more infos about the torrent in its properties - FEATURE: Allow the user to edit torrents' trackers diff --git a/TODO b/TODO index 23824119a..56d071233 100644 --- a/TODO +++ b/TODO @@ -29,7 +29,7 @@ - Add a torrent scheduler - Improve Ipfilter.dat parser (move to a thread ? - too slow to load qBT and more importantly options) -// in v0.11 +// in v1.1.0 - Tabs support in search // in v1.0.0 (partial) - WIP @@ -41,8 +41,8 @@ - Use tooltips to explain options? - Display more info in log (UPnP) - Update to libtorrent SVN (0.13) - DONE - - Use its UPnP/NAT-PMP built-in support instead of ours ALMOST - - Use its piece prioritization support + - Use its UPnP/NAT-PMP built-in support instead of ours DONE (enabled as a default) + - Use its piece prioritization support (debug) - Get upload/download limit per torrent (uncomment some code) ALMOST - Improve ratio display / calculation / saving / per torrent... - Display the sum of the size of the selected files in the torrent instead of the whole torrent size in download list diff --git a/src/PropListDelegate.h b/src/PropListDelegate.h index caea70650..97e3dc499 100644 --- a/src/PropListDelegate.h +++ b/src/PropListDelegate.h @@ -36,7 +36,12 @@ #define NAME 0 #define SIZE 1 #define PROGRESS 2 -#define SELECTED 3 +#define PRIORITY 3 + +#define IGNORED 0 +#define NORMAL 1 +#define HIGH 2 +#define MAXIMUM 7 class PropListDelegate: public QItemDelegate { Q_OBJECT @@ -103,19 +108,27 @@ class PropListDelegate: public QItemDelegate { painter->drawText(option.rect, Qt::AlignCenter, newopt.text); break; } - case SELECTED:{ + case PRIORITY:{ QStyleOptionComboBox newopt; newopt.rect = opt.rect; - if(index.data().toBool()){ -// painter->drawText(option.rect, Qt::AlignCenter, tr("True")); - newopt.currentText = tr("True"); - }else{ -// painter->drawText(option.rect, Qt::AlignCenter, tr("False")); - newopt.currentText = tr("False"); + switch(index.data().toInt()){ + case IGNORED: + newopt.currentText = tr("Ignored"); + break; + case NORMAL: + newopt.currentText = tr("Normal", "Normal (priority)"); + break; + case HIGH: + newopt.currentText = tr("High", "High (priority)"); + break; + case MAXIMUM: + newopt.currentText = tr("Maximum", "Maximum (priority)"); + break; + default: + qDebug("Unhandled priority, setting NORMAL"); + newopt.currentText = tr("Normal", "Normal (priority)"); } newopt.state |= QStyle::State_Enabled; -// newopt.frame = true; -// newopt.editable = true; QApplication::style()->drawComplexControl(QStyle::CC_ComboBox, &newopt, painter); opt.palette.setColor(QPalette::Text, QColor("black")); @@ -129,29 +142,38 @@ class PropListDelegate: public QItemDelegate { } QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const { - if(index.column() != SELECTED) return 0; + if(index.column() != PRIORITY) return 0; QComboBox* editor = new QComboBox(parent); editor->setFocusPolicy(Qt::StrongFocus); - editor->addItem(tr("True")); - editor->addItem(tr("False")); + editor->addItem(tr("Ignored")); + editor->addItem(tr("Normal", "Normal (priority)")); + editor->addItem(tr("High", "High (priority)")); + editor->addItem(tr("Maximum", "Maximum (priority)")); return editor; } void setEditorData(QWidget *editor, const QModelIndex &index) const { - bool value = index.model()->data(index, Qt::DisplayRole).toBool(); + unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt(); QComboBox *combobox = static_cast(editor); - if(value) { - combobox->setCurrentIndex(0); - } else { - combobox->setCurrentIndex(1); + switch(val){ + case IGNORED: + combobox->setCurrentIndex(0); + break; + case NORMAL: + combobox->setCurrentIndex(1); + break; + case HIGH: + combobox->setCurrentIndex(2); + break; + case MAXIMUM: + combobox->setCurrentIndex(3); + break; + default: + qDebug("Unhandled priority, setting to NORMAL"); + combobox->setCurrentIndex(1); } } -// bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index ){ -// qDebug("Event!!!!"); -// return false; -// } - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const{ QVariant value = index.data(Qt::FontRole); QFont fnt = value.isValid() ? qvariant_cast(value) : option.font; @@ -168,13 +190,27 @@ class PropListDelegate: public QItemDelegate { int value = combobox->currentIndex(); qDebug("Setting combobox value in index: %d", value); QString color; - if(value == 0) { - model->setData(index, true); + if(value) { color = "green"; } else { - model->setData(index, false); color = "red"; } + switch(value){ + case 0: + model->setData(index, QVariant(IGNORED)); + break; + case 1: + model->setData(index, QVariant(NORMAL)); + break; + case 2: + model->setData(index, QVariant(HIGH)); + break; + case 3: + model->setData(index, QVariant(MAXIMUM)); + break; + default: + model->setData(index, QVariant(NORMAL)); + } for(int i=0; icolumnCount(); ++i){ model->setData(model->index(index.row(), i), QVariant(QColor(color)), Qt::TextColorRole); } diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index b37d82b6d..723c917fc 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -162,8 +162,9 @@ void bittorrent::deleteTorrent(const QString& hash, bool permanent){ torrentBackup.remove(hash+".fastresume"); torrentBackup.remove(hash+".paused"); torrentBackup.remove(hash+".incremental"); - torrentBackup.remove(hash+".pieces"); + torrentBackup.remove(hash+".priorities"); torrentBackup.remove(hash+".savepath"); + torrentBackup.remove(hash+".trackers"); if(permanent){ // Remove from Hard drive qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName)); @@ -249,11 +250,11 @@ void bittorrent::addTorrent(const QString& path, bool fromScanDir, const QString if(torrentBackup.exists(QString(t.name().c_str())+".torrent")){ QFile::rename(torrentBackup.path()+QDir::separator()+QString(t.name().c_str())+".torrent", torrentBackup.path()+QDir::separator()+hash+".torrent"); QFile::rename(torrentBackup.path()+QDir::separator()+QString(t.name().c_str())+".fastresume", torrentBackup.path()+QDir::separator()+hash+".fastresume"); - QFile::rename(torrentBackup.path()+QDir::separator()+QString(t.name().c_str())+".pieces", torrentBackup.path()+QDir::separator()+hash+".pieces"); QFile::rename(torrentBackup.path()+QDir::separator()+QString(t.name().c_str())+".savepath", torrentBackup.path()+QDir::separator()+hash+".savepath"); QFile::rename(torrentBackup.path()+QDir::separator()+QString(t.name().c_str())+".paused", torrentBackup.path()+QDir::separator()+hash+".paused"); QFile::rename(torrentBackup.path()+QDir::separator()+QString(t.name().c_str())+".incremental", torrentBackup.path()+QDir::separator()+hash+".incremental"); file = torrentBackup.path() + QDir::separator() + hash + ".torrent"; + } //Getting fast resume data if existing if(torrentBackup.exists(hash+".fastresume")){ @@ -364,23 +365,24 @@ void bittorrent::setMaxConnections(int maxConnec){ s->set_max_connections(maxConnec); } -// Check in .pieces file if the user filtered files +// Check in .priorities file if the user filtered files // in this torrent. bool bittorrent::hasFilteredFiles(const QString& fileHash) const{ - QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".pieces"); + QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".priorities"); // Read saved file if(!pieces_file.open(QIODevice::ReadOnly | QIODevice::Text)){ return false; } - QByteArray pieces_selection = pieces_file.readAll(); + QByteArray pieces_text = pieces_file.readAll(); pieces_file.close(); - QList pieces_selection_list = pieces_selection.split('\n'); - for(int i=0; i 1){ - isFiltered = 0; - } - if(isFiltered){ + QList pieces_priorities_list = pieces_text.split('\n'); + unsigned int listSize = pieces_priorities_list.size(); + for(unsigned int i=0; i 7){ + priority = 1; + } + if(!priority){ return true; } } @@ -419,36 +421,35 @@ void bittorrent::disableDHT(){ } } -// Read filtered pieces from .pieces file -// and ask torrent_handle to filter them +// Read pieces priorities from .priorities file +// and ask torrent_handle to consider them void bittorrent::loadFilteredFiles(torrent_handle &h){ torrent_info torrentInfo = h.get_torrent_info(); + unsigned int nbTorrents = torrentInfo.num_files(); if(!h.is_valid()){ qDebug("/!\\ Error: Invalid handle"); return; } QString fileHash = QString(misc::toString(torrentInfo.info_hash()).c_str()); - QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".pieces"); + QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".priorities"); // Read saved file if(!pieces_file.open(QIODevice::ReadOnly | QIODevice::Text)){ return; } - QByteArray pieces_selection = pieces_file.readAll(); + QByteArray pieces_priorities = pieces_file.readAll(); pieces_file.close(); - QList pieces_selection_list = pieces_selection.split('\n'); - if(pieces_selection_list.size() != torrentInfo.num_files()+1){ + QList pieces_priorities_list = pieces_priorities.split('\n'); + if((unsigned int)pieces_priorities_list.size() != nbTorrents+1){ std::cerr << "Error: Corrupted pieces file\n"; return; } - std::vector selectionBitmask; - for(int i=0; i 1){ - isFiltered = 0; + for(unsigned int i=0; i 7){ + priority = 1; } - selectionBitmask.push_back(isFiltered); + h.piece_priority(i, priority); } - h.filter_files(selectionBitmask); } // Save fastresume data for all torrents diff --git a/src/properties.ui b/src/properties.ui index e15a3bdc0..cb3e786fa 100644 --- a/src/properties.ui +++ b/src/properties.ui @@ -874,57 +874,7 @@ - - - 0 - - - 6 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Unselect - - - - - - - Select - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + Sans Serif @@ -937,10 +887,13 @@ - You can select here precisely which files you want to download in current torrent. + - Qt::AlignCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + true diff --git a/src/properties_imp.cpp b/src/properties_imp.cpp index 2ba7ebcb6..f4b90c3dc 100644 --- a/src/properties_imp.cpp +++ b/src/properties_imp.cpp @@ -27,9 +27,8 @@ // Constructor properties::properties(QWidget *parent, torrent_handle &h, QStringList trackerErrors): QDialog(parent), h(h){ 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 - unselect->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png"))); - select->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png"))); addTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png"))); removeTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png"))); lowerTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/downarrow.png"))); @@ -37,10 +36,10 @@ properties::properties(QWidget *parent, torrent_handle &h, QStringList trackerEr setAttribute(Qt::WA_DeleteOnClose); // Set Properties list model PropListModel = new QStandardItemModel(0,4); - PropListModel->setHeaderData(NAME, Qt::Horizontal, tr("File Name")); + PropListModel->setHeaderData(NAME, Qt::Horizontal, tr("File name")); PropListModel->setHeaderData(SIZE, Qt::Horizontal, tr("Size")); PropListModel->setHeaderData(PROGRESS, Qt::Horizontal, tr("Progress")); - PropListModel->setHeaderData(SELECTED, Qt::Horizontal, tr("Selected")); + PropListModel->setHeaderData(PRIORITY, Qt::Horizontal, tr("Priority")); filesList->setModel(PropListModel); PropDelegate = new PropListDelegate(); filesList->setItemDelegate(PropDelegate); @@ -92,7 +91,7 @@ properties::properties(QWidget *parent, torrent_handle &h, QStringList trackerEr PropListModel->setData(PropListModel->index(row, SIZE), QVariant((qlonglong)torrentInfo.file_at(i).size)); PropListModel->setData(PropListModel->index(row, PROGRESS), QVariant((double)fp[i])); } - loadFilteredFiles(); + loadPiecesPriorities(); // Incremental download if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".incremental")){ incrementalDownload->setChecked(true); @@ -111,42 +110,40 @@ properties::~properties(){ delete PropListModel; } -void properties::loadFilteredFiles(){ +void properties::loadPiecesPriorities(){ torrent_info torrentInfo = h.get_torrent_info(); + unsigned int nbFiles = torrentInfo.num_files(); QString fileName = QString(torrentInfo.name().c_str()); - QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".pieces"); + QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".priorities"); has_filtered_files = false; - qDebug("Loading filtered state of files"); + qDebug("Loading pieces priorities"); // Read saved file if(!pieces_file.open(QIODevice::ReadOnly | QIODevice::Text)){ qDebug("Could not find pieces file"); - setAllPiecesState(true); - selectionBitmask.assign(torrentInfo.num_files(), 0); + setAllPiecesState(NORMAL); return; } - QByteArray pieces_selection = pieces_file.readAll(); + QByteArray pieces_text = pieces_file.readAll(); pieces_file.close(); - QList pieces_selection_list = pieces_selection.split('\n'); - if(pieces_selection_list.size() != torrentInfo.num_files()+1){ + QList pieces_priority_list = pieces_text.split('\n'); + if((unsigned int)pieces_priority_list.size() != nbFiles+1){ std::cerr << "Error: Corrupted pieces file\n"; - setAllPiecesState(true); - selectionBitmask.assign(torrentInfo.num_files(), 0); + setAllPiecesState(NORMAL); return; } - for(int i=0; i 1){ - isFiltered = 0; + for(unsigned int i=0; i 7){ + // Normal priority as default + priority = 1; } - selectionBitmask.push_back(isFiltered); - if(isFiltered){ - PropListModel->setData(PropListModel->index(i, SELECTED), QVariant(false)); + if(!priority){ setRowColor(i, "red"); has_filtered_files = true; }else{ - PropListModel->setData(PropListModel->index(i, SELECTED), QVariant(true)); setRowColor(i, "green"); } + PropListModel->setData(PropListModel->index(i, PRIORITY), QVariant(priority)); } } @@ -290,15 +287,15 @@ void properties::setRowColor(int row, QString color){ } } -void properties::setAllPiecesState(bool selected){ +void properties::setAllPiecesState(unsigned short priority){ torrent_info torrentInfo = h.get_torrent_info(); for(int i=0; isetData(PropListModel->index(i, SELECTED), QVariant(selected)); + PropListModel->setData(PropListModel->index(i, PRIORITY), QVariant(priority)); } } @@ -317,77 +314,32 @@ void properties::on_incrementalDownload_stateChanged(int){ } } -// Resume download of specified files of torrent -void properties::on_select_clicked(){ - QModelIndexList selectedIndexes = filesList->selectionModel()->selectedIndexes(); - QModelIndex index; - foreach(index, selectedIndexes){ - if(index.column() == NAME){ - int row = index.row(); - if(selectionBitmask.at(row)){ - // File is selected - selectionBitmask.erase(selectionBitmask.begin()+row); - selectionBitmask.insert(selectionBitmask.begin()+row, 0); - h.filter_files(selectionBitmask); - // Update list infos - setRowColor(row, "green"); - PropListModel->setData(PropListModel->index(row, SELECTED), QVariant(true)); - } - } - } - // Save filtered pieces to a file to remember them - if(selectedIndexes.size() != 0){ - saveFilteredFiles(); - } -} - void properties::on_okButton_clicked(){ + savePiecesPriorities(); close(); } -// Cancel download of specified files of torrent -void properties::on_unselect_clicked(){ - QModelIndexList selectedIndexes = filesList->selectionModel()->selectedIndexes(); - QModelIndex index; - foreach(index, selectedIndexes){ - if(index.column() == NAME){ - int row = index.row(); - if(!selectionBitmask.at(row)){ - // File is selected - selectionBitmask.erase(selectionBitmask.begin()+row); - selectionBitmask.insert(selectionBitmask.begin()+row, 1); - h.filter_files(selectionBitmask); - // Update list infos - setRowColor(row, "red"); - PropListModel->setData(PropListModel->index(row, SELECTED), QVariant(false)); - } - } - } - // Save filtered files to a file to remember them - if(selectedIndexes.size() != 0){ - saveFilteredFiles(); - } -} - -void properties::saveFilteredFiles(){ +void properties::savePiecesPriorities(){ + qDebug("Saving pieces priorities"); torrent_info torrentInfo = h.get_torrent_info(); bool hasFilteredFiles = false; QString fileName = QString(torrentInfo.name().c_str()); - QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".pieces"); + QFile pieces_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".priorities"); // First, remove old file pieces_file.remove(); // Write new files if(!pieces_file.open(QIODevice::WriteOnly | QIODevice::Text)){ - std::cerr << "Error: Could not save filtered pieces\n"; + std::cerr << "Error: Could not save pieces priorities\n"; return; } - for(int i=0; irowCount(); + for(unsigned int i=0; iitem(i, PRIORITY); + unsigned short priority = item->text().toInt(); + if(!priority) { hasFilteredFiles = true; } + pieces_file.write(QByteArray((misc::toString(priority)+"\n").c_str())); } pieces_file.close(); if(!has_filtered_files){ diff --git a/src/properties_imp.h b/src/properties_imp.h index 8f5ef5064..1557f1907 100644 --- a/src/properties_imp.h +++ b/src/properties_imp.h @@ -39,19 +39,16 @@ class properties : public QDialog, private Ui::properties{ PropListDelegate *PropDelegate; QStandardItemModel *PropListModel; QTimer *updateProgressTimer; - std::vector selectionBitmask; bool has_filtered_files; protected slots: - void on_select_clicked(); - void on_unselect_clicked(); void on_okButton_clicked(); void on_incrementalDownload_stateChanged(int); void setRowColor(int row, QString color); - void saveFilteredFiles(); + void savePiecesPriorities(); void updateProgress(); - void loadFilteredFiles(); - void setAllPiecesState(bool selected); + void loadPiecesPriorities(); + void setAllPiecesState(unsigned short priority); void askForTracker(); void loadTrackers(); void deleteSelectedTrackers();