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]