From a5e4a5056948f5d63f032f9c04d2ada64f4b6638 Mon Sep 17 00:00:00 2001
From: Kris Maglione
Date: Thu, 28 Apr 2011 16:55:55 -0400
Subject: [PATCH] Poke :downloads a bit. Add -sort flag, downloadsort option,
:dlclear command. Closes issue #386.
--HG--
extra : rebase_source : 1ce7621d39e91f2779d1827975b44f00c7a1d00b
---
common/content/dactyl.js | 3 +-
common/content/mow.js | 2 +-
common/locale/en-US/gui.xml | 15 +++
common/locale/en-US/messages.properties | 3 +
common/locale/en-US/options.xml | 26 +++++
common/modules/config.jsm | 2 +-
common/modules/downloads.jsm | 145 +++++++++++++++++++++---
common/modules/options.jsm | 8 ++
pentadactyl/NEWS | 4 +-
9 files changed, 187 insertions(+), 21 deletions(-)
diff --git a/common/content/dactyl.js b/common/content/dactyl.js
index 6b567acd..845dfb3e 100644
--- a/common/content/dactyl.js
+++ b/common/content/dactyl.js
@@ -983,6 +983,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
}
else if (obj instanceof Option) {
link = function (opt, name) {name};
+ args = { value: "", values: [] };
}
XML.prettyPrinting = false;
@@ -1027,7 +1028,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
}
if (obj.completer)
- add(completion._runCompleter(obj.completer, "", null, args).items
+ add(completion._runCompleter(obj.closure.completer, "", null, args).items
.map(function (i) [i.text, i.description]));
if (obj.options && obj.options.some(function (o) o.description))
diff --git a/common/content/mow.js b/common/content/mow.js
index 94cdfb51..5c7a28c6 100644
--- a/common/content/mow.js
+++ b/common/content/mow.js
@@ -131,8 +131,8 @@ var MOW = Module("mow", {
catch (e) {
util.reportError(e);
util.dump(data);
- this.messages.push(data);
}
+ this.messages.push(data);
}
else {
let style = isString(data) ? "pre" : "nowrap";
diff --git a/common/locale/en-US/gui.xml b/common/locale/en-US/gui.xml
index 9bb1151c..6cfa79a3 100644
--- a/common/locale/en-US/gui.xml
+++ b/common/locale/en-US/gui.xml
@@ -68,6 +68,21 @@
Show progress of current downloads. Here, downloads can
be paused, resumed, and canceled.
+
+ Available options include:
+
+
+ - -sort
- Sort order (see downloadsort) (short name: -s)
+
+
+
+
+-
+ :dlc :dlclear
+
+ :dlclear
+
+
Clear completed downloads.
diff --git a/common/locale/en-US/messages.properties b/common/locale/en-US/messages.properties
index 796c02eb..8097ebce 100644
--- a/common/locale/en-US/messages.properties
+++ b/common/locale/en-US/messages.properties
@@ -257,6 +257,9 @@ quickmark.added-2 = Added Quick Mark '%S': %S
save.invalidDestination-1 = Invalid destination: %S
+sort.ascending = ascending
+sort.descending = descending
+
status.link-1 = Link: %S
style.none = No style found
diff --git a/common/locale/en-US/options.xml b/common/locale/en-US/options.xml
index 8b026d9a..9dcb20f3 100644
--- a/common/locale/en-US/options.xml
+++ b/common/locale/en-US/options.xml
@@ -519,6 +519,32 @@
+-
+ 'dls' 'dlsort' 'downloadsort'
+ 'downloadsort'
+ stringlist
+ -active,+filename
+
+
+ :downloads sort order, in order of precedence.
+ Each element must be preceded by a + or
+ -, indicating ascending or descending sorting,
+ respectively. Valid sort orders are:
+
+
+
+ - active
- Whether download is active
+ - complete
- Percent complete
+ - filename
- Target filename
+ - size
- File size
+ - speed
- Download speed
+ - time
- Time remaining
+ - url
- Source URL
+
+
+
+
+
-
'ds' 'defsearch'
'defsearch' 'ds'
diff --git a/common/modules/config.jsm b/common/modules/config.jsm
index 61766ad3..e9f85fa9 100644
--- a/common/modules/config.jsm
+++ b/common/modules/config.jsm
@@ -479,7 +479,7 @@ var ConfigBase = Class("ConfigBase", {
Dense margin-top: 0; margin-bottom: 0;
- EditorEditing;;* background: #bbb !important; -moz-user-input: none !important; -moz-user-modify: read-only !important;
+ EditorEditing;;* background-color: #bbb !important; -moz-user-input: none !important; -moz-user-modify: read-only !important;
EditorError;;* background: red !important;
EditorBlink1;;* background: yellow !important;
EditorBlink2;;*
diff --git a/common/modules/downloads.jsm b/common/modules/downloads.jsm
index b4d8e070..a565a01f 100644
--- a/common/modules/downloads.jsm
+++ b/common/modules/downloads.jsm
@@ -51,6 +51,7 @@ var Download = Class("Download", {
/>/
|
+ |
|
{self.source.spec} |
,
@@ -120,25 +121,46 @@ var Download = Class("Download", {
}
},
- compare: function compare(other) String.localeCompare(this.displayName, other.displayName),
+ _compare: {
+ active: function (a, b) a.alive - b.alive,
+ complete: function (a, b) a.percentComplete - b.percentComplete,
+ filename: function (a, b) String.localeCompare(a.targetFile.leafName, b.targetFile.leafName),
+ size: function (a, b) a.size - b.size,
+ speed: function (a, b) a.speed - b.speed,
+ time: function (a, b) a.timeRemaining - b.timeRemaining,
+ url: function (a, b) String.localeCompare(a.source.spec, b.source.spec)
+ },
+
+ compare: function compare(other) values(this.list.sortOrder).map(function (order) {
+ let val = this._compare[order.substr(1)](this, other);
+
+ return (order[0] == "-") ? -val : val;
+ }, this).nth(util.identity, 0) || 0,
timeRemaining: Infinity,
updateProgress: function updateProgress() {
let self = this.__proto__;
- if (this.amountTransferred === this.size)
+ if (this.amountTransferred === this.size) {
+ this.nodes.speed.textContent = "";
this.nodes.time.textContent = "";
- else if (this.speed == 0 || this.size == 0)
- this.nodes.time.textContent = "Unknown";
- else {
- let seconds = (this.size - this.amountTransferred) / this.speed;
- [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining);
- if (this.timeRemaining)
- this.nodes.time.textContent = util.formatSeconds(this.timeRemaining);
- else
- this.nodes.time.textContent = /*L*/"~1 second";
}
+ else {
+ this.nodes.speed.textContent = util.formatBytes(this.speed, 1, true) + "/s";
+
+ if (this.speed == 0 || this.size == 0)
+ this.nodes.time.textContent = "Unknown";
+ else {
+ let seconds = (this.size - this.amountTransferred) / this.speed;
+ [, self.timeRemaining] = DownloadUtils.getTimeLeft(seconds, this.timeRemaining);
+ if (this.timeRemaining)
+ this.nodes.time.textContent = util.formatSeconds(this.timeRemaining);
+ else
+ this.nodes.time.textContent = /*L*/"~1 second";
+ }
+ }
+
let total = this.nodes.progressTotal.textContent = this.size ? util.formatBytes(this.size, 1, true) : "Unknown";
let suffix = RegExp(/( [a-z]+)?$/i.exec(total)[0] + "$");
this.nodes.progressHave.textContent = util.formatBytes(this.amountTransferred, 1, true).replace(suffix, "");
@@ -165,7 +187,8 @@ var DownloadList = Class("DownloadList",
XPCOM([Ci.nsIDownloadProgressListener,
Ci.nsIObserver,
Ci.nsISupportsWeakReference]), {
- init: function init(modules, filter) {
+ init: function init(modules, filter, sort) {
+ this.sortOrder = sort;
this.modules = modules;
this.filter = filter && filter.toLowerCase();
this.nodes = {
@@ -173,6 +196,7 @@ var DownloadList = Class("DownloadList",
};
this.downloads = {};
},
+
cleanup: function cleanup() {
this.observe.unregister();
services.downloadManager.removeListener(this);
@@ -187,6 +211,7 @@ var DownloadList = Class("DownloadList",
Progress
+ Speed
Time remaining
Source
@@ -202,6 +227,7 @@ var DownloadList = Class("DownloadList",
/>/
|
+ |
|
|
@@ -253,6 +279,17 @@ var DownloadList = Class("DownloadList",
}
},
+ sort: function sort() {
+ let list = values(this.downloads).sort(function (a, b) a.compare(b));
+
+ for (let [i, download] in iter(list))
+ if (this.nodes.list.childNodes[i + 1] != download.nodes.row)
+ this.nodes.list.insertBefore(download.nodes.row,
+ this.nodes.list.childNodes[i + 1]);
+ },
+
+ shouldSort: function shouldSort() Array.some(arguments, function (val) this.sortOrder.some(function (v) v.substr(1) == val), this),
+
update: function update() {
for (let node in values(this.nodes))
if (node.update && node.update != update)
@@ -278,8 +315,11 @@ var DownloadList = Class("DownloadList",
let active = downloads.filter(function (dl) dl.alive).length;
if (active)
this.nodes.total.textContent = /*L*/active + " active";
- else for (let key in values(["total", "percent", "time"]))
+ else for (let key in values(["total", "percent", "speed", "time"]))
this.nodes[key].textContent = "";
+
+ if (this.shouldSort("complete", "size", "speed", "time"))
+ this.sort();
},
observers: {
@@ -305,11 +345,15 @@ var DownloadList = Class("DownloadList",
this.nodes.list.scrollIntoView(false);
}
this.update();
+
+ if (this.shouldSort("active"))
+ this.sort();
}
catch (e) {
util.reportError(e);
}
},
+
onProgressChange: function (webProgress, request,
curProgress, maxProgress,
curTotalProgress, maxTotalProgress,
@@ -328,17 +372,84 @@ var DownloadList = Class("DownloadList",
var Downloads = Module("downloads", {
}, {
}, {
- commands: function (dactyl, modules, window) {
- const { commands } = modules;
+ commands: function initCommands(dactyl, modules, window) {
+ const { commands, CommandOption } = modules;
commands.add(["downl[oads]", "dl"],
"Display the downloads list",
function (args) {
- let downloads = DownloadList(modules, args[0]);
+ let downloads = DownloadList(modules, args[0], args["-sort"]);
modules.commandline.echo(downloads);
},
{
- argCount: "?"
+ argCount: "?",
+ options: [
+ {
+ names: ["-sort", "-s"],
+ description: "Sort order (see 'downloadsort')",
+ type: CommandOption.LIST,
+ get default() modules.options["downloadsort"],
+ completer: function (context, args) modules.options.get("downloadsort").completer(context, { values: args["-sort"] }),
+ validator: function (value) modules.options.get("downloadsort").validator(value)
+ }
+ ]
+ });
+
+ commands.add(["dlc[lear]"],
+ "Clear completed downloads",
+ function (args) { services.downloadManager.cleanUp(); });
+ },
+ options: function initOptions(dactyl, modules, window) {
+ const { options } = modules;
+
+ if (false)
+ options.add(["downloadcolumns", "dlc"],
+ "The columns to show in the download manager",
+ "stringlist", "filename,state,buttons,progress,percent,time,url",
+ {
+ values: {
+ buttons: "Control buttons",
+ filename: "Target filename",
+ percent: "Percent complete",
+ size: "File size",
+ speed: "Download speed",
+ state: "The download's state",
+ time: "Time remaining",
+ url: "Source URL"
+ }
+ });
+
+ options.add(["downloadsort", "dlsort", "dls"],
+ ":downloads sort order",
+ "stringlist", "-active,+filename",
+ {
+ values: {
+ active: "Whether download is active",
+ complete: "Percent complete",
+ filename: "Target filename",
+ size: "File size",
+ speed: "Download speed",
+ time: "Time remaining",
+ url: "Source URL"
+ },
+
+ completer: function (context, extra) {
+ let seen = set.has(set(extra.values.map(function (val) val.substr(1))));
+
+ context.completions = iter(this.values).filter(function ([k, v]) !seen(k))
+ .map(function ([k, v]) [["+" + k, [v, " (", _("sort.ascending"), ")"].join("")],
+ ["-" + k, [v, " (", _("sort.descending"), ")"].join("")]])
+ .flatten().array;
+ },
+
+ has: function () Array.some(arguments, function (val) this.value.some(function (v) v.substr(1) == val)),
+
+ validator: function (value) {
+ let seen = {};
+ return value.every(function (val) /^[+-]/.test(val) && set.has(this.values, val.substr(1))
+ && !set.add(seen, val.substr(1)),
+ this) && value.length;
+ }
});
}
});
diff --git a/common/modules/options.jsm b/common/modules/options.jsm
index dc73558a..0bc9658c 100644
--- a/common/modules/options.jsm
+++ b/common/modules/options.jsm
@@ -1439,6 +1439,14 @@ var Options = Module("options", {
context.filters.push(function (i) curValues.indexOf(i.text) == -1);
if (op == "-")
context.filters.push(function (i) curValues.indexOf(i.text) > -1);
+
+ memoize(extra, "values", function () {
+ if (op == "+")
+ return curValues.concat(newValues);
+ if (op == "-")
+ return curValues.filter(function (v) newValues.indexOf(val) == -1);
+ return newValues;
+ });
}
let res = completer.call(opt, context, extra);
diff --git a/pentadactyl/NEWS b/pentadactyl/NEWS
index 4dcd78b5..f30fe5aa 100644
--- a/pentadactyl/NEWS
+++ b/pentadactyl/NEWS
@@ -89,7 +89,8 @@
listing keys for modes other than Normal, filtering the output,
and linking to source code locations). [b4]
- :downloads now opens a download list in the multi-line output
- buffer. [b6]
+ buffer. Added -sort flag. [b6][b7]
+ - Added :dlclear command. [b7]
- :extensions has been replaced with a more powerful :addons. [b6]
- :javascript! now opens a Read Eval Print Loop. [b6]
- Added -arg flag to :map. [b6]
@@ -177,6 +178,7 @@
- Added 'autocomplete' option for specifying which completion
groups should be auto-completed. [b2]
- Added 'banghist' option. [b1]
+ - Added 'downloadsort' option. [b7]
- Replaced 'focuscontent' with 'strictfocus'. [b1]
- 'complete' now defaults to "slf" but file completion only
triggers when the URL begins as above. [b1]