From dbc5ffee759c47cbce72d52587d5420a25e3ee34 Mon Sep 17 00:00:00 2001 From: buinsky Date: Sun, 7 Dec 2014 23:33:36 +0300 Subject: [PATCH 01/13] Follow project coding style. Issue #2192. --- src/webui/abstractrequesthandler.cpp | 124 +++++++++++++-------------- 1 file changed, 58 insertions(+), 66 deletions(-) diff --git a/src/webui/abstractrequesthandler.cpp b/src/webui/abstractrequesthandler.cpp index 3152532de..1eec6cdd8 100644 --- a/src/webui/abstractrequesthandler.cpp +++ b/src/webui/abstractrequesthandler.cpp @@ -36,145 +36,137 @@ #include "abstractrequesthandler.h" AbstractRequestHandler::AbstractRequestHandler(const HttpRequest &request, const HttpEnvironment &env, WebApplication *app) - : app_(app), session_(0), request_(request), env_(env) + : app_(app), session_(0), request_(request), env_(env) { - sessionInitialize(); - if (!sessionActive() && !isAuthNeeded()) - sessionStart(); + sessionInitialize(); + if (!sessionActive() && !isAuthNeeded()) + sessionStart(); } HttpResponse AbstractRequestHandler::run() { - response_ = HttpResponse(); + response_ = HttpResponse(); - if (isBanned()) - { - status(403, "Forbidden"); - print(QObject::tr("Your IP address has been banned after too many failed authentication attempts."), CONTENT_TYPE_TXT); - } - else - { - processRequest(); - } + if (isBanned()) { + status(403, "Forbidden"); + print(QObject::tr("Your IP address has been banned after too many failed authentication attempts."), CONTENT_TYPE_TXT); + } + else { + processRequest(); + } - return response_; + return response_; } bool AbstractRequestHandler::isAuthNeeded() { - return - ( + return + ( env_.clientAddress != QHostAddress::LocalHost && env_.clientAddress != QHostAddress::LocalHostIPv6 - ) || Preferences::instance()->isWebUiLocalAuthEnabled(); + ) || Preferences::instance()->isWebUiLocalAuthEnabled(); } void AbstractRequestHandler::status(uint code, const QString& text) { - response_.status = HttpResponseStatus(code, text); + response_.status = HttpResponseStatus(code, text); } void AbstractRequestHandler::header(const QString& name, const QString& value) { - response_.headers[name] = value; + response_.headers[name] = value; } void AbstractRequestHandler::print(const QString& text, const QString& type) { - print_impl(text.toUtf8(), type); + print_impl(text.toUtf8(), type); } void AbstractRequestHandler::print(const QByteArray& data, const QString& type) { - print_impl(data, type); + print_impl(data, type); } void AbstractRequestHandler::print_impl(const QByteArray& data, const QString& type) { - if (!response_.headers.contains(HEADER_CONTENT_TYPE)) - response_.headers[HEADER_CONTENT_TYPE] = type; + if (!response_.headers.contains(HEADER_CONTENT_TYPE)) + response_.headers[HEADER_CONTENT_TYPE] = type; - if (type.indexOf("image") > -1) - response_.headers[HEADER_CACHE_CONTROL] = "max-age=3000000"; + if (type.indexOf("image") > -1) + response_.headers[HEADER_CACHE_CONTROL] = "max-age=3000000"; - response_.content += data; + response_.content += data; } void AbstractRequestHandler::printFile(const QString& path) { - QByteArray data; - QString type; + QByteArray data; + QString type; - if (!app_->readFile(path, data, type)) - { - status(404, "Not Found"); - return; - } + if (!app_->readFile(path, data, type)) { + status(404, "Not Found"); + return; + } - print(data, type); + print(data, type); } void AbstractRequestHandler::sessionInitialize() { - app_->sessionInitialize(this); + app_->sessionInitialize(this); } void AbstractRequestHandler::sessionStart() { - if (app_->sessionStart(this)) - { - QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8()); - cookie.setPath("/"); - header(HEADER_SET_COOKIE, cookie.toRawForm()); - } + if (app_->sessionStart(this)) { + QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8()); + cookie.setPath("/"); + header(HEADER_SET_COOKIE, cookie.toRawForm()); + } } void AbstractRequestHandler::sessionEnd() { - if (sessionActive()) - { - QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8()); - cookie.setPath("/"); - cookie.setExpirationDate(QDateTime::currentDateTime()); + if (sessionActive()) { + QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8()); + cookie.setPath("/"); + cookie.setExpirationDate(QDateTime::currentDateTime()); - if (app_->sessionEnd(this)) - { - header(HEADER_SET_COOKIE, cookie.toRawForm()); + if (app_->sessionEnd(this)) + header(HEADER_SET_COOKIE, cookie.toRawForm()); } - } } bool AbstractRequestHandler::isBanned() const { - return app_->isBanned(this); + return app_->isBanned(this); } int AbstractRequestHandler::failedAttempts() const { - return app_->failedAttempts(this); + return app_->failedAttempts(this); } void AbstractRequestHandler::resetFailedAttempts() { - app_->resetFailedAttempts(this); + app_->resetFailedAttempts(this); } void AbstractRequestHandler::increaseFailedAttempts() { - app_->increaseFailedAttempts(this); + app_->increaseFailedAttempts(this); } QString AbstractRequestHandler::saveTmpFile(const QByteArray &data) { - QTemporaryFile tmpfile(QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent")); - tmpfile.setAutoRemove(false); - if (tmpfile.open()) - { - tmpfile.write(data); - tmpfile.close(); - return tmpfile.fileName(); - } - - qWarning() << "I/O Error: Could not create temporary file"; - return QString(); + QTemporaryFile tmpfile(QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent")); + tmpfile.setAutoRemove(false); + if (tmpfile.open()) { + tmpfile.write(data); + tmpfile.close(); + return tmpfile.fileName(); + } + + qWarning() << "I/O Error: Could not create temporary file"; + return QString(); } From ba40408c8d2368a0559f4bb5d66f675f978807c1 Mon Sep 17 00:00:00 2001 From: buinsky Date: Sun, 7 Dec 2014 23:50:06 +0300 Subject: [PATCH 02/13] Images cache control code edit --- src/webui/abstractrequesthandler.cpp | 3 --- src/webui/requesthandler.cpp | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webui/abstractrequesthandler.cpp b/src/webui/abstractrequesthandler.cpp index 1eec6cdd8..6d6fa1c8b 100644 --- a/src/webui/abstractrequesthandler.cpp +++ b/src/webui/abstractrequesthandler.cpp @@ -92,9 +92,6 @@ void AbstractRequestHandler::print_impl(const QByteArray& data, const QString& t if (!response_.headers.contains(HEADER_CONTENT_TYPE)) response_.headers[HEADER_CONTENT_TYPE] = type; - if (type.indexOf("image") > -1) - response_.headers[HEADER_CACHE_CONTROL] = "max-age=3000000"; - response_.content += data; } diff --git a/src/webui/requesthandler.cpp b/src/webui/requesthandler.cpp index 092bd11e3..3b56c6daf 100644 --- a/src/webui/requesthandler.cpp +++ b/src/webui/requesthandler.cpp @@ -62,6 +62,7 @@ const QString SCOPE_THEME = "theme"; const QString DEFAULT_ACTION = "index"; const QString WEBUI_ACTION = "webui"; const QString VERSION_INFO = "version"; +const QString MAX_AGE_MONTH = "public, max-age=2592000"; #define ADD_ACTION(scope, action) actions[#scope][#action] = &RequestHandler::action_##scope##_##action @@ -178,12 +179,14 @@ void RequestHandler::action_public_theme() qDebug() << Q_FUNC_INFO << "There icon:" << url; printFile(url); + header(HEADER_CACHE_CONTROL, MAX_AGE_MONTH); } void RequestHandler::action_public_images() { const QString path = ":/Icons/" + args_.join("/"); printFile(path); + header(HEADER_CACHE_CONTROL, MAX_AGE_MONTH); } // GET params: From 69506ec50512162bb400fd4b4355f555200850a4 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 00:14:55 +0300 Subject: [PATCH 03/13] Follow project coding style. Issue #2192. --- src/webui/www/private/index.html | 226 +++++++++++++++---------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 7bb4d6d26..a906886f3 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -1,120 +1,120 @@ - - - qBittorrent web User Interface - - - - - - - - - - - - - - - - - + + + qBittorrent web User Interface + + + + + + + + + + + + + + + + + - - -
-
- - - -
-
-
+ + +
+
+ + + +
+
+
From 28fad54cc44b3e6f515693e1c415f093448f3d04 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 00:32:39 +0300 Subject: [PATCH 04/13] Add higest and lowest priority icons into toolbar --- src/webui/www/private/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index a906886f3..de85d90f6 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -83,8 +83,10 @@ _(Resume) _(Pause) - _(Decrease priority) + _(Move to bottom) + _(Decrease priority) _(Increase priority) + _(Move to top) _(Options) From 57a4f3ed192edfbbaa2d5dd90a5bc3d8dcea3c61 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 00:46:07 +0300 Subject: [PATCH 05/13] Hide priority menu items when queueing is disabled --- src/webui/www/private/index.html | 15 ++++++++------- src/webui/www/public/scripts/client.js | 2 ++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index de85d90f6..631cbddf2 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -98,13 +98,14 @@
  • _(Resume) _(Resume)
  • _(Pause) _(Pause)
  • _(Delete) _(Delete)
  • -
  • _(Priority) - +
  • + _(Priority) +
  • _(Limit download rate...) _(Limit download rate...)
  • _(Limit upload rate...) _(Limit upload rate...)
  • diff --git a/src/webui/www/public/scripts/client.js b/src/webui/www/public/scripts/client.js index 07b842486..0d13ab518 100644 --- a/src/webui/www/public/scripts/client.js +++ b/src/webui/www/public/scripts/client.js @@ -213,9 +213,11 @@ window.addEvent('load', function () { }); if (queueing_enabled) { $('queueingButtons').removeClass('invisible'); + $('queueingMenuItems').removeClass('invisible'); myTable.showPriority(); } else { $('queueingButtons').addClass('invisible'); + $('queueingMenuItems').addClass('invisible'); myTable.hidePriority(); } From e8ad465c5f0189555074be821e54c3f2adb76da9 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 00:52:29 +0300 Subject: [PATCH 06/13] Follow project coding style. Issue #2192. --- src/webui/www/public/scripts/mocha-init.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/webui/www/public/scripts/mocha-init.js b/src/webui/www/public/scripts/mocha-init.js index 25ef028c8..ea9fba72a 100644 --- a/src/webui/www/public/scripts/mocha-init.js +++ b/src/webui/www/public/scripts/mocha-init.js @@ -1,11 +1,11 @@ /* ----------------------------------------------------------------- - ATTACH MOCHA LINK EVENTS - Notes: Here is where you define your windows and the events that open them. - If you are not using links to run Mocha methods you can remove this function. + ATTACH MOCHA LINK EVENTS + Notes: Here is where you define your windows and the events that open them. + If you are not using links to run Mocha methods you can remove this function. - If you need to add link events to links within windows you are creating, do - it in the onContentLoaded function of the new window. + If you need to add link events to links within windows you are creating, do + it in the onContentLoaded function of the new window. ----------------------------------------------------------------- */ /* Define localStorage object for older browsers */ @@ -164,10 +164,10 @@ initializeWindows = function() { deleteFN = function() { var h = myTable.selectedIds(); /*if(h.length && confirm('_(Are you sure you want to delete the selected torrents from the transfer list?)')) { - h.each(function(item, index){ - new Request({url: 'command/delete', method: 'post', data: {hash: item}}).send(); - }); - }*/ + h.each(function(item, index){ + new Request({url: 'command/delete', method: 'post', data: {hash: item}}).send(); + }); + }*/ if (h.length) { new MochaUI.Window({ id: 'confirmDeletionPage', From b4f39add085e64271ea7939b366d4f12241f7cf5 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 02:15:31 +0300 Subject: [PATCH 07/13] Update torrent list after operations with torrents. Immediately update torrent list after some operations with torrents. Such as changing priority, adding torrent, pause/resume, etc. --- src/webui/www/public/scripts/client.js | 226 ++++++++++----------- src/webui/www/public/scripts/mocha-init.js | 7 + 2 files changed, 120 insertions(+), 113 deletions(-) diff --git a/src/webui/www/public/scripts/client.js b/src/webui/www/public/scripts/client.js index 0d13ab518..6495df18b 100644 --- a/src/webui/www/public/scripts/client.js +++ b/src/webui/www/public/scripts/client.js @@ -24,6 +24,119 @@ myTable = new dynamicTable(); +var stateToImg = function (state) { + if (state == "pausedUP" || state == "pausedDL") { + state = "paused"; + } else { + if (state == "queuedUP" || state == "queuedDL") { + state = "queued"; + } else { + if (state == "checkingUP" || state == "checkingDL") { + state = "checking"; + } + } + } + return 'images/skin/' + state + '.png'; +}; + +var loadTorrentsInfoTimer; +var loadTorrentsInfo = function () { + var queueing_enabled = false; + var url = new URI('json/torrents'); + url.setData('filter', filter); + url.setData('sort', myTable.table.sortedColumn); + url.setData('reverse', myTable.table.reverseSort); + var request = new Request.JSON({ + url : url, + noCache : true, + method : 'get', + onFailure : function () { + $('error_div').set('html', '_(qBittorrent client is not reachable)'); + loadTorrentsInfoTimer = loadTorrentsInfo.delay(2000); + }, + onSuccess : function (events) { + $('error_div').set('html', ''); + if (events) { + // Add new torrents or update them + torrent_hashes = myTable.getRowIds(); + events_hashes = new Array(); + pos = 0; + events.each(function (event) { + events_hashes[events_hashes.length] = event.hash; + var row = new Array(); + var data = new Array(); + row.length = 10; + row[0] = stateToImg(event.state); + row[1] = event.name; + row[2] = event.priority > -1 ? event.priority : null; + data[2] = event.priority; + row[3] = friendlyUnit(event.size, false); + data[3] = event.size; + row[4] = (event.progress * 100).round(1); + if (row[4] == 100.0 && event.progress != 1.0) + row[4] = 99.9; + data[4] = event.progress; + row[5] = event.num_seeds; + if (event.num_complete != -1) + row[5] += " (" + event.num_complete + ")"; + data[5] = event.num_seeds; + row[6] = event.num_leechs; + if (event.num_incomplete != -1) + row[6] += " (" + event.num_incomplete + ")"; + data[6] = event.num_leechs; + row[7] = friendlyUnit(event.dlspeed, true); + data[7] = event.dlspeed; + row[8] = friendlyUnit(event.upspeed, true); + data[8] = event.upspeed; + row[9] = friendlyDuration(event.eta); + data[9] = event.eta; + if (event.ratio == -1) + row[10] = "∞"; + else + row[10] = (Math.floor(100 * event.ratio) / 100).toFixed(2); //Don't round up + data[10] = event.ratio; + if (row[2] != null) + queueing_enabled = true; + if (!torrent_hashes.contains(event.hash)) { + // New unfinished torrent + torrent_hashes[torrent_hashes.length] = event.hash; + //alert("Inserting row"); + myTable.insertRow(event.hash, row, data, event.state, pos); + } else { + // Update torrent data + myTable.updateRow(event.hash, row, data, event.state, pos); + } + + pos++; + }); + // Remove deleted torrents + torrent_hashes.each(function (hash) { + if (!events_hashes.contains(hash)) { + myTable.removeRow(hash); + } + }); + if (queueing_enabled) { + $('queueingButtons').removeClass('invisible'); + $('queueingMenuItems').removeClass('invisible'); + myTable.showPriority(); + } else { + $('queueingButtons').addClass('invisible'); + $('queueingMenuItems').addClass('invisible'); + myTable.hidePriority(); + } + + myTable.altRow(); + } + loadTorrentsInfoTimer = loadTorrentsInfo.delay(1500); + } + }).send(); +}; + +var updateTransferList = function() { + clearTimeout(loadTorrentsInfoTimer); + loadTorrentsInfoTimer = loadTorrentsInfo(); +} + window.addEvent('load', function () { var saveColumnSizes = function () { @@ -80,21 +193,6 @@ window.addEvent('load', function () { if (!speedInTitle) $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0'; - var stateToImg = function (state) { - if (state == "pausedUP" || state == "pausedDL") { - state = "paused"; - } else { - if (state == "queuedUP" || state == "queuedDL") { - state = "queued"; - } else { - if (state == "checkingUP" || state == "checkingDL") { - state = "checking"; - } - } - } - return 'images/skin/' + state + '.png'; - }; - var loadTransferInfoTimer; var loadTransferInfo = function () { var url = 'json/transferInfo'; @@ -135,104 +233,6 @@ window.addEvent('load', function () { $('DlInfos').addEvent('click', globalDownloadLimitFN); $('UpInfos').addEvent('click', globalUploadLimitFN); - var ajaxfnTimer; - var ajaxfn = function () { - var queueing_enabled = false; - var url = new URI('json/torrents'); - url.setData('filter', filter); - url.setData('sort', myTable.table.sortedColumn); - url.setData('reverse', myTable.table.reverseSort); - var request = new Request.JSON({ - url : url, - noCache : true, - method : 'get', - onFailure : function () { - $('error_div').set('html', '_(qBittorrent client is not reachable)'); - ajaxfnTimer = ajaxfn.delay(2000); - }, - onSuccess : function (events) { - $('error_div').set('html', ''); - if (events) { - // Add new torrents or update them - torrent_hashes = myTable.getRowIds(); - events_hashes = new Array(); - pos = 0; - events.each(function (event) { - events_hashes[events_hashes.length] = event.hash; - var row = new Array(); - var data = new Array(); - row.length = 10; - row[0] = stateToImg(event.state); - row[1] = event.name; - row[2] = event.priority > -1 ? event.priority : null; - data[2] = event.priority; - row[3] = friendlyUnit(event.size, false); - data[3] = event.size; - row[4] = (event.progress * 100).round(1); - if (row[4] == 100.0 && event.progress != 1.0) - row[4] = 99.9; - data[4] = event.progress; - row[5] = event.num_seeds; - if (event.num_complete != -1) - row[5] += " (" + event.num_complete + ")"; - data[5] = event.num_seeds; - row[6] = event.num_leechs; - if (event.num_incomplete != -1) - row[6] += " (" + event.num_incomplete + ")"; - data[6] = event.num_leechs; - row[7] = friendlyUnit(event.dlspeed, true); - data[7] = event.dlspeed; - row[8] = friendlyUnit(event.upspeed, true); - data[8] = event.upspeed; - row[9] = friendlyDuration(event.eta); - data[9] = event.eta; - if (event.ratio == -1) - row[10] = "∞"; - else - row[10] = (Math.floor(100 * event.ratio) / 100).toFixed(2); //Don't round up - data[10] = event.ratio; - if (row[2] != null) - queueing_enabled = true; - if (!torrent_hashes.contains(event.hash)) { - // New unfinished torrent - torrent_hashes[torrent_hashes.length] = event.hash; - //alert("Inserting row"); - myTable.insertRow(event.hash, row, data, event.state, pos); - } else { - // Update torrent data - myTable.updateRow(event.hash, row, data, event.state, pos); - } - - pos++; - }); - // Remove deleted torrents - torrent_hashes.each(function (hash) { - if (!events_hashes.contains(hash)) { - myTable.removeRow(hash); - } - }); - if (queueing_enabled) { - $('queueingButtons').removeClass('invisible'); - $('queueingMenuItems').removeClass('invisible'); - myTable.showPriority(); - } else { - $('queueingButtons').addClass('invisible'); - $('queueingMenuItems').addClass('invisible'); - myTable.hidePriority(); - } - - myTable.altRow(); - } - ajaxfnTimer = ajaxfn.delay(1500); - } - }).send(); - }; - - var updateTransferList = function() { - clearTimeout(ajaxfnTimer); - ajaxfnTimer = ajaxfn(); - } - setSortedColumn = function (column) { myTable.setSortedColumn(column); updateTransferList(); diff --git a/src/webui/www/public/scripts/mocha-init.js b/src/webui/www/public/scripts/mocha-init.js index ea9fba72a..5d6676a51 100644 --- a/src/webui/www/public/scripts/mocha-init.js +++ b/src/webui/www/public/scripts/mocha-init.js @@ -48,6 +48,7 @@ initializeWindows = function() { width: 500, height: 300 }); + updateTransferList(); }); addClickEvent('preferences', function(e) { @@ -87,6 +88,7 @@ initializeWindows = function() { width: 600, height: 130 }); + updateTransferList(); }); globalUploadLimitFN = function() { @@ -181,6 +183,7 @@ initializeWindows = function() { width: 424, height: 140 }); + updateTransferList(); } }; @@ -201,6 +204,7 @@ initializeWindows = function() { } }).send(); }); + updateTransferList(); } }; @@ -216,6 +220,7 @@ initializeWindows = function() { } }).send(); }); + updateTransferList(); } }; @@ -248,6 +253,7 @@ initializeWindows = function() { } }).send(); }); + updateTransferList(); } }); @@ -276,6 +282,7 @@ initializeWindows = function() { hashes: h.join("|") } }).send(); + updateTransferList(); } } From 5b604ac2511019f79723d77705b0ebcf2a829375 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 03:03:53 +0300 Subject: [PATCH 08/13] Save torrent list sort order in local storage --- src/webui/www/public/scripts/dynamicTable.js | 10 ++++++---- src/webui/www/public/scripts/mocha-init.js | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/webui/www/public/scripts/dynamicTable.js b/src/webui/www/public/scripts/dynamicTable.js index b1687b04f..4e032b362 100644 --- a/src/webui/www/public/scripts/dynamicTable.js +++ b/src/webui/www/public/scripts/dynamicTable.js @@ -42,18 +42,20 @@ var dynamicTable = new Class({ this.priority_hidden = false; this.progressIndex = progressIndex; this.context_menu = context_menu; - this.table.sortedColumn = 'name'; // Default is NAME - this.table.reverseSort = false; + this.table.sortedColumn = getLocalStorageItem('sorted_column', 'name'); + this.table.reverseSort = getLocalStorageItem('reverse_sort', 'false');; }, setSortedColumn : function (column) { if (column != this.table.sortedColumn) { this.table.sortedColumn = column; - this.table.reverseSort = false; + this.table.reverseSort = 'false'; } else { // Toggle sort order - this.table.reverseSort = !this.table.reverseSort; + this.table.reverseSort = this.table.reverseSort == 'true' ? 'false' : 'true'; } + localStorage.setItem('sorted_column', column); + localStorage.setItem('reverse_sort', this.table.reverseSort); }, getCurrentTorrentHash : function () { diff --git a/src/webui/www/public/scripts/mocha-init.js b/src/webui/www/public/scripts/mocha-init.js index 5d6676a51..651bf19eb 100644 --- a/src/webui/www/public/scripts/mocha-init.js +++ b/src/webui/www/public/scripts/mocha-init.js @@ -22,6 +22,13 @@ if (typeof localStorage == 'undefined') { } } +function getLocalStorageItem(name, defaultVal) { + val = localStorage.getItem(name); + if (val === null || val === undefined) + val = defaultVal; + return val; +} + initializeWindows = function() { function addClickEvent(el, fn) { From 44992056cf96b572140272034b270083a0fec5c8 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 04:31:04 +0300 Subject: [PATCH 09/13] Restore selected filter on page load --- src/webui/www/public/filters.html | 11 +------- src/webui/www/public/scripts/client.js | 38 +++++++++++++++----------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/webui/www/public/filters.html b/src/webui/www/public/filters.html index c18cae28a..6e220a9de 100644 --- a/src/webui/www/public/filters.html +++ b/src/webui/www/public/filters.html @@ -5,13 +5,4 @@
  • _(Paused)
  • _(Active)
  • _(Inactive)
  • - - - + \ No newline at end of file diff --git a/src/webui/www/public/scripts/client.js b/src/webui/www/public/scripts/client.js index 6495df18b..9a4f7b9ba 100644 --- a/src/webui/www/public/scripts/client.js +++ b/src/webui/www/public/scripts/client.js @@ -39,6 +39,8 @@ var stateToImg = function (state) { return 'images/skin/' + state + '.png'; }; +filter = getLocalStorageItem('selected_filter', 'all'); + var loadTorrentsInfoTimer; var loadTorrentsInfo = function () { var queueing_enabled = false; @@ -172,6 +174,23 @@ window.addEvent('load', function () { resizeLimit : [100, 300] }); MochaUI.Desktop.setDesktopSize(); + + setFilter = function (f) { + // Visually Select the right filter + $("all_filter").removeClass("selectedFilter"); + $("downloading_filter").removeClass("selectedFilter"); + $("completed_filter").removeClass("selectedFilter"); + $("paused_filter").removeClass("selectedFilter"); + $("active_filter").removeClass("selectedFilter"); + $("inactive_filter").removeClass("selectedFilter"); + $(f + "_filter").addClass("selectedFilter"); + filter = f; + localStorage.setItem('selected_filter', f); + // Reload torrents + if (typeof myTable.table != 'undefined') + updateTransferList(); + } + new MochaUI.Panel({ id : 'Filters', title : 'Panel', @@ -184,6 +203,9 @@ window.addEvent('load', function () { }, loadMethod : 'xhr', contentURL : 'filters.html', + onContentLoaded : function () { + setFilter(filter); + }, column : 'filtersColumn', height : 300 }); @@ -290,22 +312,6 @@ window.addEvent('load', function () { column : 'mainColumn', height : prop_h }); - - setFilter = function (f) { - // Visually Select the right filter - $("all_filter").removeClass("selectedFilter"); - $("downloading_filter").removeClass("selectedFilter"); - $("completed_filter").removeClass("selectedFilter"); - $("paused_filter").removeClass("selectedFilter"); - $("active_filter").removeClass("selectedFilter"); - $("inactive_filter").removeClass("selectedFilter"); - $(f + "_filter").addClass("selectedFilter"); - filter = f; - localStorage.setItem('selected_filter', f); - // Reload torrents - updateTransferList(); - } - }); function closeWindows() { From 664479145881363cb64c352e3f61c6a6ab08b2b7 Mon Sep 17 00:00:00 2001 From: buinsky Date: Mon, 8 Dec 2014 04:40:58 +0300 Subject: [PATCH 10/13] Follow project coding style. Issue #2192. --- src/qtlibtorrent/qtorrenthandle.cpp | 859 +++++++------- src/qtlibtorrent/qtorrenthandle.h | 168 +-- src/transferlistwidget.cpp | 1642 ++++++++++++++------------- src/transferlistwidget.h | 115 +- src/webui/btjson.cpp | 25 +- src/webui/www/public/filters.html | 16 +- 6 files changed, 1456 insertions(+), 1369 deletions(-) diff --git a/src/qtlibtorrent/qtorrenthandle.cpp b/src/qtlibtorrent/qtorrenthandle.cpp index e81d0fa99..62b03ce5c 100644 --- a/src/qtlibtorrent/qtorrenthandle.cpp +++ b/src/qtlibtorrent/qtorrenthandle.cpp @@ -56,358 +56,395 @@ using namespace std; static QPair get_file_extremity_pieces(const torrent_info& t, int file_index) { - const int num_pieces = t.num_pieces(); - const int piece_size = t.piece_length(); - const file_entry& file = t.file_at(file_index); + const int num_pieces = t.num_pieces(); + const int piece_size = t.piece_length(); + const file_entry& file = t.file_at(file_index); - // Determine the first and last piece of the file - int first_piece = floor((file.offset + 1) / (float) piece_size); - Q_ASSERT(first_piece >= 0 && first_piece < num_pieces); - qDebug("First piece of the file is %d/%d", first_piece, num_pieces - 1); + // Determine the first and last piece of the file + int first_piece = floor((file.offset + 1) / (float) piece_size); + Q_ASSERT(first_piece >= 0 && first_piece < num_pieces); + qDebug("First piece of the file is %d/%d", first_piece, num_pieces - 1); - int num_pieces_in_file = ceil(file.size / (float) piece_size); - int last_piece = first_piece + num_pieces_in_file - 1; - Q_ASSERT(last_piece >= 0 && last_piece < num_pieces); - qDebug("last piece of the file is %d/%d", last_piece, num_pieces - 1); + int num_pieces_in_file = ceil(file.size / (float) piece_size); + int last_piece = first_piece + num_pieces_in_file - 1; + Q_ASSERT(last_piece >= 0 && last_piece < num_pieces); + qDebug("last piece of the file is %d/%d", last_piece, num_pieces - 1); - return qMakePair(first_piece, last_piece); + return qMakePair(first_piece, last_piece); } -QTorrentHandle::QTorrentHandle(const torrent_handle& h): torrent_handle(h) {} +QTorrentHandle::QTorrentHandle(const torrent_handle& h): torrent_handle(h) +{ +} // // Getters // -QString QTorrentHandle::hash() const { - return misc::toQString(torrent_handle::info_hash()); +QString QTorrentHandle::hash() const +{ + return misc::toQString(torrent_handle::info_hash()); } -QString QTorrentHandle::name() const { - QString name = TorrentPersistentData::getName(hash()); - if (name.isEmpty()) { +QString QTorrentHandle::name() const +{ + QString name = TorrentPersistentData::getName(hash()); + if (name.isEmpty()) { #if LIBTORRENT_VERSION_NUM < 10000 - name = misc::toQStringU(torrent_handle::name()); + name = misc::toQStringU(torrent_handle::name()); #else - name = misc::toQStringU(status(query_name).name); + name = misc::toQStringU(status(query_name).name); #endif - } - return name; + } + return name; } -QString QTorrentHandle::creation_date() const { +QString QTorrentHandle::creation_date() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - boost::optional t = torrent_handle::get_torrent_info().creation_date(); + boost::optional t = torrent_handle::get_torrent_info().creation_date(); #else - boost::optional t = torrent_handle::torrent_file()->creation_date(); + boost::optional t = torrent_handle::torrent_file()->creation_date(); #endif - return t ? misc::toQString(*t) : ""; + return t ? misc::toQString(*t) : ""; } -qlonglong QTorrentHandle::creation_date_unix() const { +qlonglong QTorrentHandle::creation_date_unix() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - boost::optional t = torrent_handle::get_torrent_info().creation_date(); + boost::optional t = torrent_handle::get_torrent_info().creation_date(); #else - boost::optional t = torrent_handle::torrent_file()->creation_date(); + boost::optional t = torrent_handle::torrent_file()->creation_date(); #endif - return t ? *t : -1; + return t ? *t : -1; } -QString QTorrentHandle::current_tracker() const { - return misc::toQString(status(0x0).current_tracker); +QString QTorrentHandle::current_tracker() const +{ + return misc::toQString(status(0x0).current_tracker); } -bool QTorrentHandle::is_paused() const { - return is_paused(status(0x0)); +bool QTorrentHandle::is_paused() const +{ + return is_paused(status(0x0)); } -bool QTorrentHandle::is_queued() const { - return is_queued(status(0x0)); +bool QTorrentHandle::is_queued() const +{ + return is_queued(status(0x0)); } -size_type QTorrentHandle::total_size() const { +size_type QTorrentHandle::total_size() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().total_size(); + return torrent_handle::get_torrent_info().total_size(); #else - return torrent_handle::torrent_file()->total_size(); + return torrent_handle::torrent_file()->total_size(); #endif } -size_type QTorrentHandle::piece_length() const { +size_type QTorrentHandle::piece_length() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().piece_length(); + return torrent_handle::get_torrent_info().piece_length(); #else - return torrent_handle::torrent_file()->piece_length(); + return torrent_handle::torrent_file()->piece_length(); #endif } -int QTorrentHandle::num_pieces() const { +int QTorrentHandle::num_pieces() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().num_pieces(); + return torrent_handle::get_torrent_info().num_pieces(); #else - return torrent_handle::torrent_file()->num_pieces(); + return torrent_handle::torrent_file()->num_pieces(); #endif } -bool QTorrentHandle::first_last_piece_first() const { +bool QTorrentHandle::first_last_piece_first() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const* t = &get_torrent_info(); + torrent_info const* t = &get_torrent_info(); #else - boost::intrusive_ptr t = torrent_file(); + boost::intrusive_ptr t = torrent_file(); #endif - // Get int first media file - int index = 0; - for (index = 0; index < t->num_files(); ++index) { - QString path = misc::toQStringU(t->file_at(index).path); - const QString ext = fsutils::fileExtension(path); - if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) - break; - } - - if (index >= t->num_files()) // No media file - return false; + // Get int first media file + int index = 0; + for (index = 0; index < t->num_files(); ++index) { + QString path = misc::toQStringU(t->file_at(index).path); + const QString ext = fsutils::fileExtension(path); + if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) + break; + } + if (index >= t->num_files()) // No media file + return false; - QPair extremities = get_file_extremity_pieces(*t, index); + QPair extremities = get_file_extremity_pieces(*t, index); - return (torrent_handle::piece_priority(extremities.first) == 7) - && (torrent_handle::piece_priority(extremities.second) == 7); + return (torrent_handle::piece_priority(extremities.first) == 7) + && (torrent_handle::piece_priority(extremities.second) == 7); } -QString QTorrentHandle::save_path() const { +QString QTorrentHandle::save_path() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return fsutils::fromNativePath(misc::toQStringU(torrent_handle::save_path())); + return fsutils::fromNativePath(misc::toQStringU(torrent_handle::save_path())); #else - return fsutils::fromNativePath(misc::toQStringU(status(torrent_handle::query_save_path).save_path)); + return fsutils::fromNativePath(misc::toQStringU(status(torrent_handle::query_save_path).save_path)); #endif } -QString QTorrentHandle::save_path_parsed() const { +QString QTorrentHandle::save_path_parsed() const +{ QString p; if (has_metadata() && num_files() == 1) { - p = firstFileSavePath(); - } else { - p = fsutils::fromNativePath(TorrentPersistentData::getSavePath(hash())); - if (p.isEmpty()) - p = save_path(); + p = firstFileSavePath(); + } + else { + p = fsutils::fromNativePath(TorrentPersistentData::getSavePath(hash())); + if (p.isEmpty()) + p = save_path(); } return p; } -QStringList QTorrentHandle::url_seeds() const { - QStringList res; - try { - const std::set existing_seeds = torrent_handle::url_seeds(); - - std::set::const_iterator it = existing_seeds.begin(); - std::set::const_iterator itend = existing_seeds.end(); - for ( ; it != itend; ++it) { - qDebug("URL Seed: %s", it->c_str()); - res << misc::toQString(*it); +QStringList QTorrentHandle::url_seeds() const +{ + QStringList res; + try { + const std::set existing_seeds = torrent_handle::url_seeds(); + + std::set::const_iterator it = existing_seeds.begin(); + std::set::const_iterator itend = existing_seeds.end(); + for (; it != itend; ++it) { + qDebug("URL Seed: %s", it->c_str()); + res << misc::toQString(*it); + } + } catch(std::exception &e) { + std::cout << "ERROR: Failed to convert the URL seed" << std::endl; } - } catch(std::exception &e) { - std::cout << "ERROR: Failed to convert the URL seed" << std::endl; - } - return res; + return res; } // get the size of the torrent without the filtered files -size_type QTorrentHandle::actual_size() const { - return status(query_accurate_download_counters).total_wanted; +size_type QTorrentHandle::actual_size() const +{ + return status(query_accurate_download_counters).total_wanted; } -bool QTorrentHandle::has_filtered_pieces() const { - const std::vector piece_priorities = torrent_handle::piece_priorities(); - foreach (const int priority, piece_priorities) { - if (priority == 0) - return true; - } - return false; +bool QTorrentHandle::has_filtered_pieces() const +{ + const std::vector piece_priorities = torrent_handle::piece_priorities(); + foreach (const int priority, piece_priorities) + if (priority == 0) + return true; + return false; } -int QTorrentHandle::num_files() const { +int QTorrentHandle::num_files() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().num_files(); + return torrent_handle::get_torrent_info().num_files(); #else - return torrent_handle::torrent_file()->num_files(); + return torrent_handle::torrent_file()->num_files(); #endif } -QString QTorrentHandle::filename_at(unsigned int index) const { +QString QTorrentHandle::filename_at(unsigned int index) const +{ #if LIBTORRENT_VERSION_NUM < 10000 - Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files()); + Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files()); #else - Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files()); + Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files()); #endif - return fsutils::fileName(filepath_at(index)); + return fsutils::fileName(filepath_at(index)); } -size_type QTorrentHandle::filesize_at(unsigned int index) const { +size_type QTorrentHandle::filesize_at(unsigned int index) const +{ #if LIBTORRENT_VERSION_NUM < 10000 - Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files()); - return torrent_handle::get_torrent_info().files().file_size(index); + Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files()); + return torrent_handle::get_torrent_info().files().file_size(index); #else - Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files()); - return torrent_handle::torrent_file()->files().file_size(index); + Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files()); + return torrent_handle::torrent_file()->files().file_size(index); #endif } -QString QTorrentHandle::filepath_at(unsigned int index) const { +QString QTorrentHandle::filepath_at(unsigned int index) const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return filepath_at(torrent_handle::get_torrent_info(), index); + return filepath_at(torrent_handle::get_torrent_info(), index); #else - return filepath_at(*torrent_handle::torrent_file(), index); + return filepath_at(*torrent_handle::torrent_file(), index); #endif } -QString QTorrentHandle::orig_filepath_at(unsigned int index) const { +QString QTorrentHandle::orig_filepath_at(unsigned int index) const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return fsutils::fromNativePath(misc::toQStringU(torrent_handle::get_torrent_info().orig_files().file_path(index))); + return fsutils::fromNativePath(misc::toQStringU(torrent_handle::get_torrent_info().orig_files().file_path(index))); #else - return fsutils::fromNativePath(misc::toQStringU(torrent_handle::torrent_file()->orig_files().file_path(index))); + return fsutils::fromNativePath(misc::toQStringU(torrent_handle::torrent_file()->orig_files().file_path(index))); #endif } -torrent_status::state_t QTorrentHandle::state() const { - return status(0x0).state; +torrent_status::state_t QTorrentHandle::state() const +{ + return status(0x0).state; } -QString QTorrentHandle::creator() const { +QString QTorrentHandle::creator() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return misc::toQStringU(torrent_handle::get_torrent_info().creator()); + return misc::toQStringU(torrent_handle::get_torrent_info().creator()); #else - return misc::toQStringU(torrent_handle::torrent_file()->creator()); + return misc::toQStringU(torrent_handle::torrent_file()->creator()); #endif } -QString QTorrentHandle::comment() const { +QString QTorrentHandle::comment() const +{ #if LIBTORRENT_VERSION_NUM < 10000 - return misc::toQStringU(torrent_handle::get_torrent_info().comment()); + return misc::toQStringU(torrent_handle::get_torrent_info().comment()); #else - return misc::toQStringU(torrent_handle::torrent_file()->comment()); + return misc::toQStringU(torrent_handle::torrent_file()->comment()); #endif } -bool QTorrentHandle::is_checking() const { - return is_checking(status(0x0)); +bool QTorrentHandle::is_checking() const +{ + return is_checking(status(0x0)); } // Return a list of absolute paths corresponding // to all files in a torrent -QStringList QTorrentHandle::absolute_files_path() const { - QDir saveDir(save_path()); - QStringList res; - for (int i = 0; i fp = torrent_handle::file_priorities(); - for (uint i = 0; i < fp.size(); ++i) { - if (fp[i] == 0) { - const QString file_path = fsutils::expandPathAbs(saveDir.absoluteFilePath(filepath_at(i))); - if (file_path.contains(".unwanted")) - res << file_path; +QStringList QTorrentHandle::absolute_files_path() const +{ + QDir saveDir(save_path()); + QStringList res; + for (int i = 0; i fp = torrent_handle::file_priorities(); + for (uint i = 0; i < fp.size(); ++i) { + if (fp[i] == 0) { + const QString file_path = fsutils::expandPathAbs(saveDir.absoluteFilePath(filepath_at(i))); + if (file_path.contains(".unwanted")) + res << file_path; + } } - } - return res; + return res; } -bool QTorrentHandle::has_missing_files() const { - const QStringList paths = absolute_files_path(); - foreach (const QString &path, paths) { - if (!QFile::exists(path)) return true; - } - return false; +bool QTorrentHandle::has_missing_files() const +{ + const QStringList paths = absolute_files_path(); + foreach (const QString &path, paths) + if (!QFile::exists(path)) return true; + return false; } -int QTorrentHandle::queue_position() const { - return queue_position(status(0x0)); +int QTorrentHandle::queue_position() const +{ + return queue_position(status(0x0)); } -bool QTorrentHandle::is_seed() const { - // Affected by bug http://code.rasterbar.com/libtorrent/ticket/402 - //return torrent_handle::is_seed(); - // May suffer from approximation problems - //return (progress() == 1.); - // This looks safe - return is_seed(status(0x0)); +bool QTorrentHandle::is_seed() const +{ + // Affected by bug http://code.rasterbar.com/libtorrent/ticket/402 + //return torrent_handle::is_seed(); + // May suffer from approximation problems + //return (progress() == 1.); + // This looks safe + return is_seed(status(0x0)); } -bool QTorrentHandle::is_sequential_download() const { - return status(0x0).sequential_download; +bool QTorrentHandle::is_sequential_download() const +{ + return status(0x0).sequential_download; } -bool QTorrentHandle::priv() const { - if (!has_metadata()) - return false; +bool QTorrentHandle::priv() const +{ + if (!has_metadata()) + return false; #if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().priv(); + return torrent_handle::get_torrent_info().priv(); #else - return torrent_handle::torrent_file()->priv(); + return torrent_handle::torrent_file()->priv(); #endif } -QString QTorrentHandle::firstFileSavePath() const { - Q_ASSERT(has_metadata()); - QString fsave_path = fsutils::fromNativePath(TorrentPersistentData::getSavePath(hash())); - if (fsave_path.isEmpty()) - fsave_path = save_path(); - if (!fsave_path.endsWith("/")) - fsave_path += "/"; - fsave_path += filepath_at(0); - // Remove .!qB extension - if (fsave_path.endsWith(".!qB", Qt::CaseInsensitive)) - fsave_path.chop(4); - return fsave_path; +QString QTorrentHandle::firstFileSavePath() const +{ + Q_ASSERT(has_metadata()); + QString fsave_path = fsutils::fromNativePath(TorrentPersistentData::getSavePath(hash())); + if (fsave_path.isEmpty()) + fsave_path = save_path(); + if (!fsave_path.endsWith("/")) + fsave_path += "/"; + fsave_path += filepath_at(0); + // Remove .!qB extension + if (fsave_path.endsWith(".!qB", Qt::CaseInsensitive)) + fsave_path.chop(4); + return fsave_path; } QString QTorrentHandle::root_path() const { - if (num_files() < 2) + if (num_files() < 2) + return save_path(); + QString first_filepath = filepath_at(0); + const int slashIndex = first_filepath.indexOf("/"); + if (slashIndex >= 0) + return QDir(save_path()).absoluteFilePath(first_filepath.left(slashIndex)); return save_path(); - QString first_filepath = filepath_at(0); - const int slashIndex = first_filepath.indexOf("/"); - if (slashIndex >= 0) - return QDir(save_path()).absoluteFilePath(first_filepath.left(slashIndex)); - return save_path(); } -bool QTorrentHandle::has_error() const { - return has_error(status(0x0)); +bool QTorrentHandle::has_error() const +{ + return has_error(status(0x0)); } -QString QTorrentHandle::error() const { - return misc::toQString(status(0x0).error); +QString QTorrentHandle::error() const +{ + return misc::toQString(status(0x0).error); } -void QTorrentHandle::downloading_pieces(bitfield &bf) const { - std::vector queue; - torrent_handle::get_download_queue(queue); +void QTorrentHandle::downloading_pieces(bitfield &bf) const +{ + std::vector queue; + torrent_handle::get_download_queue(queue); - std::vector::const_iterator it = queue.begin(); - std::vector::const_iterator itend = queue.end(); - for ( ; it!= itend; ++it) { - bf.set_bit(it->piece_index); - } - return; + std::vector::const_iterator it = queue.begin(); + std::vector::const_iterator itend = queue.end(); + for (; it!= itend; ++it) + bf.set_bit(it->piece_index); + return; } -bool QTorrentHandle::has_metadata() const { - return status(0x0).has_metadata; +bool QTorrentHandle::has_metadata() const +{ + return status(0x0).has_metadata; } -void QTorrentHandle::file_progress(std::vector& fp) const { +void QTorrentHandle::file_progress(std::vector& fp) const +{ torrent_handle::file_progress(fp, torrent_handle::piece_granularity); } -QTorrentState QTorrentHandle::torrentState() const { +QTorrentState QTorrentHandle::torrentState() const +{ QTorrentState state = QTorrentState::Unknown; libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters); @@ -456,275 +493,295 @@ qulonglong QTorrentHandle::eta() const // Setters // -void QTorrentHandle::pause() const { - torrent_handle::auto_managed(false); - torrent_handle::pause(); - torrent_handle::save_resume_data(); +void QTorrentHandle::pause() const +{ + torrent_handle::auto_managed(false); + torrent_handle::pause(); + torrent_handle::save_resume_data(); } -void QTorrentHandle::resume() const { - if (has_error()) - torrent_handle::clear_error(); - - const QString torrent_hash = hash(); - bool has_persistant_error = TorrentPersistentData::hasError(torrent_hash); - TorrentPersistentData::setErrorState(torrent_hash, false); - bool temp_path_enabled = Preferences::instance()->isTempPathEnabled(); - if (has_persistant_error && temp_path_enabled) { - // Torrent was supposed to be seeding, checking again in final destination - qDebug("Resuming a torrent with error..."); - const QString final_save_path = TorrentPersistentData::getSavePath(torrent_hash); - qDebug("Torrent final path is: %s", qPrintable(final_save_path)); - if (!final_save_path.isEmpty()) - move_storage(final_save_path); - } - torrent_handle::auto_managed(true); - torrent_handle::resume(); - if (has_persistant_error && temp_path_enabled) { - // Force recheck - torrent_handle::force_recheck(); - } +void QTorrentHandle::resume() const +{ + if (has_error()) + torrent_handle::clear_error(); + + const QString torrent_hash = hash(); + bool has_persistant_error = TorrentPersistentData::hasError(torrent_hash); + TorrentPersistentData::setErrorState(torrent_hash, false); + bool temp_path_enabled = Preferences::instance()->isTempPathEnabled(); + if (has_persistant_error && temp_path_enabled) { + // Torrent was supposed to be seeding, checking again in final destination + qDebug("Resuming a torrent with error..."); + const QString final_save_path = TorrentPersistentData::getSavePath(torrent_hash); + qDebug("Torrent final path is: %s", qPrintable(final_save_path)); + if (!final_save_path.isEmpty()) + move_storage(final_save_path); + } + torrent_handle::auto_managed(true); + torrent_handle::resume(); + if (has_persistant_error && temp_path_enabled) + // Force recheck + torrent_handle::force_recheck(); } -void QTorrentHandle::remove_url_seed(const QString& seed) const { - torrent_handle::remove_url_seed(seed.toStdString()); +void QTorrentHandle::remove_url_seed(const QString& seed) const +{ + torrent_handle::remove_url_seed(seed.toStdString()); } -void QTorrentHandle::add_url_seed(const QString& seed) const { - const std::string str_seed = seed.toStdString(); - qDebug("calling torrent_handle::add_url_seed(%s)", str_seed.c_str()); - torrent_handle::add_url_seed(str_seed); +void QTorrentHandle::add_url_seed(const QString& seed) const +{ + const std::string str_seed = seed.toStdString(); + qDebug("calling torrent_handle::add_url_seed(%s)", str_seed.c_str()); + torrent_handle::add_url_seed(str_seed); } -void QTorrentHandle::set_tracker_login(const QString& username, const QString& password) const { - torrent_handle::set_tracker_login(std::string(username.toLocal8Bit().constData()), std::string(password.toLocal8Bit().constData())); +void QTorrentHandle::set_tracker_login(const QString& username, const QString& password) const +{ + torrent_handle::set_tracker_login(std::string(username.toLocal8Bit().constData()), std::string(password.toLocal8Bit().constData())); } -void QTorrentHandle::move_storage(const QString& new_path) const { - QString hashstr = hash(); +void QTorrentHandle::move_storage(const QString& new_path) const +{ + QString hashstr = hash(); - if (TorrentTempData::isMoveInProgress(hashstr)) { - qDebug("enqueue move storage to %s", qPrintable(new_path)); - TorrentTempData::enqueueMove(hashstr, new_path); - } - else { - QString old_path = save_path(); + if (TorrentTempData::isMoveInProgress(hashstr)) { + qDebug("enqueue move storage to %s", qPrintable(new_path)); + TorrentTempData::enqueueMove(hashstr, new_path); + } + else { + QString old_path = save_path(); - qDebug("move storage: %s to %s", qPrintable(old_path), qPrintable(new_path)); + qDebug("move storage: %s to %s", qPrintable(old_path), qPrintable(new_path)); - if (QDir(old_path) == QDir(new_path)) - return; + if (QDir(old_path) == QDir(new_path)) + return; - TorrentTempData::startMove(hashstr, old_path, new_path); + TorrentTempData::startMove(hashstr, old_path, new_path); - // Create destination directory if necessary - // or move_storage() will fail... - QDir().mkpath(new_path); - // Actually move the storage - torrent_handle::move_storage(fsutils::toNativePath(new_path).toUtf8().constData()); - } + // Create destination directory if necessary + // or move_storage() will fail... + QDir().mkpath(new_path); + // Actually move the storage + torrent_handle::move_storage(fsutils::toNativePath(new_path).toUtf8().constData()); + } } -bool QTorrentHandle::save_torrent_file(const QString& path) const { - if (!has_metadata()) return false; +bool QTorrentHandle::save_torrent_file(const QString& path) const +{ + if (!has_metadata()) return false; #if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const* t = &get_torrent_info(); + torrent_info const* t = &get_torrent_info(); #else - boost::intrusive_ptr t = torrent_file(); + boost::intrusive_ptr t = torrent_file(); #endif - entry meta = bdecode(t->metadata().get(), - t->metadata().get() + t->metadata_size()); - entry torrent_entry(entry::dictionary_t); - torrent_entry["info"] = meta; - if (!torrent_handle::trackers().empty()) - torrent_entry["announce"] = torrent_handle::trackers().front().url; - - vector out; - bencode(back_inserter(out), torrent_entry); - QFile torrent_file(path); - if (!out.empty() && torrent_file.open(QIODevice::WriteOnly)) { - torrent_file.write(&out[0], out.size()); - torrent_file.close(); - return true; - } + entry meta = bdecode(t->metadata().get(), + t->metadata().get() + t->metadata_size()); + entry torrent_entry(entry::dictionary_t); + torrent_entry["info"] = meta; + if (!torrent_handle::trackers().empty()) + torrent_entry["announce"] = torrent_handle::trackers().front().url; + + vector out; + bencode(back_inserter(out), torrent_entry); + QFile torrent_file(path); + if (!out.empty() && torrent_file.open(QIODevice::WriteOnly)) { + torrent_file.write(&out[0], out.size()); + torrent_file.close(); + return true; + } - return false; + return false; } -void QTorrentHandle::file_priority(int index, int priority) const { - vector priorities = torrent_handle::file_priorities(); - if (priorities[index] != priority) { - priorities[index] = priority; - prioritize_files(priorities); - } +void QTorrentHandle::file_priority(int index, int priority) const +{ + vector priorities = torrent_handle::file_priorities(); + if (priorities[index] != priority) { + priorities[index] = priority; + prioritize_files(priorities); + } } -void QTorrentHandle::prioritize_files(const vector &files) const { +void QTorrentHandle::prioritize_files(const vector &files) const +{ #if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const& info = torrent_handle::get_torrent_info(); + torrent_info const& info = torrent_handle::get_torrent_info(); #else - boost::intrusive_ptr info_ptr = torrent_handle::torrent_file(); - torrent_info const& info = *info_ptr; + boost::intrusive_ptr info_ptr = torrent_handle::torrent_file(); + torrent_info const& info = *info_ptr; #endif - if ((int)files.size() != info.num_files()) return; - qDebug() << Q_FUNC_INFO; - bool was_seed = is_seed(); - qDebug() << Q_FUNC_INFO << "Changing files priorities..."; - torrent_handle::prioritize_files(files); - qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely..."; - - QString spath = save_path(); - - for (uint i = 0; i < files.size(); ++i) { - QString filepath = filepath_at(info, i); - // Move unwanted files to a .unwanted subfolder - if (files[i] == 0) { - QString old_abspath = QDir(spath).absoluteFilePath(filepath); - QString parent_abspath = fsutils::branchPath(old_abspath); - // Make sure the file does not already exists - if (QDir(parent_abspath).dirName() != ".unwanted") { - QString unwanted_abspath = parent_abspath+"/.unwanted"; - QString new_abspath = unwanted_abspath+"/"+fsutils::fileName(filepath); - qDebug() << "Unwanted path is" << unwanted_abspath; - if (QFile::exists(new_abspath)) { - qWarning() << "File" << new_abspath << "already exists at destination."; - continue; - } - bool created = QDir().mkpath(unwanted_abspath); + if ((int)files.size() != info.num_files()) return; + qDebug() << Q_FUNC_INFO; + bool was_seed = is_seed(); + qDebug() << Q_FUNC_INFO << "Changing files priorities..."; + torrent_handle::prioritize_files(files); + qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely..."; + + QString spath = save_path(); + + for (uint i = 0; i < files.size(); ++i) { + QString filepath = filepath_at(info, i); + // Move unwanted files to a .unwanted subfolder + if (files[i] == 0) { + QString old_abspath = QDir(spath).absoluteFilePath(filepath); + QString parent_abspath = fsutils::branchPath(old_abspath); + // Make sure the file does not already exists + if (QDir(parent_abspath).dirName() != ".unwanted") { + QString unwanted_abspath = parent_abspath + "/.unwanted"; + QString new_abspath = unwanted_abspath + "/" + fsutils::fileName(filepath); + qDebug() << "Unwanted path is" << unwanted_abspath; + if (QFile::exists(new_abspath)) { + qWarning() << "File" << new_abspath << "already exists at destination."; + continue; + } + bool created = QDir().mkpath(unwanted_abspath); #ifdef Q_OS_WIN - qDebug() << "unwanted folder was created:" << created; - if (created) { - // Hide the folder on Windows - qDebug() << "Hiding folder (Windows)"; - wstring win_path = fsutils::toNativePath(unwanted_abspath).toStdWString(); - DWORD dwAttrs = GetFileAttributesW(win_path.c_str()); - bool ret = SetFileAttributesW(win_path.c_str(), dwAttrs|FILE_ATTRIBUTE_HIDDEN); - Q_ASSERT(ret != 0); Q_UNUSED(ret); - } + qDebug() << "unwanted folder was created:" << created; + if (created) { + // Hide the folder on Windows + qDebug() << "Hiding folder (Windows)"; + wstring win_path = fsutils::toNativePath(unwanted_abspath).toStdWString(); + DWORD dwAttrs = GetFileAttributesW(win_path.c_str()); + bool ret = SetFileAttributesW(win_path.c_str(), dwAttrs | FILE_ATTRIBUTE_HIDDEN); + Q_ASSERT(ret != 0); Q_UNUSED(ret); + } #else - Q_UNUSED(created); + Q_UNUSED(created); #endif - QString parent_path = fsutils::branchPath(filepath); - if (!parent_path.isEmpty() && !parent_path.endsWith("/")) - parent_path += "/"; - rename_file(i, parent_path+".unwanted/"+fsutils::fileName(filepath)); - } - } - // Move wanted files back to their original folder - if (files[i] > 0) { - QString parent_relpath = fsutils::branchPath(filepath); - if (QDir(parent_relpath).dirName() == ".unwanted") { - QString old_name = fsutils::fileName(filepath); - QString new_relpath = fsutils::branchPath(parent_relpath); - if (new_relpath.isEmpty()) - rename_file(i, old_name); - else - rename_file(i, QDir(new_relpath).filePath(old_name)); - // Remove .unwanted directory if empty - qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + new_relpath).absoluteFilePath(".unwanted"); - QDir(spath + "/" + new_relpath).rmdir(".unwanted"); - } + QString parent_path = fsutils::branchPath(filepath); + if (!parent_path.isEmpty() && !parent_path.endsWith("/")) + parent_path += "/"; + rename_file(i, parent_path + ".unwanted/" + fsutils::fileName(filepath)); + } + } + // Move wanted files back to their original folder + if (files[i] > 0) { + QString parent_relpath = fsutils::branchPath(filepath); + if (QDir(parent_relpath).dirName() == ".unwanted") { + QString old_name = fsutils::fileName(filepath); + QString new_relpath = fsutils::branchPath(parent_relpath); + if (new_relpath.isEmpty()) + rename_file(i, old_name); + else + rename_file(i, QDir(new_relpath).filePath(old_name)); + // Remove .unwanted directory if empty + qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + new_relpath).absoluteFilePath(".unwanted"); + QDir(spath + "/" + new_relpath).rmdir(".unwanted"); + } + } } - } - - if (was_seed && !is_seed()) { - qDebug() << "Torrent is no longer SEEDING"; - // Save seed status - TorrentPersistentData::saveSeedStatus(*this); - // Move to temp folder if necessary - const Preferences* const pref = Preferences::instance(); - if (pref->isTempPathEnabled()) { - QString tmp_path = pref->getTempPath(); - qDebug() << "tmp folder is enabled, move torrent to " << tmp_path << " from " << spath; - move_storage(tmp_path); + + if (was_seed && !is_seed()) { + qDebug() << "Torrent is no longer SEEDING"; + // Save seed status + TorrentPersistentData::saveSeedStatus(*this); + // Move to temp folder if necessary + const Preferences* const pref = Preferences::instance(); + if (pref->isTempPathEnabled()) { + QString tmp_path = pref->getTempPath(); + qDebug() << "tmp folder is enabled, move torrent to " << tmp_path << " from " << spath; + move_storage(tmp_path); + } } - } } -void QTorrentHandle::prioritize_first_last_piece(int file_index, bool b) const { - // Determine the priority to set - int prio = b ? 7 : torrent_handle::file_priority(file_index); +void QTorrentHandle::prioritize_first_last_piece(int file_index, bool b) const +{ + // Determine the priority to set + int prio = b ? 7 : torrent_handle::file_priority(file_index); #if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const* tf = &get_torrent_info(); + torrent_info const* tf = &get_torrent_info(); #else - boost::intrusive_ptr tf = torrent_file(); + boost::intrusive_ptr tf = torrent_file(); #endif - QPair extremities = get_file_extremity_pieces(*tf, file_index); - piece_priority(extremities.first, prio); - piece_priority(extremities.second, prio); -} - -void QTorrentHandle::prioritize_first_last_piece(bool b) const { - if (!has_metadata()) return; - // Download first and last pieces first for all media files in the torrent - const uint nbfiles = num_files(); - for (uint index = 0; index < nbfiles; ++index) { - const QString path = filepath_at(index); - const QString ext = fsutils::fileExtension(path); - if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) { - qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first"; - prioritize_first_last_piece(index, b); + QPair extremities = get_file_extremity_pieces(*tf, file_index); + piece_priority(extremities.first, prio); + piece_priority(extremities.second, prio); +} + +void QTorrentHandle::prioritize_first_last_piece(bool b) const +{ + if (!has_metadata()) return; + // Download first and last pieces first for all media files in the torrent + const uint nbfiles = num_files(); + for (uint index = 0; index < nbfiles; ++index) { + const QString path = filepath_at(index); + const QString ext = fsutils::fileExtension(path); + if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) { + qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first"; + prioritize_first_last_piece(index, b); + } } - } } -void QTorrentHandle::rename_file(int index, const QString& name) const { - qDebug() << Q_FUNC_INFO << index << name; - torrent_handle::rename_file(index, std::string(fsutils::toNativePath(name).toUtf8().constData())); +void QTorrentHandle::rename_file(int index, const QString& name) const +{ + qDebug() << Q_FUNC_INFO << index << name; + torrent_handle::rename_file(index, std::string(fsutils::toNativePath(name).toUtf8().constData())); } // // Operators // -bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const { - return info_hash() == new_h.info_hash(); +bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const +{ + return info_hash() == new_h.info_hash(); } -bool QTorrentHandle::is_paused(const libtorrent::torrent_status &status) { - return status.paused && !status.auto_managed; +bool QTorrentHandle::is_paused(const libtorrent::torrent_status &status) +{ + return status.paused && !status.auto_managed; } -int QTorrentHandle::queue_position(const libtorrent::torrent_status &status) { - if (status.queue_position < 0) - return -1; - return status.queue_position+1; +int QTorrentHandle::queue_position(const libtorrent::torrent_status &status) +{ + if (status.queue_position < 0) + return -1; + return status.queue_position + 1; } -bool QTorrentHandle::is_queued(const libtorrent::torrent_status &status) { - return status.paused && status.auto_managed; +bool QTorrentHandle::is_queued(const libtorrent::torrent_status &status) +{ + return status.paused && status.auto_managed; } -bool QTorrentHandle::is_seed(const libtorrent::torrent_status &status) { - return status.state == torrent_status::finished - || status.state == torrent_status::seeding; +bool QTorrentHandle::is_seed(const libtorrent::torrent_status &status) +{ + return status.state == torrent_status::finished + || status.state == torrent_status::seeding; } -bool QTorrentHandle::is_checking(const libtorrent::torrent_status &status) { - return status.state == torrent_status::checking_files - || status.state == torrent_status::checking_resume_data; +bool QTorrentHandle::is_checking(const libtorrent::torrent_status &status) +{ + return status.state == torrent_status::checking_files + || status.state == torrent_status::checking_resume_data; } -bool QTorrentHandle::has_error(const libtorrent::torrent_status &status) { - return status.paused && !status.error.empty(); +bool QTorrentHandle::has_error(const libtorrent::torrent_status &status) +{ + return status.paused && !status.error.empty(); } -float QTorrentHandle::progress(const libtorrent::torrent_status &status) { - if (!status.total_wanted) - return 0.; - if (status.total_wanted_done == status.total_wanted) - return 1.; - float progress = (float) status.total_wanted_done / (float) status.total_wanted; - Q_ASSERT(progress >= 0.f && progress <= 1.f); - return progress; +float QTorrentHandle::progress(const libtorrent::torrent_status &status) +{ + if (!status.total_wanted) + return 0.; + if (status.total_wanted_done == status.total_wanted) + return 1.; + float progress = (float) status.total_wanted_done / (float) status.total_wanted; + Q_ASSERT(progress >= 0.f && progress <= 1.f); + return progress; } -QString QTorrentHandle::filepath_at(const libtorrent::torrent_info &info, unsigned int index) { - return fsutils::fromNativePath(misc::toQStringU(info.files().file_path(index))); +QString QTorrentHandle::filepath_at(const libtorrent::torrent_info &info, unsigned int index) +{ + return fsutils::fromNativePath(misc::toQStringU(info.files().file_path(index))); } diff --git a/src/qtlibtorrent/qtorrenthandle.h b/src/qtlibtorrent/qtorrenthandle.h index 6c25dcc96..a36593ba5 100644 --- a/src/qtlibtorrent/qtorrenthandle.h +++ b/src/qtlibtorrent/qtorrenthandle.h @@ -42,7 +42,8 @@ QT_END_NAMESPACE class QTorrentState { public: - enum { + enum + { Unknown = -1, Error, @@ -71,93 +72,94 @@ private: // A wrapper for torrent_handle in libtorrent // to interact well with Qt types -class QTorrentHandle : public libtorrent::torrent_handle { +class QTorrentHandle: public libtorrent::torrent_handle +{ public: - // - // Constructors - // - - QTorrentHandle() {} - explicit QTorrentHandle(const libtorrent::torrent_handle& h); - - // - // Getters - // - QString hash() const; - QString name() const; - QString current_tracker() const; - bool is_paused() const; - bool has_filtered_pieces() const; - libtorrent::size_type total_size() const; - libtorrent::size_type piece_length() const; - int num_pieces() const; - QString save_path() const; - QString save_path_parsed() const; - QStringList url_seeds() const; - libtorrent::size_type actual_size() const; - int num_files() const; - int queue_position() const; - bool is_queued() const; - QString filename_at(unsigned int index) const; - libtorrent::size_type filesize_at(unsigned int index) const; - QString filepath_at(unsigned int index) const; - QString orig_filepath_at(unsigned int index) const; - libtorrent::torrent_status::state_t state() const; - QString creator() const; - QString comment() const; - QStringList absolute_files_path() const; - QStringList absolute_files_path_uneeded() const; - bool has_missing_files() const; - bool is_seed() const; - bool is_checking() const; - bool is_sequential_download() const; - QString creation_date() const; - qlonglong creation_date_unix() const; - bool priv() const; - bool first_last_piece_first() const; - QString root_path() const; - QString firstFileSavePath() const; - bool has_error() const; - QString error() const; - void downloading_pieces(libtorrent::bitfield& bf) const; - bool has_metadata() const; - void file_progress(std::vector& fp) const; - QTorrentState torrentState() const; - qulonglong eta() const; - - // - // Setters - // - void pause() const; - void resume() const; - void remove_url_seed(const QString& seed) const; - void add_url_seed(const QString& seed) const; - void set_tracker_login(const QString& username, const QString& password) const; - void move_storage(const QString& path) const; - void prioritize_first_last_piece(bool b) const; - void rename_file(int index, const QString& name) const; - bool save_torrent_file(const QString& path) const; - void prioritize_files(const std::vector& files) const; - void file_priority(int index, int priority) const; - - // - // Operators - // - bool operator ==(const QTorrentHandle& new_h) const; - - static bool is_paused(const libtorrent::torrent_status &status); - static int queue_position(const libtorrent::torrent_status &status); - static bool is_queued(const libtorrent::torrent_status &status); - static bool is_seed(const libtorrent::torrent_status &status); - static bool is_checking(const libtorrent::torrent_status &status); - static bool has_error(const libtorrent::torrent_status &status); - static float progress(const libtorrent::torrent_status &status); - static QString filepath_at(const libtorrent::torrent_info &info, unsigned int index); + // + // Constructors + // + + QTorrentHandle() {} + explicit QTorrentHandle(const libtorrent::torrent_handle& h); + + // + // Getters + // + QString hash() const; + QString name() const; + QString current_tracker() const; + bool is_paused() const; + bool has_filtered_pieces() const; + libtorrent::size_type total_size() const; + libtorrent::size_type piece_length() const; + int num_pieces() const; + QString save_path() const; + QString save_path_parsed() const; + QStringList url_seeds() const; + libtorrent::size_type actual_size() const; + int num_files() const; + int queue_position() const; + bool is_queued() const; + QString filename_at(unsigned int index) const; + libtorrent::size_type filesize_at(unsigned int index) const; + QString filepath_at(unsigned int index) const; + QString orig_filepath_at(unsigned int index) const; + libtorrent::torrent_status::state_t state() const; + QString creator() const; + QString comment() const; + QStringList absolute_files_path() const; + QStringList absolute_files_path_uneeded() const; + bool has_missing_files() const; + bool is_seed() const; + bool is_checking() const; + bool is_sequential_download() const; + QString creation_date() const; + qlonglong creation_date_unix() const; + bool priv() const; + bool first_last_piece_first() const; + QString root_path() const; + QString firstFileSavePath() const; + bool has_error() const; + QString error() const; + void downloading_pieces(libtorrent::bitfield& bf) const; + bool has_metadata() const; + void file_progress(std::vector& fp) const; + QTorrentState torrentState() const; + qulonglong eta() const; + + // + // Setters + // + void pause() const; + void resume() const; + void remove_url_seed(const QString& seed) const; + void add_url_seed(const QString& seed) const; + void set_tracker_login(const QString& username, const QString& password) const; + void move_storage(const QString& path) const; + void prioritize_first_last_piece(bool b) const; + void rename_file(int index, const QString& name) const; + bool save_torrent_file(const QString& path) const; + void prioritize_files(const std::vector& files) const; + void file_priority(int index, int priority) const; + + // + // Operators + // + bool operator ==(const QTorrentHandle& new_h) const; + + static bool is_paused(const libtorrent::torrent_status &status); + static int queue_position(const libtorrent::torrent_status &status); + static bool is_queued(const libtorrent::torrent_status &status); + static bool is_seed(const libtorrent::torrent_status &status); + static bool is_checking(const libtorrent::torrent_status &status); + static bool has_error(const libtorrent::torrent_status &status); + static float progress(const libtorrent::torrent_status &status); + static QString filepath_at(const libtorrent::torrent_info &info, unsigned int index); private: - void prioritize_first_last_piece(int file_index, bool b) const; + void prioritize_first_last_piece(int file_index, bool b) const; }; diff --git a/src/transferlistwidget.cpp b/src/transferlistwidget.cpp index 2b9a09614..382e76359 100644 --- a/src/transferlistwidget.cpp +++ b/src/transferlistwidget.cpp @@ -68,858 +68,884 @@ using namespace libtorrent; TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *main_window, QBtSession *_BTSession): - QTreeView(parent), BTSession(_BTSession), main_window(main_window) { - - setUniformRowHeights(true); - // Load settings - bool column_loaded = loadSettings(); - - // Create and apply delegate - listDelegate = new TransferListDelegate(this); - setItemDelegate(listDelegate); - - // Create transfer list model - listModel = new TorrentModel(this); - - nameFilterModel = new TransferListSortModel(); - nameFilterModel->setDynamicSortFilter(true); - nameFilterModel->setSourceModel(listModel); - nameFilterModel->setFilterKeyColumn(TorrentModelItem::TR_NAME); - nameFilterModel->setFilterRole(Qt::DisplayRole); - nameFilterModel->setSortCaseSensitivity(Qt::CaseInsensitive); - - setModel(nameFilterModel); - - // Visual settings - setRootIsDecorated(false); - setAllColumnsShowFocus(true); - setSortingEnabled(true); - setSelectionMode(QAbstractItemView::ExtendedSelection); - setItemsExpandable(false); - setAutoScroll(true); - setDragDropMode(QAbstractItemView::DragOnly); + QTreeView(parent), BTSession(_BTSession), main_window(main_window) +{ + + setUniformRowHeights(true); + // Load settings + bool column_loaded = loadSettings(); + + // Create and apply delegate + listDelegate = new TransferListDelegate(this); + setItemDelegate(listDelegate); + + // Create transfer list model + listModel = new TorrentModel(this); + + nameFilterModel = new TransferListSortModel(); + nameFilterModel->setDynamicSortFilter(true); + nameFilterModel->setSourceModel(listModel); + nameFilterModel->setFilterKeyColumn(TorrentModelItem::TR_NAME); + nameFilterModel->setFilterRole(Qt::DisplayRole); + nameFilterModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + setModel(nameFilterModel); + + // Visual settings + setRootIsDecorated(false); + setAllColumnsShowFocus(true); + setSortingEnabled(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setItemsExpandable(false); + setAutoScroll(true); + setDragDropMode(QAbstractItemView::DragOnly); #if defined(Q_OS_MAC) - setAttribute(Qt::WA_MacShowFocusRect, false); + setAttribute(Qt::WA_MacShowFocusRect, false); #endif - header()->setStretchLastSection(false); + header()->setStretchLastSection(false); + + // Default hidden columns + if (!column_loaded) { + setColumnHidden(TorrentModelItem::TR_PRIORITY, true); + setColumnHidden(TorrentModelItem::TR_ADD_DATE, true); + setColumnHidden(TorrentModelItem::TR_SEED_DATE, true); + setColumnHidden(TorrentModelItem::TR_UPLIMIT, true); + setColumnHidden(TorrentModelItem::TR_DLLIMIT, true); + setColumnHidden(TorrentModelItem::TR_TRACKER, true); + setColumnHidden(TorrentModelItem::TR_AMOUNT_DOWNLOADED, true); + setColumnHidden(TorrentModelItem::TR_AMOUNT_UPLOADED, true); + setColumnHidden(TorrentModelItem::TR_AMOUNT_LEFT, true); + setColumnHidden(TorrentModelItem::TR_TIME_ELAPSED, true); + setColumnHidden(TorrentModelItem::TR_SAVE_PATH, true); + setColumnHidden(TorrentModelItem::TR_COMPLETED, true); + setColumnHidden(TorrentModelItem::TR_RATIO_LIMIT, true); + setColumnHidden(TorrentModelItem::TR_SEEN_COMPLETE_DATE, true); + setColumnHidden(TorrentModelItem::TR_LAST_ACTIVITY, true); + setColumnHidden(TorrentModelItem::TR_TOTAL_SIZE, true); + } - // Default hidden columns - if (!column_loaded) { - setColumnHidden(TorrentModelItem::TR_PRIORITY, true); - setColumnHidden(TorrentModelItem::TR_ADD_DATE, true); - setColumnHidden(TorrentModelItem::TR_SEED_DATE, true); - setColumnHidden(TorrentModelItem::TR_UPLIMIT, true); - setColumnHidden(TorrentModelItem::TR_DLLIMIT, true); - setColumnHidden(TorrentModelItem::TR_TRACKER, true); - setColumnHidden(TorrentModelItem::TR_AMOUNT_DOWNLOADED, true); - setColumnHidden(TorrentModelItem::TR_AMOUNT_UPLOADED, true); - setColumnHidden(TorrentModelItem::TR_AMOUNT_LEFT, true); - setColumnHidden(TorrentModelItem::TR_TIME_ELAPSED, true); - setColumnHidden(TorrentModelItem::TR_SAVE_PATH, true); - setColumnHidden(TorrentModelItem::TR_COMPLETED, true); - setColumnHidden(TorrentModelItem::TR_RATIO_LIMIT, true); - setColumnHidden(TorrentModelItem::TR_SEEN_COMPLETE_DATE, true); - setColumnHidden(TorrentModelItem::TR_LAST_ACTIVITY, true); - setColumnHidden(TorrentModelItem::TR_TOTAL_SIZE, true); - } - - //Ensure that at least one column is visible at all times - bool atLeastOne = false; - for (unsigned int i=0; isetContextMenuPolicy(Qt::CustomContextMenu); - connect(header(), SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayDLHoSMenu(const QPoint&))); - - editHotkey = new QShortcut(QKeySequence("F2"), this, SLOT(renameSelectedTorrent()), 0, Qt::WidgetShortcut); - deleteHotkey = new QShortcut(QKeySequence::Delete, this, SLOT(deleteSelectedTorrents()), 0, Qt::WidgetShortcut); -} - -TransferListWidget::~TransferListWidget() { - qDebug() << Q_FUNC_INFO << "ENTER"; - // Save settings - saveSettings(); - // Clean up - delete nameFilterModel; - delete listModel; - delete listDelegate; - delete editHotkey; - delete deleteHotkey; - qDebug() << Q_FUNC_INFO << "EXIT"; -} - -TorrentModel* TransferListWidget::getSourceModel() const { - return listModel; -} - -void TransferListWidget::previewFile(QString filePath) { - openUrl(filePath); -} - -void TransferListWidget::setRefreshInterval(int t) { - qDebug("Settings transfer list refresh interval to %dms", t); - listModel->setRefreshInterval(t); -} - -int TransferListWidget::getRowFromHash(QString hash) const { - return listModel->torrentRow(hash); -} - -inline QString TransferListWidget::getHashFromRow(int row) const { - return listModel->torrentHash(row); -} - -inline QModelIndex TransferListWidget::mapToSource(const QModelIndex &index) const { - Q_ASSERT(index.isValid()); - if (index.model() == nameFilterModel) - return nameFilterModel->mapToSource(index); - return index; -} - -inline QModelIndex TransferListWidget::mapFromSource(const QModelIndex &index) const { - Q_ASSERT(index.isValid()); - Q_ASSERT(index.model() == nameFilterModel); - return nameFilterModel->mapFromSource(index); -} - -void TransferListWidget::torrentDoubleClicked(const QModelIndex& index) { - const int row = mapToSource(index).row(); - const QString hash = getHashFromRow(row); - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_valid()) return; - int action; - if (h.is_seed()) { - action = Preferences::instance()->getActionOnDblClOnTorrentFn(); - } else { - action = Preferences::instance()->getActionOnDblClOnTorrentDl(); - } - - switch(action) { - case TOGGLE_PAUSE: - if (h.is_paused()) { - h.resume(); - } else { - h.pause(); - } - break; - case OPEN_DEST: - const QString path = h.root_path(); - openUrl(path); - } -} - -QStringList TransferListWidget::getSelectedTorrentsHashes() const { - QStringList hashes; - const QModelIndexList selectedIndexes = selectionModel()->selectedRows(); - foreach (const QModelIndex &index, selectedIndexes) { - hashes << getHashFromRow(mapToSource(index).row()); - } - return hashes; -} - -void TransferListWidget::setSelectedTorrentsLocation() { - const QStringList hashes = getSelectedTorrentsHashes(); - if (hashes.isEmpty()) return; - QString dir; - const QDir saveDir(TorrentPersistentData::getSavePath(hashes.first())); - qDebug("Old save path is %s", qPrintable(saveDir.absolutePath())); - dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), saveDir.absolutePath(), - QFileDialog::DontConfirmOverwrite|QFileDialog::ShowDirsOnly|QFileDialog::HideNameFilterDetails); - if (!dir.isNull()) { - qDebug("New path is %s", qPrintable(dir)); - // Check if savePath exists - QDir savePath(fsutils::expandPathAbs(dir)); - qDebug("New path after clean up is %s", qPrintable(savePath.absolutePath())); - foreach (const QString & hash, hashes) { - // Actually move storage - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!BTSession->useTemporaryFolder() || h.is_seed()) { - if (!savePath.exists()) savePath.mkpath(savePath.absolutePath()); - h.move_storage(savePath.absolutePath()); - } else { - TorrentPersistentData::saveSavePath(h.hash(), savePath.absolutePath()); - main_window->getProperties()->updateSavePath(h); - } - } - } -} - -void TransferListWidget::startSelectedTorrents() { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - BTSession->resumeTorrent(hash); - } -} - -void TransferListWidget::startVisibleTorrents() { - QStringList hashes; - for (int i=0; irowCount(); ++i) { - const int row = mapToSource(nameFilterModel->index(i, 0)).row(); - hashes << getHashFromRow(row); - } - foreach (const QString &hash, hashes) { - BTSession->resumeTorrent(hash); - } -} - -void TransferListWidget::pauseSelectedTorrents() { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - BTSession->pauseTorrent(hash); - } -} - -void TransferListWidget::pauseVisibleTorrents() { - QStringList hashes; - for (int i=0; irowCount(); ++i) { - const int row = mapToSource(nameFilterModel->index(i, 0)).row(); - hashes << getHashFromRow(row); - } - foreach (const QString &hash, hashes) { - BTSession->pauseTorrent(hash); - } -} - -void TransferListWidget::deleteSelectedTorrents() { - if (main_window->getCurrentTabWidget() != this) return; - const QStringList& hashes = getSelectedTorrentsHashes(); - if (hashes.empty()) return; - QTorrentHandle torrent = BTSession->getTorrentHandle(hashes[0]); - bool delete_local_files = false; - if (Preferences::instance()->confirmTorrentDeletion() && - !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, hashes.size(), torrent.name())) - return; - foreach (const QString &hash, hashes) { - BTSession->deleteTorrent(hash, delete_local_files); - } -} - -void TransferListWidget::deleteVisibleTorrents() { - if (nameFilterModel->rowCount() <= 0) return; - QTorrentHandle torrent = BTSession->getTorrentHandle(getHashFromRow(0)); - bool delete_local_files = false; - if (Preferences::instance()->confirmTorrentDeletion() && - !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, nameFilterModel->rowCount(), torrent.name())) - return; - QStringList hashes; - for (int i=0; irowCount(); ++i) { - const int row = mapToSource(nameFilterModel->index(i, 0)).row(); - hashes << getHashFromRow(row); - } - foreach (const QString &hash, hashes) { - BTSession->deleteTorrent(hash, delete_local_files); - } -} - -void TransferListWidget::increasePrioSelectedTorrents() { - qDebug() << Q_FUNC_INFO; - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - std::priority_queue, std::vector >, std::greater > > torrent_queue; - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_seed()) { - torrent_queue.push(qMakePair(h.queue_position(), h)); - } - }catch(invalid_handle&) {} - } - // Increase torrents priority (starting with the ones with highest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - try { - h.queue_position_up(); - } catch(invalid_handle& h) {} - torrent_queue.pop(); - } -} - -void TransferListWidget::decreasePrioSelectedTorrents() { - qDebug() << Q_FUNC_INFO; - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - std::priority_queue, std::vector >, std::less > > torrent_queue; - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_seed()) { - torrent_queue.push(qMakePair(h.queue_position(), h)); - } - }catch(invalid_handle&) {} - } - // Decrease torrents priority (starting with the ones with lowest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - try { - h.queue_position_down(); - } catch(invalid_handle& h) {} - torrent_queue.pop(); - } -} - -void TransferListWidget::topPrioSelectedTorrents() { - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && !h.is_seed()) { - h.queue_position_top(); + //Ensure that at least one column is visible at all times + bool atLeastOne = false; + for (unsigned int i = 0; isetContextMenuPolicy(Qt::CustomContextMenu); + connect(header(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayDLHoSMenu(const QPoint &))); + + editHotkey = new QShortcut(QKeySequence("F2"), this, SLOT(renameSelectedTorrent()), 0, Qt::WidgetShortcut); + deleteHotkey = new QShortcut(QKeySequence::Delete, this, SLOT(deleteSelectedTorrents()), 0, Qt::WidgetShortcut); +} + +TransferListWidget::~TransferListWidget() +{ + qDebug() << Q_FUNC_INFO << "ENTER"; + // Save settings + saveSettings(); + // Clean up + delete nameFilterModel; + delete listModel; + delete listDelegate; + delete editHotkey; + delete deleteHotkey; + qDebug() << Q_FUNC_INFO << "EXIT"; +} + +TorrentModel* TransferListWidget::getSourceModel() const +{ + return listModel; +} + +void TransferListWidget::previewFile(QString filePath) +{ + openUrl(filePath); +} + +void TransferListWidget::setRefreshInterval(int t) +{ + qDebug("Settings transfer list refresh interval to %dms", t); + listModel->setRefreshInterval(t); +} + +int TransferListWidget::getRowFromHash(QString hash) const +{ + return listModel->torrentRow(hash); } -void TransferListWidget::bottomPrioSelectedTorrents() { - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { +inline QString TransferListWidget::getHashFromRow(int row) const +{ + return listModel->torrentHash(row); +} + +inline QModelIndex TransferListWidget::mapToSource(const QModelIndex &index) const +{ + Q_ASSERT(index.isValid()); + if (index.model() == nameFilterModel) + return nameFilterModel->mapToSource(index); + return index; +} + +inline QModelIndex TransferListWidget::mapFromSource(const QModelIndex &index) const +{ + Q_ASSERT(index.isValid()); + Q_ASSERT(index.model() == nameFilterModel); + return nameFilterModel->mapFromSource(index); +} + +void TransferListWidget::torrentDoubleClicked(const QModelIndex& index) +{ + const int row = mapToSource(index).row(); + const QString hash = getHashFromRow(row); QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && !h.is_seed()) { - h.queue_position_bottom(); + if (!h.is_valid()) return; + int action; + if (h.is_seed()) + action = Preferences::instance()->getActionOnDblClOnTorrentFn(); + else + action = Preferences::instance()->getActionOnDblClOnTorrentDl(); + + switch(action) { + case TOGGLE_PAUSE: + if (h.is_paused()) + h.resume(); + else + h.pause(); + break; + case OPEN_DEST: + const QString path = h.root_path(); + openUrl(path); } - } } -void TransferListWidget::copySelectedMagnetURIs() const { - QStringList magnet_uris; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid()) - magnet_uris << misc::toQString(make_magnet_uri(h)); - } - qApp->clipboard()->setText(magnet_uris.join("\n")); +QStringList TransferListWidget::getSelectedTorrentsHashes() const +{ + QStringList hashes; + const QModelIndexList selectedIndexes = selectionModel()->selectedRows(); + foreach (const QModelIndex &index, selectedIndexes) + hashes << getHashFromRow(mapToSource(index).row()); + return hashes; } -void TransferListWidget::hidePriorityColumn(bool hide) { - qDebug("hidePriorityColumn(%d)", hide); - setColumnHidden(TorrentModelItem::TR_PRIORITY, hide); +void TransferListWidget::setSelectedTorrentsLocation() +{ + const QStringList hashes = getSelectedTorrentsHashes(); + if (hashes.isEmpty()) return; + QString dir; + const QDir saveDir(TorrentPersistentData::getSavePath(hashes.first())); + qDebug("Old save path is %s", qPrintable(saveDir.absolutePath())); + dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), saveDir.absolutePath(), + QFileDialog::DontConfirmOverwrite | QFileDialog::ShowDirsOnly | QFileDialog::HideNameFilterDetails); + if (!dir.isNull()) { + qDebug("New path is %s", qPrintable(dir)); + // Check if savePath exists + QDir savePath(fsutils::expandPathAbs(dir)); + qDebug("New path after clean up is %s", qPrintable(savePath.absolutePath())); + foreach (const QString & hash, hashes) { + // Actually move storage + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (!BTSession->useTemporaryFolder() || h.is_seed()) { + if (!savePath.exists()) savePath.mkpath(savePath.absolutePath()); + h.move_storage(savePath.absolutePath()); + } + else { + TorrentPersistentData::saveSavePath(h.hash(), savePath.absolutePath()); + main_window->getProperties()->updateSavePath(h); + } + } + } } -void TransferListWidget::openSelectedTorrentsFolder() const { - QSet pathsList; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid()) { - QString rootFolder = h.root_path(); - qDebug("Opening path at %s", qPrintable(rootFolder)); - if (!pathsList.contains(rootFolder)) { - pathsList.insert(rootFolder); - openUrl(rootFolder); - } +void TransferListWidget::startSelectedTorrents() +{ + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) + BTSession->resumeTorrent(hash); +} + +void TransferListWidget::startVisibleTorrents() +{ + QStringList hashes; + for (int i = 0; irowCount(); ++i) { + const int row = mapToSource(nameFilterModel->index(i, 0)).row(); + hashes << getHashFromRow(row); } - } + foreach (const QString &hash, hashes) + BTSession->resumeTorrent(hash); } -void TransferListWidget::previewSelectedTorrents() { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) { - new PreviewSelect(this, h); +void TransferListWidget::pauseSelectedTorrents() +{ + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) + BTSession->pauseTorrent(hash); +} + +void TransferListWidget::pauseVisibleTorrents() +{ + QStringList hashes; + for (int i = 0; irowCount(); ++i) { + const int row = mapToSource(nameFilterModel->index(i, 0)).row(); + hashes << getHashFromRow(row); } - } + foreach (const QString &hash, hashes) + BTSession->pauseTorrent(hash); } -void TransferListWidget::setDlLimitSelectedTorrents() { - QList selected_torrents; - bool first = true; - bool all_same_limit = true; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && !h.is_seed()) { - selected_torrents << h; - // Determine current limit for selected torrents - if (first) { - first = false; - } else { - if (all_same_limit && h.download_limit() != selected_torrents.first().download_limit()) - all_same_limit = false; - } - } - } - if (selected_torrents.empty()) return; - - bool ok=false; - int default_limit = -1; - if (all_same_limit) - default_limit = selected_torrents.first().download_limit(); - const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Torrent Download Speed Limiting"), default_limit, Preferences::instance()->getGlobalDownloadLimit()*1024.); - if (ok) { - foreach (const QTorrentHandle &h, selected_torrents) { - qDebug("Applying download speed limit of %ld Kb/s to torrent %s", (long)(new_limit/1024.), qPrintable(h.hash())); - BTSession->setDownloadLimit(h.hash(), new_limit); - } - } -} - -void TransferListWidget::setUpLimitSelectedTorrents() { - QList selected_torrents; - bool first = true; - bool all_same_limit = true; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid()) { - selected_torrents << h; - // Determine current limit for selected torrents - if (first) { - first = false; - } else { - if (all_same_limit && h.upload_limit() != selected_torrents.first().upload_limit()) - all_same_limit = false; - } - } - } - if (selected_torrents.empty()) return; - - bool ok=false; - int default_limit = -1; - if (all_same_limit) - default_limit = selected_torrents.first().upload_limit(); - const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Torrent Upload Speed Limiting"), default_limit, Preferences::instance()->getGlobalUploadLimit()*1024.); - if (ok) { - foreach (const QTorrentHandle &h, selected_torrents) { - qDebug("Applying upload speed limit of %ld Kb/s to torrent %s", (long)(new_limit/1024.), qPrintable(h.hash())); - BTSession->setUploadLimit(h.hash(), new_limit); - } - } -} - -void TransferListWidget::setMaxRatioSelectedTorrents() { - const QStringList hashes = getSelectedTorrentsHashes(); - if (hashes.isEmpty()) - return; - bool useGlobalValue; - qreal currentMaxRatio; - if (hashes.count() == 1) { - currentMaxRatio = BTSession->getMaxRatioPerTorrent(hashes.first(), &useGlobalValue); - } else { - useGlobalValue = true; - currentMaxRatio = BTSession->getGlobalMaxRatio(); - } - UpDownRatioDlg dlg(useGlobalValue, currentMaxRatio, QBtSession::MAX_RATIO, this); - if (dlg.exec() != QDialog::Accepted) - return; - foreach (const QString &hash, hashes) { - if (dlg.useDefault()) - BTSession->removeRatioPerTorrent(hash); - else - BTSession->setMaxRatioPerTorrent(hash, dlg.ratio()); - } +void TransferListWidget::deleteSelectedTorrents() +{ + if (main_window->getCurrentTabWidget() != this) return; + const QStringList& hashes = getSelectedTorrentsHashes(); + if (hashes.empty()) return; + QTorrentHandle torrent = BTSession->getTorrentHandle(hashes[0]); + bool delete_local_files = false; + if (Preferences::instance()->confirmTorrentDeletion() && + !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, hashes.size(), torrent.name())) + return; + foreach (const QString &hash, hashes) + BTSession->deleteTorrent(hash, delete_local_files); +} + +void TransferListWidget::deleteVisibleTorrents() +{ + if (nameFilterModel->rowCount() <= 0) return; + QTorrentHandle torrent = BTSession->getTorrentHandle(getHashFromRow(0)); + bool delete_local_files = false; + if (Preferences::instance()->confirmTorrentDeletion() && + !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, nameFilterModel->rowCount(), torrent.name())) + return; + QStringList hashes; + for (int i = 0; irowCount(); ++i) { + const int row = mapToSource(nameFilterModel->index(i, 0)).row(); + hashes << getHashFromRow(row); + } + foreach (const QString &hash, hashes) + BTSession->deleteTorrent(hash, delete_local_files); +} + +void TransferListWidget::increasePrioSelectedTorrents() +{ + qDebug() << Q_FUNC_INFO; + if (main_window->getCurrentTabWidget() != this) return; + const QStringList hashes = getSelectedTorrentsHashes(); + std::priority_queue, std::vector >, std::greater > > torrent_queue; + // Sort torrents by priority + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (!h.is_seed()) + torrent_queue.push(qMakePair(h.queue_position(), h)); + }catch(invalid_handle&) {} + } + // Increase torrents priority (starting with the ones with highest priority) + while(!torrent_queue.empty()) { + QTorrentHandle h = torrent_queue.top().second; + try { + h.queue_position_up(); + } catch(invalid_handle& h) {} + torrent_queue.pop(); + } } -void TransferListWidget::recheckSelectedTorrents() { - QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Recheck confirmation"), tr("Are you sure you want to recheck the selected torrent(s)?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); - if (ret != QMessageBox::Yes) - return; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - BTSession->recheckTorrent(hash); - } +void TransferListWidget::decreasePrioSelectedTorrents() +{ + qDebug() << Q_FUNC_INFO; + if (main_window->getCurrentTabWidget() != this) return; + const QStringList hashes = getSelectedTorrentsHashes(); + std::priority_queue, std::vector >, std::less > > torrent_queue; + // Sort torrents by priority + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (!h.is_seed()) + torrent_queue.push(qMakePair(h.queue_position(), h)); + }catch(invalid_handle&) {} + } + // Decrease torrents priority (starting with the ones with lowest priority) + while(!torrent_queue.empty()) { + QTorrentHandle h = torrent_queue.top().second; + try { + h.queue_position_down(); + } catch(invalid_handle& h) {} + torrent_queue.pop(); + } } -// hide/show columns menu -void TransferListWidget::displayDLHoSMenu(const QPoint&) { - QMenu hideshowColumn(this); - hideshowColumn.setTitle(tr("Column visibility")); - QList actions; - for (int i=0; i < listModel->columnCount(); ++i) { - if (!BTSession->isQueueingEnabled() && i == TorrentModelItem::TR_PRIORITY) { - actions.append(0); - continue; - } - QAction *myAct = hideshowColumn.addAction(listModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString()); - myAct->setCheckable(true); - myAct->setChecked(!isColumnHidden(i)); - actions.append(myAct); - } - int visibleCols = 0; - for (unsigned int i=0; i 1) - break; - } - - // Call menu - QAction *act = hideshowColumn.exec(QCursor::pos()); - if (act) { - int col = actions.indexOf(act); - Q_ASSERT(col >= 0); - Q_ASSERT(visibleCols > 0); - if (!isColumnHidden(col) && visibleCols == 1) - return; - qDebug("Toggling column %d visibility", col); - setColumnHidden(col, !isColumnHidden(col)); - if (!isColumnHidden(col) && columnWidth(col) <= 5) - setColumnWidth(col, 100); - } -} - -void TransferListWidget::toggleSelectedTorrentsSuperSeeding() const { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) { - h.super_seeding(!h.status(0).super_seeding); +void TransferListWidget::topPrioSelectedTorrents() +{ + if (main_window->getCurrentTabWidget() != this) return; + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid() && !h.is_seed()) + h.queue_position_top(); } - } } -void TransferListWidget::toggleSelectedTorrentsSequentialDownload() const { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) { - bool was_sequential = h.is_sequential_download(); - h.set_sequential_download(!was_sequential); - if (!was_sequential) - h.prioritize_first_last_piece(true); +void TransferListWidget::bottomPrioSelectedTorrents() +{ + if (main_window->getCurrentTabWidget() != this) return; + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid() && !h.is_seed()) + h.queue_position_bottom(); } - } } -void TransferListWidget::toggleSelectedFirstLastPiecePrio() const { - QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) { - h.prioritize_first_last_piece(!h.first_last_piece_first()); - } - } -} - -void TransferListWidget::askNewLabelForSelection() { - // Ask for label - bool ok; - bool invalid; - do { - invalid = false; - const QString label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, "", &ok).trimmed(); - if (ok && !label.isEmpty()) { - if (fsutils::isValidFileSystemName(label)) { - setSelectionLabel(label); - } else { - QMessageBox::warning(this, tr("Invalid label name"), tr("Please don't use any special characters in the label name.")); - invalid = true; - } - } - }while(invalid); -} - -bool TransferListWidget::openUrl(const QString &_path) const { - const QString path = fsutils::fromNativePath(_path); - // Hack to access samba shares with QDesktopServices::openUrl - if (path.startsWith("//")) - return QDesktopServices::openUrl(fsutils::toNativePath("file:" + path)); - else - return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); -} - -void TransferListWidget::renameSelectedTorrent() { - const QModelIndexList selectedIndexes = selectionModel()->selectedRows(); - if (selectedIndexes.size() != 1) return; - if (!selectedIndexes.first().isValid()) return; - QModelIndex mi = mapToSource(selectedIndexes.first()); - const QString hash = getHashFromRow(mi.row()); - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_valid()) return; - // Ask for a new Name - bool ok; - QString name = AutoExpandableDialog::getText(this, tr("Rename"), tr("New name:"), QLineEdit::Normal, h.name(), &ok); - if (ok && !name.isEmpty()) { - name.replace(QRegExp("\r?\n|\r"), " "); - // Rename the torrent - listModel->setData(mi, name, Qt::DisplayRole); - } -} - -void TransferListWidget::setSelectionLabel(QString label) { - const QStringList& hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - Q_ASSERT(!hash.isEmpty()); - const int row = getRowFromHash(hash); - const QString old_label = listModel->data(listModel->index(row, TorrentModelItem::TR_LABEL)).toString(); - listModel->setData(listModel->index(row, TorrentModelItem::TR_LABEL), QVariant(label), Qt::DisplayRole); - // Update save path if necessary - QTorrentHandle h = BTSession->getTorrentHandle(hash); - BTSession->changeLabelInTorrentSavePath(h, old_label, label); - } -} - -void TransferListWidget::removeLabelFromRows(QString label) { - for (int i=0; irowCount(); ++i) { - if (listModel->data(listModel->index(i, TorrentModelItem::TR_LABEL)) == label) { - const QString hash = getHashFromRow(i); - listModel->setData(listModel->index(i, TorrentModelItem::TR_LABEL), "", Qt::DisplayRole); - // Update save path if necessary - QTorrentHandle h = BTSession->getTorrentHandle(hash); - BTSession->changeLabelInTorrentSavePath(h, label, ""); - } - } -} - -void TransferListWidget::displayListMenu(const QPoint&) { - QModelIndexList selectedIndexes = selectionModel()->selectedRows(); - if (selectedIndexes.size() == 0) - return; - // Create actions - QAction actionStart(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume", "Resume/start the torrent"), 0); - connect(&actionStart, SIGNAL(triggered()), this, SLOT(startSelectedTorrents())); - QAction actionPause(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause", "Pause the torrent"), 0); - connect(&actionPause, SIGNAL(triggered()), this, SLOT(pauseSelectedTorrents())); - QAction actionDelete(IconProvider::instance()->getIcon("edit-delete"), tr("Delete", "Delete the torrent"), 0); - connect(&actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedTorrents())); - QAction actionPreview_file(IconProvider::instance()->getIcon("view-preview"), tr("Preview file..."), 0); - connect(&actionPreview_file, SIGNAL(triggered()), this, SLOT(previewSelectedTorrents())); - QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/Icons/skin/ratio.png")), tr("Limit share ratio..."), 0); - connect(&actionSet_max_ratio, SIGNAL(triggered()), this, SLOT(setMaxRatioSelectedTorrents())); - QAction actionSet_upload_limit(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png")), tr("Limit upload rate..."), 0); - connect(&actionSet_upload_limit, SIGNAL(triggered()), this, SLOT(setUpLimitSelectedTorrents())); - QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/Icons/skin/download.png")), tr("Limit download rate..."), 0); - connect(&actionSet_download_limit, SIGNAL(triggered()), this, SLOT(setDlLimitSelectedTorrents())); - QAction actionOpen_destination_folder(IconProvider::instance()->getIcon("inode-directory"), tr("Open destination folder"), 0); - connect(&actionOpen_destination_folder, SIGNAL(triggered()), this, SLOT(openSelectedTorrentsFolder())); - QAction actionIncreasePriority(IconProvider::instance()->getIcon("go-up"), tr("Move up", "i.e. move up in the queue"), 0); - connect(&actionIncreasePriority, SIGNAL(triggered()), this, SLOT(increasePrioSelectedTorrents())); - QAction actionDecreasePriority(IconProvider::instance()->getIcon("go-down"), tr("Move down", "i.e. Move down in the queue"), 0); - connect(&actionDecreasePriority, SIGNAL(triggered()), this, SLOT(decreasePrioSelectedTorrents())); - QAction actionTopPriority(IconProvider::instance()->getIcon("go-top"), tr("Move to top", "i.e. Move to top of the queue"), 0); - connect(&actionTopPriority, SIGNAL(triggered()), this, SLOT(topPrioSelectedTorrents())); - QAction actionBottomPriority(IconProvider::instance()->getIcon("go-bottom"), tr("Move to bottom", "i.e. Move to bottom of the queue"), 0); - connect(&actionBottomPriority, SIGNAL(triggered()), this, SLOT(bottomPrioSelectedTorrents())); - QAction actionSetTorrentPath(IconProvider::instance()->getIcon("inode-directory"), tr("Set location..."), 0); - connect(&actionSetTorrentPath, SIGNAL(triggered()), this, SLOT(setSelectedTorrentsLocation())); - QAction actionForce_recheck(IconProvider::instance()->getIcon("document-edit-verify"), tr("Force recheck"), 0); - connect(&actionForce_recheck, SIGNAL(triggered()), this, SLOT(recheckSelectedTorrents())); - QAction actionCopy_magnet_link(QIcon(":/Icons/magnet.png"), tr("Copy magnet link"), 0); - connect(&actionCopy_magnet_link, SIGNAL(triggered()), this, SLOT(copySelectedMagnetURIs())); - QAction actionSuper_seeding_mode(tr("Super seeding mode"), 0); - actionSuper_seeding_mode.setCheckable(true); - connect(&actionSuper_seeding_mode, SIGNAL(triggered()), this, SLOT(toggleSelectedTorrentsSuperSeeding())); - QAction actionRename(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."), 0); - connect(&actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedTorrent())); - QAction actionSequential_download(tr("Download in sequential order"), 0); - actionSequential_download.setCheckable(true); - connect(&actionSequential_download, SIGNAL(triggered()), this, SLOT(toggleSelectedTorrentsSequentialDownload())); - QAction actionFirstLastPiece_prio(tr("Download first and last piece first"), 0); - actionFirstLastPiece_prio.setCheckable(true); - connect(&actionFirstLastPiece_prio, SIGNAL(triggered()), this, SLOT(toggleSelectedFirstLastPiecePrio())); - // End of actions - QMenu listMenu(this); - // Enable/disable pause/start action given the DL state - bool has_pause = false, has_start = false, has_preview = false; - bool all_same_super_seeding = true; - bool super_seeding_mode = false; - bool all_same_sequential_download_mode = true, all_same_prio_firstlast = true; - bool sequential_download_mode = false, prioritize_first_last = false; - bool one_has_metadata = false, one_not_seed = false; - bool first = true; - QTorrentHandle h; - qDebug("Displaying menu"); - foreach (const QModelIndex &index, selectedIndexes) { - // Get the file name - QString hash = getHashFromRow(mapToSource(index).row()); - // Get handle and pause the torrent - h = BTSession->getTorrentHandle(hash); - if (!h.is_valid()) continue; - if (h.has_metadata()) - one_has_metadata = true; - if (!h.is_seed()) { - one_not_seed = true; - if (h.has_metadata()) { - if (first) { - sequential_download_mode = h.is_sequential_download(); - prioritize_first_last = h.first_last_piece_first(); - } else { - if (sequential_download_mode != h.is_sequential_download()) { - all_same_sequential_download_mode = false; - } - if (prioritize_first_last != h.first_last_piece_first()) { - all_same_prio_firstlast = false; - } +void TransferListWidget::copySelectedMagnetURIs() const +{ + QStringList magnet_uris; + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + const QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid()) + magnet_uris << misc::toQString(make_magnet_uri(h)); + } + qApp->clipboard()->setText(magnet_uris.join("\n")); +} + +void TransferListWidget::hidePriorityColumn(bool hide) +{ + qDebug("hidePriorityColumn(%d)", hide); + setColumnHidden(TorrentModelItem::TR_PRIORITY, hide); +} + +void TransferListWidget::openSelectedTorrentsFolder() const +{ + QSet pathsList; + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + const QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid()) { + QString rootFolder = h.root_path(); + qDebug("Opening path at %s", qPrintable(rootFolder)); + if (!pathsList.contains(rootFolder)) { + pathsList.insert(rootFolder); + openUrl(rootFolder); + } } - } + } +} + +void TransferListWidget::previewSelectedTorrents() +{ + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + const QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid() && h.has_metadata()) + new PreviewSelect(this, h); + } +} + +void TransferListWidget::setDlLimitSelectedTorrents() +{ + QList selected_torrents; + bool first = true; + bool all_same_limit = true; + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + const QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid() && !h.is_seed()) { + selected_torrents << h; + // Determine current limit for selected torrents + if (first) + first = false; + else + if (all_same_limit && h.download_limit() != selected_torrents.first().download_limit()) + all_same_limit = false; + } + } + if (selected_torrents.empty()) return; + + bool ok = false; + int default_limit = -1; + if (all_same_limit) + default_limit = selected_torrents.first().download_limit(); + const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Torrent Download Speed Limiting"), default_limit, Preferences::instance()->getGlobalDownloadLimit() * 1024.); + if (ok) { + foreach (const QTorrentHandle &h, selected_torrents) { + qDebug("Applying download speed limit of %ld Kb/s to torrent %s", (long)(new_limit / 1024.), qPrintable(h.hash())); + BTSession->setDownloadLimit(h.hash(), new_limit); + } + } +} + +void TransferListWidget::setUpLimitSelectedTorrents() +{ + QList selected_torrents; + bool first = true; + bool all_same_limit = true; + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + const QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid()) { + selected_torrents << h; + // Determine current limit for selected torrents + if (first) + first = false; + else + if (all_same_limit && h.upload_limit() != selected_torrents.first().upload_limit()) + all_same_limit = false; + } + } + if (selected_torrents.empty()) return; + + bool ok = false; + int default_limit = -1; + if (all_same_limit) + default_limit = selected_torrents.first().upload_limit(); + const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Torrent Upload Speed Limiting"), default_limit, Preferences::instance()->getGlobalUploadLimit() * 1024.); + if (ok) { + foreach (const QTorrentHandle &h, selected_torrents) { + qDebug("Applying upload speed limit of %ld Kb/s to torrent %s", (long)(new_limit / 1024.), qPrintable(h.hash())); + BTSession->setUploadLimit(h.hash(), new_limit); + } + } +} + +void TransferListWidget::setMaxRatioSelectedTorrents() +{ + const QStringList hashes = getSelectedTorrentsHashes(); + if (hashes.isEmpty()) + return; + bool useGlobalValue; + qreal currentMaxRatio; + if (hashes.count() == 1) { + currentMaxRatio = BTSession->getMaxRatioPerTorrent(hashes.first(), &useGlobalValue); } else { - if (!one_not_seed && all_same_super_seeding && h.has_metadata()) { - if (first) { - super_seeding_mode = h.status(0).super_seeding; - } else { - if (super_seeding_mode != h.status(0).super_seeding) { - all_same_super_seeding = false; - } + useGlobalValue = true; + currentMaxRatio = BTSession->getGlobalMaxRatio(); + } + UpDownRatioDlg dlg(useGlobalValue, currentMaxRatio, QBtSession::MAX_RATIO, this); + if (dlg.exec() != QDialog::Accepted) + return; + foreach (const QString &hash, hashes) { + if (dlg.useDefault()) + BTSession->removeRatioPerTorrent(hash); + else + BTSession->setMaxRatioPerTorrent(hash, dlg.ratio()); + } +} + +void TransferListWidget::recheckSelectedTorrents() +{ + QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Recheck confirmation"), tr("Are you sure you want to recheck the selected torrent(s)?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (ret != QMessageBox::Yes) + return; + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) + BTSession->recheckTorrent(hash); +} + +// hide/show columns menu +void TransferListWidget::displayDLHoSMenu(const QPoint&) +{ + QMenu hideshowColumn(this); + hideshowColumn.setTitle(tr("Column visibility")); + QList actions; + for (int i = 0; i < listModel->columnCount(); ++i) { + if (!BTSession->isQueueingEnabled() && i == TorrentModelItem::TR_PRIORITY) { + actions.append(0); + continue; } - } - } - if (h.is_paused()) { - if (!has_start) { - listMenu.addAction(&actionStart); - has_start = true; - } - }else{ - if (!has_pause) { - listMenu.addAction(&actionPause); - has_pause = true; - } - } - if (h.has_metadata() && BTSession->isFilePreviewPossible(hash) && !has_preview) { - has_preview = true; - } - first = false; - if (has_pause && has_start && has_preview && one_not_seed) break; - } - listMenu.addSeparator(); - listMenu.addAction(&actionDelete); - listMenu.addSeparator(); - listMenu.addAction(&actionSetTorrentPath); - if (selectedIndexes.size() == 1) - listMenu.addAction(&actionRename); - // Label Menu - QStringList customLabels = Preferences::instance()->getTorrentLabels(); - customLabels.sort(); - QList labelActions; - QMenu *labelMenu = listMenu.addMenu(IconProvider::instance()->getIcon("view-categories"), tr("Label")); - labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("list-add"), tr("New...", "New label...")); - labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("edit-clear"), tr("Reset", "Reset label")); - labelMenu->addSeparator(); - foreach (const QString &label, customLabels) { - labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("inode-directory"), label); - } - listMenu.addSeparator(); - if (one_not_seed) - listMenu.addAction(&actionSet_download_limit); - listMenu.addAction(&actionSet_max_ratio); - listMenu.addAction(&actionSet_upload_limit); - if (!one_not_seed && all_same_super_seeding && one_has_metadata) { - actionSuper_seeding_mode.setChecked(super_seeding_mode); - listMenu.addAction(&actionSuper_seeding_mode); - } - listMenu.addSeparator(); - bool added_preview_action = false; - if (has_preview) { - listMenu.addAction(&actionPreview_file); - added_preview_action = true; - } - if (one_not_seed && one_has_metadata) { - if (all_same_sequential_download_mode) { - actionSequential_download.setChecked(sequential_download_mode); - listMenu.addAction(&actionSequential_download); - added_preview_action = true; - } - if (all_same_prio_firstlast) { - actionFirstLastPiece_prio.setChecked(prioritize_first_last); - listMenu.addAction(&actionFirstLastPiece_prio); - added_preview_action = true; - } - } - if (added_preview_action) + QAction *myAct = hideshowColumn.addAction(listModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString()); + myAct->setCheckable(true); + myAct->setChecked(!isColumnHidden(i)); + actions.append(myAct); + } + int visibleCols = 0; + for (unsigned int i = 0; i 1) + break; + } + + // Call menu + QAction *act = hideshowColumn.exec(QCursor::pos()); + if (act) { + int col = actions.indexOf(act); + Q_ASSERT(col >= 0); + Q_ASSERT(visibleCols > 0); + if (!isColumnHidden(col) && visibleCols == 1) + return; + qDebug("Toggling column %d visibility", col); + setColumnHidden(col, !isColumnHidden(col)); + if (!isColumnHidden(col) && columnWidth(col) <= 5) + setColumnWidth(col, 100); + } +} + +void TransferListWidget::toggleSelectedTorrentsSuperSeeding() const +{ + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid() && h.has_metadata()) + h.super_seeding(!h.status(0).super_seeding); + } +} + +void TransferListWidget::toggleSelectedTorrentsSequentialDownload() const +{ + const QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid() && h.has_metadata()) { + bool was_sequential = h.is_sequential_download(); + h.set_sequential_download(!was_sequential); + if (!was_sequential) + h.prioritize_first_last_piece(true); + } + } +} + +void TransferListWidget::toggleSelectedFirstLastPiecePrio() const +{ + QStringList hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (h.is_valid() && h.has_metadata()) + h.prioritize_first_last_piece(!h.first_last_piece_first()); + } +} + +void TransferListWidget::askNewLabelForSelection() +{ + // Ask for label + bool ok; + bool invalid; + do { + invalid = false; + const QString label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, "", &ok).trimmed(); + if (ok && !label.isEmpty()) { + if (fsutils::isValidFileSystemName(label)) { + setSelectionLabel(label); + } + else { + QMessageBox::warning(this, tr("Invalid label name"), tr("Please don't use any special characters in the label name.")); + invalid = true; + } + } + } while(invalid); +} + +bool TransferListWidget::openUrl(const QString &_path) const +{ + const QString path = fsutils::fromNativePath(_path); + // Hack to access samba shares with QDesktopServices::openUrl + if (path.startsWith("//")) + return QDesktopServices::openUrl(fsutils::toNativePath("file:" + path)); + else + return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); +} + +void TransferListWidget::renameSelectedTorrent() +{ + const QModelIndexList selectedIndexes = selectionModel()->selectedRows(); + if (selectedIndexes.size() != 1) return; + if (!selectedIndexes.first().isValid()) return; + QModelIndex mi = mapToSource(selectedIndexes.first()); + const QString hash = getHashFromRow(mi.row()); + const QTorrentHandle h = BTSession->getTorrentHandle(hash); + if (!h.is_valid()) return; + // Ask for a new Name + bool ok; + QString name = AutoExpandableDialog::getText(this, tr("Rename"), tr("New name:"), QLineEdit::Normal, h.name(), &ok); + if (ok && !name.isEmpty()) { + name.replace(QRegExp("\r?\n|\r"), " "); + // Rename the torrent + listModel->setData(mi, name, Qt::DisplayRole); + } +} + +void TransferListWidget::setSelectionLabel(QString label) +{ + const QStringList& hashes = getSelectedTorrentsHashes(); + foreach (const QString &hash, hashes) { + Q_ASSERT(!hash.isEmpty()); + const int row = getRowFromHash(hash); + const QString old_label = listModel->data(listModel->index(row, TorrentModelItem::TR_LABEL)).toString(); + listModel->setData(listModel->index(row, TorrentModelItem::TR_LABEL), QVariant(label), Qt::DisplayRole); + // Update save path if necessary + QTorrentHandle h = BTSession->getTorrentHandle(hash); + BTSession->changeLabelInTorrentSavePath(h, old_label, label); + } +} + +void TransferListWidget::removeLabelFromRows(QString label) +{ + for (int i = 0; irowCount(); ++i) { + if (listModel->data(listModel->index(i, TorrentModelItem::TR_LABEL)) == label) { + const QString hash = getHashFromRow(i); + listModel->setData(listModel->index(i, TorrentModelItem::TR_LABEL), "", Qt::DisplayRole); + // Update save path if necessary + QTorrentHandle h = BTSession->getTorrentHandle(hash); + BTSession->changeLabelInTorrentSavePath(h, label, ""); + } + } +} + +void TransferListWidget::displayListMenu(const QPoint&) +{ + QModelIndexList selectedIndexes = selectionModel()->selectedRows(); + if (selectedIndexes.size() == 0) + return; + // Create actions + QAction actionStart(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume", "Resume/start the torrent"), 0); + connect(&actionStart, SIGNAL(triggered()), this, SLOT(startSelectedTorrents())); + QAction actionPause(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause", "Pause the torrent"), 0); + connect(&actionPause, SIGNAL(triggered()), this, SLOT(pauseSelectedTorrents())); + QAction actionDelete(IconProvider::instance()->getIcon("edit-delete"), tr("Delete", "Delete the torrent"), 0); + connect(&actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedTorrents())); + QAction actionPreview_file(IconProvider::instance()->getIcon("view-preview"), tr("Preview file..."), 0); + connect(&actionPreview_file, SIGNAL(triggered()), this, SLOT(previewSelectedTorrents())); + QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/Icons/skin/ratio.png")), tr("Limit share ratio..."), 0); + connect(&actionSet_max_ratio, SIGNAL(triggered()), this, SLOT(setMaxRatioSelectedTorrents())); + QAction actionSet_upload_limit(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png")), tr("Limit upload rate..."), 0); + connect(&actionSet_upload_limit, SIGNAL(triggered()), this, SLOT(setUpLimitSelectedTorrents())); + QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/Icons/skin/download.png")), tr("Limit download rate..."), 0); + connect(&actionSet_download_limit, SIGNAL(triggered()), this, SLOT(setDlLimitSelectedTorrents())); + QAction actionOpen_destination_folder(IconProvider::instance()->getIcon("inode-directory"), tr("Open destination folder"), 0); + connect(&actionOpen_destination_folder, SIGNAL(triggered()), this, SLOT(openSelectedTorrentsFolder())); + QAction actionIncreasePriority(IconProvider::instance()->getIcon("go-up"), tr("Move up", "i.e. move up in the queue"), 0); + connect(&actionIncreasePriority, SIGNAL(triggered()), this, SLOT(increasePrioSelectedTorrents())); + QAction actionDecreasePriority(IconProvider::instance()->getIcon("go-down"), tr("Move down", "i.e. Move down in the queue"), 0); + connect(&actionDecreasePriority, SIGNAL(triggered()), this, SLOT(decreasePrioSelectedTorrents())); + QAction actionTopPriority(IconProvider::instance()->getIcon("go-top"), tr("Move to top", "i.e. Move to top of the queue"), 0); + connect(&actionTopPriority, SIGNAL(triggered()), this, SLOT(topPrioSelectedTorrents())); + QAction actionBottomPriority(IconProvider::instance()->getIcon("go-bottom"), tr("Move to bottom", "i.e. Move to bottom of the queue"), 0); + connect(&actionBottomPriority, SIGNAL(triggered()), this, SLOT(bottomPrioSelectedTorrents())); + QAction actionSetTorrentPath(IconProvider::instance()->getIcon("inode-directory"), tr("Set location..."), 0); + connect(&actionSetTorrentPath, SIGNAL(triggered()), this, SLOT(setSelectedTorrentsLocation())); + QAction actionForce_recheck(IconProvider::instance()->getIcon("document-edit-verify"), tr("Force recheck"), 0); + connect(&actionForce_recheck, SIGNAL(triggered()), this, SLOT(recheckSelectedTorrents())); + QAction actionCopy_magnet_link(QIcon(":/Icons/magnet.png"), tr("Copy magnet link"), 0); + connect(&actionCopy_magnet_link, SIGNAL(triggered()), this, SLOT(copySelectedMagnetURIs())); + QAction actionSuper_seeding_mode(tr("Super seeding mode"), 0); + actionSuper_seeding_mode.setCheckable(true); + connect(&actionSuper_seeding_mode, SIGNAL(triggered()), this, SLOT(toggleSelectedTorrentsSuperSeeding())); + QAction actionRename(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."), 0); + connect(&actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedTorrent())); + QAction actionSequential_download(tr("Download in sequential order"), 0); + actionSequential_download.setCheckable(true); + connect(&actionSequential_download, SIGNAL(triggered()), this, SLOT(toggleSelectedTorrentsSequentialDownload())); + QAction actionFirstLastPiece_prio(tr("Download first and last piece first"), 0); + actionFirstLastPiece_prio.setCheckable(true); + connect(&actionFirstLastPiece_prio, SIGNAL(triggered()), this, SLOT(toggleSelectedFirstLastPiecePrio())); + // End of actions + QMenu listMenu(this); + // Enable/disable pause/start action given the DL state + bool has_pause = false, has_start = false, has_preview = false; + bool all_same_super_seeding = true; + bool super_seeding_mode = false; + bool all_same_sequential_download_mode = true, all_same_prio_firstlast = true; + bool sequential_download_mode = false, prioritize_first_last = false; + bool one_has_metadata = false, one_not_seed = false; + bool first = true; + QTorrentHandle h; + qDebug("Displaying menu"); + foreach (const QModelIndex &index, selectedIndexes) { + // Get the file name + QString hash = getHashFromRow(mapToSource(index).row()); + // Get handle and pause the torrent + h = BTSession->getTorrentHandle(hash); + if (!h.is_valid()) continue; + if (h.has_metadata()) + one_has_metadata = true; + if (!h.is_seed()) { + one_not_seed = true; + if (h.has_metadata()) { + if (first) { + sequential_download_mode = h.is_sequential_download(); + prioritize_first_last = h.first_last_piece_first(); + } + else { + if (sequential_download_mode != h.is_sequential_download()) + all_same_sequential_download_mode = false; + if (prioritize_first_last != h.first_last_piece_first()) + all_same_prio_firstlast = false; + } + } + } + else { + if (!one_not_seed && all_same_super_seeding && h.has_metadata()) { + if (first) { + super_seeding_mode = h.status(0).super_seeding; + } + else if (super_seeding_mode != h.status(0).super_seeding) + all_same_super_seeding = false; + + } + } + if (h.is_paused()) { + if (!has_start) { + listMenu.addAction(&actionStart); + has_start = true; + } + } + else { + if (!has_pause) { + listMenu.addAction(&actionPause); + has_pause = true; + } + } + if (h.has_metadata() && BTSession->isFilePreviewPossible(hash) && !has_preview) + has_preview = true; + first = false; + if (has_pause && has_start && has_preview && one_not_seed) break; + } listMenu.addSeparator(); - if (one_has_metadata) { - listMenu.addAction(&actionForce_recheck); + listMenu.addAction(&actionDelete); listMenu.addSeparator(); - } - listMenu.addAction(&actionOpen_destination_folder); - if (BTSession->isQueueingEnabled() && one_not_seed) { + listMenu.addAction(&actionSetTorrentPath); + if (selectedIndexes.size() == 1) + listMenu.addAction(&actionRename); + // Label Menu + QStringList customLabels = Preferences::instance()->getTorrentLabels(); + customLabels.sort(); + QList labelActions; + QMenu *labelMenu = listMenu.addMenu(IconProvider::instance()->getIcon("view-categories"), tr("Label")); + labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("list-add"), tr("New...", "New label...")); + labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("edit-clear"), tr("Reset", "Reset label")); + labelMenu->addSeparator(); + foreach (const QString &label, customLabels) + labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("inode-directory"), label); + listMenu.addSeparator(); + if (one_not_seed) + listMenu.addAction(&actionSet_download_limit); + listMenu.addAction(&actionSet_max_ratio); + listMenu.addAction(&actionSet_upload_limit); + if (!one_not_seed && all_same_super_seeding && one_has_metadata) { + actionSuper_seeding_mode.setChecked(super_seeding_mode); + listMenu.addAction(&actionSuper_seeding_mode); + } + listMenu.addSeparator(); + bool added_preview_action = false; + if (has_preview) { + listMenu.addAction(&actionPreview_file); + added_preview_action = true; + } + if (one_not_seed && one_has_metadata) { + if (all_same_sequential_download_mode) { + actionSequential_download.setChecked(sequential_download_mode); + listMenu.addAction(&actionSequential_download); + added_preview_action = true; + } + if (all_same_prio_firstlast) { + actionFirstLastPiece_prio.setChecked(prioritize_first_last); + listMenu.addAction(&actionFirstLastPiece_prio); + added_preview_action = true; + } + } + if (added_preview_action) + listMenu.addSeparator(); + if (one_has_metadata) { + listMenu.addAction(&actionForce_recheck); + listMenu.addSeparator(); + } + listMenu.addAction(&actionOpen_destination_folder); + if (BTSession->isQueueingEnabled() && one_not_seed) { + listMenu.addSeparator(); + QMenu *prioMenu = listMenu.addMenu(tr("Priority")); + prioMenu->addAction(&actionTopPriority); + prioMenu->addAction(&actionIncreasePriority); + prioMenu->addAction(&actionDecreasePriority); + prioMenu->addAction(&actionBottomPriority); + } listMenu.addSeparator(); - QMenu *prioMenu = listMenu.addMenu(tr("Priority")); - prioMenu->addAction(&actionTopPriority); - prioMenu->addAction(&actionIncreasePriority); - prioMenu->addAction(&actionDecreasePriority); - prioMenu->addAction(&actionBottomPriority); - } - listMenu.addSeparator(); - listMenu.addAction(&actionCopy_magnet_link); - // Call menu - QAction *act = 0; - act = listMenu.exec(QCursor::pos()); - if (act) { - // Parse label actions only (others have slots assigned) - int i = labelActions.indexOf(act); - if (i >= 0) { - // Label action - if (i == 0) { - // New Label - askNewLabelForSelection(); - } else { - QString label = ""; - if (i > 1) - label = customLabels.at(i-2); - // Update Label - setSelectionLabel(label); - } - } - } -} - -void TransferListWidget::currentChanged(const QModelIndex& current, const QModelIndex&) { - qDebug("CURRENT CHANGED"); - QTorrentHandle h; - if (current.isValid()) { - const int row = mapToSource(current).row(); - h = BTSession->getTorrentHandle(getHashFromRow(row)); - // Scroll Fix - scrollTo(current); - } - emit currentTorrentChanged(h); -} - -void TransferListWidget::applyLabelFilterAll() { - nameFilterModel->disableLabelFilter(); -} - -void TransferListWidget::applyLabelFilter(QString label) { - qDebug("Applying Label filter: %s", qPrintable(label)); - nameFilterModel->setLabelFilter(label); -} - -void TransferListWidget::applyNameFilter(const QString& name) { - nameFilterModel->setFilterRegExp(QRegExp(QRegExp::escape(name), Qt::CaseInsensitive)); -} - -void TransferListWidget::applyStatusFilter(int f) { - nameFilterModel->setStatusFilter((TorrentFilter::TorrentFilter)f); - // Select first item if nothing is selected - if (selectionModel()->selectedRows(0).empty() && nameFilterModel->rowCount() > 0) { - qDebug("Nothing is selected, selecting first row: %s", qPrintable(nameFilterModel->index(0, TorrentModelItem::TR_NAME).data().toString())); - selectionModel()->setCurrentIndex(nameFilterModel->index(0, TorrentModelItem::TR_NAME), QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); - } + listMenu.addAction(&actionCopy_magnet_link); + // Call menu + QAction *act = 0; + act = listMenu.exec(QCursor::pos()); + if (act) { + // Parse label actions only (others have slots assigned) + int i = labelActions.indexOf(act); + if (i >= 0) { + // Label action + if (i == 0) { + // New Label + askNewLabelForSelection(); + } + else { + QString label = ""; + if (i > 1) + label = customLabels.at(i - 2); + // Update Label + setSelectionLabel(label); + } + } + } +} + +void TransferListWidget::currentChanged(const QModelIndex& current, const QModelIndex&) +{ + qDebug("CURRENT CHANGED"); + QTorrentHandle h; + if (current.isValid()) { + const int row = mapToSource(current).row(); + h = BTSession->getTorrentHandle(getHashFromRow(row)); + // Scroll Fix + scrollTo(current); + } + emit currentTorrentChanged(h); +} + +void TransferListWidget::applyLabelFilterAll() +{ + nameFilterModel->disableLabelFilter(); +} + +void TransferListWidget::applyLabelFilter(QString label) +{ + qDebug("Applying Label filter: %s", qPrintable(label)); + nameFilterModel->setLabelFilter(label); +} + +void TransferListWidget::applyNameFilter(const QString& name) +{ + nameFilterModel->setFilterRegExp(QRegExp(QRegExp::escape(name), Qt::CaseInsensitive)); +} + +void TransferListWidget::applyStatusFilter(int f) +{ + nameFilterModel->setStatusFilter((TorrentFilter::TorrentFilter)f); + // Select first item if nothing is selected + if (selectionModel()->selectedRows(0).empty() && nameFilterModel->rowCount() > 0) { + qDebug("Nothing is selected, selecting first row: %s", qPrintable(nameFilterModel->index(0, TorrentModelItem::TR_NAME).data().toString())); + selectionModel()->setCurrentIndex(nameFilterModel->index(0, TorrentModelItem::TR_NAME), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); + } } void TransferListWidget::saveSettings() { - Preferences::instance()->setTransHeaderState(header()->saveState()); + Preferences::instance()->setTransHeaderState(header()->saveState()); } bool TransferListWidget::loadSettings() { - bool ok = header()->restoreState(Preferences::instance()->getTransHeaderState()); - if (!ok) { - header()->resizeSection(0, 200); // Default - } - return ok; + bool ok = header()->restoreState(Preferences::instance()->getTransHeaderState()); + if (!ok) + header()->resizeSection(0, 200); // Default + return ok; } diff --git a/src/transferlistwidget.h b/src/transferlistwidget.h index 47f1773e0..e7e5489d4 100644 --- a/src/transferlistwidget.h +++ b/src/transferlistwidget.h @@ -47,77 +47,78 @@ class QSortFilterProxyModel; class QStandardItemModel; QT_END_NAMESPACE -class TransferListWidget: public QTreeView { - Q_OBJECT +class TransferListWidget: public QTreeView +{ + Q_OBJECT public: - TransferListWidget(QWidget *parent, MainWindow *main_window, QBtSession* BTSession); - ~TransferListWidget(); - TorrentModel* getSourceModel() const; + TransferListWidget(QWidget *parent, MainWindow *main_window, QBtSession* BTSession); + ~TransferListWidget(); + TorrentModel* getSourceModel() const; public slots: - void setSelectionLabel(QString label); - void setRefreshInterval(int t); - void setSelectedTorrentsLocation(); - void startSelectedTorrents(); - void startVisibleTorrents(); - void pauseSelectedTorrents(); - void pauseVisibleTorrents(); - void deleteSelectedTorrents(); - void deleteVisibleTorrents(); - void increasePrioSelectedTorrents(); - void decreasePrioSelectedTorrents(); - void topPrioSelectedTorrents(); - void bottomPrioSelectedTorrents(); - void copySelectedMagnetURIs() const; - void openSelectedTorrentsFolder() const; - void recheckSelectedTorrents(); - void setDlLimitSelectedTorrents(); - void setUpLimitSelectedTorrents(); - void setMaxRatioSelectedTorrents(); - void previewSelectedTorrents(); - void hidePriorityColumn(bool hide); - void displayDLHoSMenu(const QPoint&); - void applyNameFilter(const QString& name); - void applyStatusFilter(int f); - void applyLabelFilterAll(); - void applyLabelFilter(QString label); - void previewFile(QString filePath); - void removeLabelFromRows(QString label); - void renameSelectedTorrent(); + void setSelectionLabel(QString label); + void setRefreshInterval(int t); + void setSelectedTorrentsLocation(); + void startSelectedTorrents(); + void startVisibleTorrents(); + void pauseSelectedTorrents(); + void pauseVisibleTorrents(); + void deleteSelectedTorrents(); + void deleteVisibleTorrents(); + void increasePrioSelectedTorrents(); + void decreasePrioSelectedTorrents(); + void topPrioSelectedTorrents(); + void bottomPrioSelectedTorrents(); + void copySelectedMagnetURIs() const; + void openSelectedTorrentsFolder() const; + void recheckSelectedTorrents(); + void setDlLimitSelectedTorrents(); + void setUpLimitSelectedTorrents(); + void setMaxRatioSelectedTorrents(); + void previewSelectedTorrents(); + void hidePriorityColumn(bool hide); + void displayDLHoSMenu(const QPoint&); + void applyNameFilter(const QString& name); + void applyStatusFilter(int f); + void applyLabelFilterAll(); + void applyLabelFilter(QString label); + void previewFile(QString filePath); + void removeLabelFromRows(QString label); + void renameSelectedTorrent(); protected: - int getRowFromHash(QString hash) const; - QString getHashFromRow(int row) const; - QModelIndex mapToSource(const QModelIndex &index) const; - QModelIndex mapFromSource(const QModelIndex &index) const; - void saveSettings(); - bool loadSettings(); - QStringList getSelectedTorrentsHashes() const; + int getRowFromHash(QString hash) const; + QString getHashFromRow(int row) const; + QModelIndex mapToSource(const QModelIndex &index) const; + QModelIndex mapFromSource(const QModelIndex &index) const; + void saveSettings(); + bool loadSettings(); + QStringList getSelectedTorrentsHashes() const; protected slots: - void torrentDoubleClicked(const QModelIndex& index); - void displayListMenu(const QPoint&); - void currentChanged(const QModelIndex& current, const QModelIndex&); - void toggleSelectedTorrentsSuperSeeding() const; - void toggleSelectedTorrentsSequentialDownload() const; - void toggleSelectedFirstLastPiecePrio() const; - void askNewLabelForSelection(); + void torrentDoubleClicked(const QModelIndex& index); + void displayListMenu(const QPoint&); + void currentChanged(const QModelIndex& current, const QModelIndex&); + void toggleSelectedTorrentsSuperSeeding() const; + void toggleSelectedTorrentsSequentialDownload() const; + void toggleSelectedFirstLastPiecePrio() const; + void askNewLabelForSelection(); private: - bool openUrl(const QString& _path) const; + bool openUrl(const QString& _path) const; signals: - void currentTorrentChanged(const QTorrentHandle &h); + void currentTorrentChanged(const QTorrentHandle &h); private: - TransferListDelegate *listDelegate; - TorrentModel *listModel; - TransferListSortModel *nameFilterModel; - QBtSession* BTSession; - MainWindow *main_window; - QShortcut *editHotkey; - QShortcut *deleteHotkey; + TransferListDelegate *listDelegate; + TorrentModel *listModel; + TransferListSortModel *nameFilterModel; + QBtSession* BTSession; + MainWindow *main_window; + QShortcut *editHotkey; + QShortcut *deleteHotkey; }; #endif // TRANSFERLISTWIDGET_H diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 4ea1483de..72878b7ba 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -55,7 +55,7 @@ using namespace libtorrent; static QElapsedTimer cacheTimer; \ static bool initialized = false; \ if (initialized && !cacheTimer.hasExpired(DUR)) \ - return json::toJson(VAR); \ + return json::toJson(VAR); \ initialized = true; \ cacheTimer.start(); \ VAR = VARTYPE() @@ -65,7 +65,7 @@ using namespace libtorrent; static QString prev_hash; \ static QElapsedTimer cacheTimer; \ if (prev_hash == HASH && !cacheTimer.hasExpired(DUR)) \ - return json::toJson(VAR); \ + return json::toJson(VAR); \ prev_hash = HASH; \ cacheTimer.start(); \ VAR = VARTYPE() @@ -157,26 +157,26 @@ public: switch (type_) { case QVariant::Int: return greaterThan_ ? torrent1.toMap().value(key_).toInt() > torrent2.toMap().value(key_).toInt() - : torrent1.toMap().value(key_).toInt() < torrent2.toMap().value(key_).toInt(); + : torrent1.toMap().value(key_).toInt() < torrent2.toMap().value(key_).toInt(); case QVariant::LongLong: return greaterThan_ ? torrent1.toMap().value(key_).toLongLong() > torrent2.toMap().value(key_).toLongLong() - : torrent1.toMap().value(key_).toLongLong() < torrent2.toMap().value(key_).toLongLong(); + : torrent1.toMap().value(key_).toLongLong() < torrent2.toMap().value(key_).toLongLong(); case QVariant::ULongLong: return greaterThan_ ? torrent1.toMap().value(key_).toULongLong() > torrent2.toMap().value(key_).toULongLong() - : torrent1.toMap().value(key_).toULongLong() < torrent2.toMap().value(key_).toULongLong(); + : torrent1.toMap().value(key_).toULongLong() < torrent2.toMap().value(key_).toULongLong(); case QMetaType::Float: return greaterThan_ ? torrent1.toMap().value(key_).toFloat() > torrent2.toMap().value(key_).toFloat() - : torrent1.toMap().value(key_).toFloat() < torrent2.toMap().value(key_).toFloat(); + : torrent1.toMap().value(key_).toFloat() < torrent2.toMap().value(key_).toFloat(); case QVariant::Double: return greaterThan_ ? torrent1.toMap().value(key_).toDouble() > torrent2.toMap().value(key_).toDouble() - : torrent1.toMap().value(key_).toDouble() < torrent2.toMap().value(key_).toDouble(); + : torrent1.toMap().value(key_).toDouble() < torrent2.toMap().value(key_).toDouble(); default: return greaterThan_ ? torrent1.toMap().value(key_).toString() > torrent2.toMap().value(key_).toString() - : torrent1.toMap().value(key_).toString() < torrent2.toMap().value(key_).toString(); + : torrent1.toMap().value(key_).toString() < torrent2.toMap().value(key_).toString(); } #else return greaterThan_ ? torrent1.toMap().value(key_) > torrent2.toMap().value(key_) - : torrent1.toMap().value(key_) < torrent2.toMap().value(key_); + : torrent1.toMap().value(key_) < torrent2.toMap().value(key_); #endif } @@ -236,7 +236,7 @@ static QVariantMap toMap(const QTorrentHandle& h) * - "state": Torrent state */ QByteArray btjson::getTorrents(QString filter, QString label, - QString sortedColumn, bool reverse, int limit, int offset) + QString sortedColumn, bool reverse, int limit, int offset) { QVariantList torrent_list; @@ -245,7 +245,7 @@ QByteArray btjson::getTorrents(QString filter, QString label, std::vector::const_iterator end = torrents.end(); QTorrentFilter torrentFilter(filter, label); - for( ; it != end; ++it) { + for(; it != end; ++it) { QTorrentHandle torrent = QTorrentHandle(*it); if (torrentFilter.apply(torrent)) @@ -294,8 +294,9 @@ QByteArray btjson::getTrackersForTorrent(const QString& hash) tracker_dict[KEY_TRACKER_URL] = tracker_url; const TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); QString status; - if (it->verified) + if (it->verified) { status = tr("Working"); + } else { if (it->updating && it->fails == 0) status = tr("Updating..."); diff --git a/src/webui/www/public/filters.html b/src/webui/www/public/filters.html index 6e220a9de..741889241 100644 --- a/src/webui/www/public/filters.html +++ b/src/webui/www/public/filters.html @@ -1,8 +1,8 @@ - \ No newline at end of file + \ No newline at end of file From 2a712a81ea9ee0d8101984c60e8f5b31a1e22ac8 Mon Sep 17 00:00:00 2001 From: buinsky Date: Tue, 9 Dec 2014 19:54:35 +0300 Subject: [PATCH 11/13] Add sequential downloading menu items --- src/qtlibtorrent/qtorrenthandle.cpp | 16 +++++++ src/qtlibtorrent/qtorrenthandle.h | 2 + src/transferlistwidget.cpp | 10 +---- src/webui/btjson.cpp | 8 ++++ src/webui/requesthandler.cpp | 26 +++++++++++ src/webui/requesthandler.h | 2 + src/webui/www/private/index.html | 2 + src/webui/www/public/scripts/client.js | 11 ++++- src/webui/www/public/scripts/contextmenu.js | 47 +++++++++++++++++--- src/webui/www/public/scripts/dynamicTable.js | 8 +++- src/webui/www/public/scripts/mocha-init.js | 28 ++++++++++++ src/webui/www/public/transferlist.html | 10 ++++- 12 files changed, 149 insertions(+), 21 deletions(-) diff --git a/src/qtlibtorrent/qtorrenthandle.cpp b/src/qtlibtorrent/qtorrenthandle.cpp index 62b03ce5c..d30d0cacc 100644 --- a/src/qtlibtorrent/qtorrenthandle.cpp +++ b/src/qtlibtorrent/qtorrenthandle.cpp @@ -489,6 +489,22 @@ qulonglong QTorrentHandle::eta() const return QBtSession::instance()->getETA(hash(), s); } +void QTorrentHandle::toggleSequentialDownload() +{ + if (is_valid() && has_metadata()) { + bool was_sequential = is_sequential_download(); + set_sequential_download(!was_sequential); + if (!was_sequential) + prioritize_first_last_piece(true); + } +} + +void QTorrentHandle::toggleFirstLastPiecePrio() +{ + if (is_valid() && has_metadata()) + prioritize_first_last_piece(!first_last_piece_first()); +} + // // Setters // diff --git a/src/qtlibtorrent/qtorrenthandle.h b/src/qtlibtorrent/qtorrenthandle.h index a36593ba5..9fc1e7364 100644 --- a/src/qtlibtorrent/qtorrenthandle.h +++ b/src/qtlibtorrent/qtorrenthandle.h @@ -128,6 +128,8 @@ public: void file_progress(std::vector& fp) const; QTorrentState torrentState() const; qulonglong eta() const; + void toggleSequentialDownload(); + void toggleFirstLastPiecePrio(); // // Setters diff --git a/src/transferlistwidget.cpp b/src/transferlistwidget.cpp index 382e76359..81109f489 100644 --- a/src/transferlistwidget.cpp +++ b/src/transferlistwidget.cpp @@ -610,12 +610,7 @@ void TransferListWidget::toggleSelectedTorrentsSequentialDownload() const const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) { - bool was_sequential = h.is_sequential_download(); - h.set_sequential_download(!was_sequential); - if (!was_sequential) - h.prioritize_first_last_piece(true); - } + h.toggleSequentialDownload(); } } @@ -624,8 +619,7 @@ void TransferListWidget::toggleSelectedFirstLastPiecePrio() const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) - h.prioritize_first_last_piece(!h.first_last_piece_first()); + h.toggleFirstLastPiecePrio(); } } diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 72878b7ba..0f422b4ad 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -98,6 +98,8 @@ static const char KEY_TORRENT_NUM_INCOMPLETE[] = "num_incomplete"; static const char KEY_TORRENT_RATIO[] = "ratio"; static const char KEY_TORRENT_ETA[] = "eta"; static const char KEY_TORRENT_STATE[] = "state"; +static const char KEY_TORRENT_SEQUENTIAL_DOWNLOAD[] = "seq_dl"; +static const char KEY_TORRENT_FIRST_LAST_PIECE_PRIO[] = "f_l_piece_prio"; // Tracker keys static const char KEY_TRACKER_URL[] = "url"; @@ -211,6 +213,12 @@ static QVariantMap toMap(const QTorrentHandle& h) ret[KEY_TORRENT_RATIO] = (ratio > 100.) ? -1 : ratio; ret[KEY_TORRENT_STATE] = h.torrentState().toString(); ret[KEY_TORRENT_ETA] = h.eta(); + if (h.has_metadata()) { + if (status.sequential_download) + ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = true; + if (h.first_last_piece_first()) + ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = true; + } return ret; } diff --git a/src/webui/requesthandler.cpp b/src/webui/requesthandler.cpp index 3b56c6daf..0276dbf4b 100644 --- a/src/webui/requesthandler.cpp +++ b/src/webui/requesthandler.cpp @@ -100,6 +100,8 @@ QMap > RequestHandler::initialize ADD_ACTION(command, getTorrentDlLimit); ADD_ACTION(command, setTorrentUpLimit); ADD_ACTION(command, setTorrentDlLimit); + ADD_ACTION(command, toggleSequentialDownload); + ADD_ACTION(command, toggleFirstLastPiecePrio); ADD_ACTION(command, delete); ADD_ACTION(command, deletePerm); ADD_ACTION(command, increasePrio); @@ -426,6 +428,30 @@ void RequestHandler::action_command_setTorrentDlLimit() h.set_download_limit(limit); } +void RequestHandler::action_command_toggleSequentialDownload() +{ + QStringList hashes = request().posts["hashes"].split("|"); + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + h.toggleSequentialDownload(); + } + catch(invalid_handle&) {} + } +} + +void RequestHandler::action_command_toggleFirstLastPiecePrio() +{ + QStringList hashes = request().posts["hashes"].split("|"); + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + h.toggleFirstLastPiecePrio(); + } + catch(invalid_handle&) {} + } +} + void RequestHandler::action_command_delete() { QStringList hashes = request().posts["hashes"].split("|"); diff --git a/src/webui/requesthandler.h b/src/webui/requesthandler.h index f1d019999..ec34385e1 100644 --- a/src/webui/requesthandler.h +++ b/src/webui/requesthandler.h @@ -74,6 +74,8 @@ private: void action_command_getTorrentDlLimit(); void action_command_setTorrentUpLimit(); void action_command_setTorrentDlLimit(); + void action_command_toggleSequentialDownload(); + void action_command_toggleFirstLastPiecePrio(); void action_command_delete(); void action_command_deletePerm(); void action_command_increasePrio(); diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 631cbddf2..4737eae1e 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -109,6 +109,8 @@
  • _(Limit download rate...) _(Limit download rate...)
  • _(Limit upload rate...) _(Limit upload rate...)
  • +
  • _(Download in sequential order) _(Download in sequential order)
  • +
  • _(Download first and last piece first) _(Download first and last piece first)
  • _(Force recheck) _(Force recheck)
  • diff --git a/src/webui/www/public/scripts/client.js b/src/webui/www/public/scripts/client.js index 9a4f7b9ba..d7aaa527a 100644 --- a/src/webui/www/public/scripts/client.js +++ b/src/webui/www/public/scripts/client.js @@ -99,14 +99,21 @@ var loadTorrentsInfo = function () { data[10] = event.ratio; if (row[2] != null) queueing_enabled = true; + + attrs = {}; + attrs['downloaded'] = (event.progress == 1.0); + attrs['state'] = event.state; + attrs['seq_dl'] = (event.seq_dl == true); + attrs['f_l_piece_prio'] = (event.f_l_piece_prio == true); + if (!torrent_hashes.contains(event.hash)) { // New unfinished torrent torrent_hashes[torrent_hashes.length] = event.hash; //alert("Inserting row"); - myTable.insertRow(event.hash, row, data, event.state, pos); + myTable.insertRow(event.hash, row, data, attrs, pos); } else { // Update torrent data - myTable.updateRow(event.hash, row, data, event.state, pos); + myTable.updateRow(event.hash, row, data, attrs, pos); } pos++; diff --git a/src/webui/www/public/scripts/contextmenu.js b/src/webui/www/public/scripts/contextmenu.js index abe0cedff..660bfb54a 100644 --- a/src/webui/www/public/scripts/contextmenu.js +++ b/src/webui/www/public/scripts/contextmenu.js @@ -127,9 +127,36 @@ var ContextMenu = new Class({ }.bind(this)); }, + updateMenuItems: function () { + all_are_seq_dl = true; + all_are_f_l_piece_prio = true; + all_are_downloaded = true; + + var h = myTable.selectedIds(); + h.each(function(item, index){ + tr = myTable.rows.get(item); + if (tr.getAttribute('seq_dl') != 'true') + all_are_seq_dl = false; + if (tr.getAttribute('f_l_piece_prio') != 'true') + all_are_f_l_piece_prio = false; + if (tr.getAttribute('downloaded') != 'true') + all_are_downloaded = false; + }); + + if (all_are_downloaded) { + this.hideItem('SequentialDownload'); + this.hideItem('FirstLastPiecePrio'); + } else { + this.showItem('SequentialDownload'); + this.showItem('FirstLastPiecePrio'); + this.setItemChecked('SequentialDownload', all_are_seq_dl); + this.setItemChecked('FirstLastPiecePrio', all_are_f_l_piece_prio); + } + }, + //show menu show: function(trigger) { - //this.menu.fade('in'); + this.updateMenuItems(); this.fx.start(1); this.fireEvent('show'); this.shown = true; @@ -147,15 +174,21 @@ var ContextMenu = new Class({ return this; }, - //disable an item - disableItem: function(item) { - this.menu.getElements('a[href$=' + item + ']').addClass('disabled'); + setItemChecked: function(item, checked) { + this.menu.getElement('a[href$=' + item + ']').firstChild.style.opacity = + checked ? '1' : '0'; + return this; + }, + + //hide an item + hideItem: function(item) { + this.menu.getElement('a[href$=' + item + ']').parentNode.addClass('invisible'); return this; }, - //enable an item - enableItem: function(item) { - this.menu.getElements('a[href$=' + item + ']').removeClass('disabled'); + //show an item + showItem: function(item) { + this.menu.getElement('a[href$=' + item + ']').parentNode.removeClass('invisible'); return this; }, diff --git a/src/webui/www/public/scripts/dynamicTable.js b/src/webui/www/public/scripts/dynamicTable.js index 4e032b362..0c5b2a44f 100644 --- a/src/webui/www/public/scripts/dynamicTable.js +++ b/src/webui/www/public/scripts/dynamicTable.js @@ -102,11 +102,13 @@ var dynamicTable = new Class({ this.priority_hidden = false; }, - insertRow : function (id, row, data, status, pos) { + insertRow : function (id, row, data, attrs, pos) { if (this.rows.has(id)) { return; } var tr = new Element('tr'); + for (var a in attrs) + tr.set(a, attrs[a]); tr.addClass("menu-target"); this.rows.set(id, tr); for (var i = 0; i < row.length; i++) { @@ -246,12 +248,14 @@ var dynamicTable = new Class({ }, this); }, - updateRow : function (id, row, data, status, newpos) { + updateRow : function (id, row, data, attrs, newpos) { if (!this.rows.has(id)) { return false; } var tr = this.rows.get(id); + for (var a in attrs) + tr.set(a, attrs[a]); var tds = tr.getElements('td'); for (var i = 0; i < row.length; i++) { if (i == 1) diff --git a/src/webui/www/public/scripts/mocha-init.js b/src/webui/www/public/scripts/mocha-init.js index 651bf19eb..1fe3791f7 100644 --- a/src/webui/www/public/scripts/mocha-init.js +++ b/src/webui/www/public/scripts/mocha-init.js @@ -134,6 +134,34 @@ initializeWindows = function() { } }; + toggleSequentialDownloadFN = function() { + var h = myTable.selectedIds(); + if (h.length) { + new Request({ + url: 'command/toggleSequentialDownload', + method: 'post', + data: { + hashes: h.join("|") + } + }).send(); + updateTransferList(); + } + }; + + toggleFirstLastPiecePrioFN = function() { + var h = myTable.selectedIds(); + if (h.length) { + new Request({ + url: 'command/toggleFirstLastPiecePrio', + method: 'post', + data: { + hashes: h.join("|") + } + }).send(); + updateTransferList(); + } + }; + globalDownloadLimitFN = function() { new MochaUI.Window({ id: 'downloadLimitPage', diff --git a/src/webui/www/public/transferlist.html b/src/webui/www/public/transferlist.html index 8cfa98680..1891a11f4 100644 --- a/src/webui/www/public/transferlist.html +++ b/src/webui/www/public/transferlist.html @@ -52,11 +52,17 @@ ForceRecheck : function (element, ref) { recheckFN(); }, - UploadLimit : function (element, red) { + UploadLimit : function (element, ref) { uploadLimitFN(); }, - DownloadLimit : function (element, red) { + DownloadLimit : function (element, ref) { downloadLimitFN(); + }, + SequentialDownload : function (element, ref) { + toggleSequentialDownloadFN(); + }, + FirstLastPiecePrio : function (element, ref) { + toggleFirstLastPiecePrioFN(); } }, offsets : { From 76e30ecf783c4fc3edc2166ba38921b565e7566d Mon Sep 17 00:00:00 2001 From: buinsky Date: Tue, 9 Dec 2014 21:03:14 +0300 Subject: [PATCH 12/13] Dynamically show 'Start' and 'Pause' menu items --- src/webui/www/public/scripts/contextmenu.js | 57 ++++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/webui/www/public/scripts/contextmenu.js b/src/webui/www/public/scripts/contextmenu.js index 660bfb54a..b459ec892 100644 --- a/src/webui/www/public/scripts/contextmenu.js +++ b/src/webui/www/public/scripts/contextmenu.js @@ -129,29 +129,82 @@ var ContextMenu = new Class({ updateMenuItems: function () { all_are_seq_dl = true; + there_are_seq_dl = false; all_are_f_l_piece_prio = true; + there_are_f_l_piece_prio = false; all_are_downloaded = true; + all_are_paused = true; + there_are_paused = false; var h = myTable.selectedIds(); h.each(function(item, index){ tr = myTable.rows.get(item); + if (tr.getAttribute('seq_dl') != 'true') all_are_seq_dl = false; + else + there_are_seq_dl = true; + if (tr.getAttribute('f_l_piece_prio') != 'true') all_are_f_l_piece_prio = false; + else + there_are_f_l_piece_prio = true; + if (tr.getAttribute('downloaded') != 'true') all_are_downloaded = false; + + state = tr.getAttribute('state'); + if ((state != 'pausedUP') && (state != 'pausedDL')) + all_are_paused = false; + else + there_are_paused = true; }); + show_seq_dl = true; + + if (!all_are_seq_dl && there_are_seq_dl) + show_seq_dl = false; + + show_f_l_piece_prio = true; + + if (!all_are_f_l_piece_prio && there_are_f_l_piece_prio) + show_f_l_piece_prio = false; + if (all_are_downloaded) { this.hideItem('SequentialDownload'); this.hideItem('FirstLastPiecePrio'); } else { - this.showItem('SequentialDownload'); - this.showItem('FirstLastPiecePrio'); + if (!show_seq_dl && show_f_l_piece_prio) + this.menu.getElement('a[href$=FirstLastPiecePrio]').parentNode.addClass('separator'); + else + this.menu.getElement('a[href$=FirstLastPiecePrio]').parentNode.removeClass('separator'); + + if (show_seq_dl) + this.showItem('SequentialDownload'); + else + this.hideItem('SequentialDownload'); + + if (show_f_l_piece_prio) + this.showItem('FirstLastPiecePrio'); + else + this.hideItem('FirstLastPiecePrio'); + this.setItemChecked('SequentialDownload', all_are_seq_dl); this.setItemChecked('FirstLastPiecePrio', all_are_f_l_piece_prio); } + + if (all_are_paused) { + this.showItem('Start'); + this.hideItem('Pause'); + } else { + if (there_are_paused) { + this.showItem('Start'); + this.showItem('Pause'); + } else { + this.hideItem('Start'); + this.showItem('Pause'); + } + } }, //show menu From 2c93330ce905f7478a4877e56844cf1aec7d9bd8 Mon Sep 17 00:00:00 2001 From: buinsky Date: Tue, 9 Dec 2014 23:55:16 +0300 Subject: [PATCH 13/13] Add space before priority menu item --- src/webui/www/private/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webui/www/private/index.html b/src/webui/www/private/index.html index 4737eae1e..7eafd52d4 100644 --- a/src/webui/www/private/index.html +++ b/src/webui/www/private/index.html @@ -99,7 +99,7 @@
  • _(Pause) _(Pause)
  • _(Delete) _(Delete)
  • - _(Priority) + _(Priority)