diff --git a/gui/application.ini b/gui/application.ini
index 0da5e498dad..a832d9d285b 100644
--- a/gui/application.ini
+++ b/gui/application.ini
@@ -32,4 +32,4 @@ MinVersion=1.9a5
; This field is optional. It specifies the maximum Gecko version that this
; application requires. It should be specified if your application uses
; unfrozen interfaces.
-;MaxVersion=1.9.0.*
+MaxVersion=2.*
diff --git a/gui/chrome.manifest b/gui/chrome.manifest
new file mode 100644
index 00000000000..865d6a88fb4
--- /dev/null
+++ b/gui/chrome.manifest
@@ -0,0 +1 @@
+manifest chrome/chrome.manifest
diff --git a/gui/chrome/chrome.manifest b/gui/chrome/chrome.manifest
index 6b524807eaa..775445ee17b 100644
--- a/gui/chrome/chrome.manifest
+++ b/gui/chrome/chrome.manifest
@@ -1 +1 @@
-content nixos-gui content/nixos-gui/
+content nixos-gui content/
diff --git a/gui/chrome/content/nixos-gui/io.js b/gui/chrome/content/io.js
similarity index 54%
rename from gui/chrome/content/nixos-gui/io.js
rename to gui/chrome/content/io.js
index 3e3b080b814..8d9c8c17365 100644
--- a/gui/chrome/content/nixos-gui/io.js
+++ b/gui/chrome/content/io.js
@@ -1,3 +1,66 @@
+
+function inspect(obj, maxLevels, level)
+{
+ var str = '', type, msg;
+
+ // Start Input Validations
+ // Don't touch, we start iterating at level zero
+ if(level == null) level = 0;
+
+ // At least you want to show the first level
+ if(maxLevels == null) maxLevels = 1;
+ if(maxLevels < 1)
+ return 'Error: Levels number must be > 0';
+
+ // We start with a non null object
+ if(obj == null)
+ return 'Error: Object NULL';
+ // End Input Validations
+
+ // Each Iteration must be indented
+ str += '
';
+
+ // Start iterations for all objects in obj
+ for(property in obj)
+ {
+ try
+ {
+ // Show "property" and "type property"
+ type = typeof(obj[property]);
+ str += '- (' + type + ') ' + property +
+ ( (obj[property]==null)?(': null'):('')) + '
';
+
+ // We keep iterating if this property is an Object, non null
+ // and we are inside the required number of levels
+ if((type == 'object') && (obj[property] != null) && (level+1 < maxLevels))
+ str += inspect(obj[property], maxLevels, level+1);
+ }
+ catch(err)
+ {
+ // Is there some properties in obj we can't access? Print it red.
+ if(typeof(err) == 'string') msg = err;
+ else if(err.message) msg = err.message;
+ else if(err.description) msg = err.description;
+ else msg = 'Unknown';
+
+ str += '- (Error) ' + property + ': ' + msg +'
';
+ }
+ }
+
+ // Close indent
+ str += '
';
+
+ return str;
+}
+
+// Run xulrunner application.ini -jsconsole -console, to see messages.
+function log(str)
+{
+ Components.classes['@mozilla.org/consoleservice;1']
+ .getService(Components.interfaces.nsIConsoleService)
+ .logStringMessage(str);
+}
+
function makeTempFile(prefix)
{
var file = Components.classes["@mozilla.org/file/directory_service;1"]
@@ -29,7 +92,7 @@ function readFromFile(file)
var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
fstream.init(file, -1, 0, 0);
- sstream.init(fstream);
+ sstream.init(fstream);
var str = sstream.read(4096);
while (str.length > 0) {
diff --git a/gui/chrome/content/main.js b/gui/chrome/content/main.js
new file mode 100644
index 00000000000..de272e27378
--- /dev/null
+++ b/gui/chrome/content/main.js
@@ -0,0 +1,40 @@
+// global variables.
+var gNixOS;
+var gOptionView;
+
+/*
+var gProgressBar;
+function setProgress(current, max)
+{
+ if (gProgressBar) {
+ gProgressBar.value = 100 * current / max;
+ log("progress: " + gProgressBar.value + "%");
+ }
+ else
+ log("unknow progress bar");
+}
+*/
+
+function updatePanel(options)
+{
+ log("updatePanel: " + options.length);
+ var t = "";
+ for (var i = 0; i < options.length; i++)
+ {
+ log("Called with " + options[i].path);
+ t += options[i].description;
+ }
+ $("#option-desc").text(t);
+}
+
+
+function onload()
+{
+ var optionTree = document.getElementById("option-tree");
+ // gProgressBar = document.getElementById("progress-bar");
+ // setProgress(0, 1);
+
+ gNixOS = new NixOS();
+ gOptionView = new OptionView(gNixOS.option, updatePanel);
+ optionTree.view = gOptionView;
+}
diff --git a/gui/chrome/content/myviewer.xul b/gui/chrome/content/myviewer.xul
new file mode 100644
index 00000000000..23c6ce0a65d
--- /dev/null
+++ b/gui/chrome/content/myviewer.xul
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gui/chrome/content/nixos-gui/main.js b/gui/chrome/content/nixos-gui/main.js
deleted file mode 100644
index baa934e43e2..00000000000
--- a/gui/chrome/content/nixos-gui/main.js
+++ /dev/null
@@ -1,171 +0,0 @@
-var COPYCOL = 2;
-var gOptionListView = new treeView(["opt-success","opt-name","opt-desc"],
- COPYCOL);
-
-// Run xulrunner application.ini -jsconsole -console, to see messages.
-function log(str)
-{
- Components.classes['@mozilla.org/consoleservice;1']
- .getService(Components.interfaces.nsIConsoleService)
- .logStringMessage(str);
-}
-
-
-// return the DOM of the value returned by nix-instantiate
-function dumpOptions(path)
-{
- var nixInstantiate = "nix-instantiate"; // "@nix@/bin/nix-instantiate";
- var nixos = "/etc/nixos/nixos/default.nix"; // "@nixos@/default.nix";
-
- var o = makeTempFile("nixos-options");
-
- path = "eval.options" + (path? "." + path : "");
- log("retrieve options from: " + path);
-
- runProgram(nixInstantiate+" "+nixos+" -A "+path+" --eval-only --strict --xml 2>/dev/null | tr -d '' >" + o.path);
-
- var xml = readFromFile(o);
- o.remove(false);
-
- // jQuery does a stack overflow when converting the XML to a DOM.
- var dom = DOMParser().parseFromString(xml, "text/xml");
-
- log("return dom");
- return dom;
-}
-
-
-// Pretty print Nix values.
-function nixPP(value, level)
-{
- function indent(level) { ret = ""; while (level--) ret+= " "; return ret; }
-
- if (!level) level = 0;
- var ret = "";
- if (value.is("attrs")) {
- var content = "";
- value.children().each(function (){
- var name = $(this).attr("name");
- var value = nixPP($(this).children(), level + 1);
- content += indent(level + 1) + name + " = " + value + ";\n";
- });
- ret = "{\n" + content + indent(level) + "}";
- }
- else if (value.is("list")) {
- var content = "";
- value.children().each(function (){
- content += indent(level + 1) + "(" + nixPP($(this), level + 1) + ")\n";
- });
- ret = "[\n" + content + indent(level) + "]";
- }
- else if (value.is("bool"))
- ret = (value.attr("value") == "true");
- else if (value.is("string"))
- ret = '"' + value.attr("value") + '"';
- else if (value.is("path"))
- ret = value.attr("value");
- else if (value.is("int"))
- ret = parseInt(value.attr("value"));
- else if (value.is("derivation"))
- ret = value.attr("outPath");
- else if (value.is("function"))
- ret = "";
- else {
- var content = "";
- value.children().each(function (){
- content += indent(level + 1) + "(" + nixPP($(this), level + 1) + ")\n";
- });
- ret = "";
- }
- return ret;
-}
-
-// Function used to reproduce the select operator on the XML DOM.
-// It return the value contained in the targeted attribute.
-function nixSelect(attrs, selector)
-{
- var names = selector.split(".");
- var value = $(attrs);
- for (var i = 0; i < names.length; i++) {
- log(nixPP(value) + "." + names[i]);
- if (value.is("attrs"))
- value = value.children("attr[name='" + names[i] + "']").children();
- else {
- log("Cannot do an attribute selection.");
- break
- }
- }
-
- log("nixSelect return: " + nixPP(value));
-
- var ret;
- if (value.is("attrs") || value.is("list"))
- ret = value;
- else if (value.is("bool"))
- ret = value.attr("value") == "true";
- else if (value.is("string"))
- ret = value.attr("value");
- else if (value.is("int"))
- ret = parseInt(value.attr("value"));
- else if (value.is("derivation"))
- ret = value.attr("outPath");
- else if (value.is("function"))
- ret = "";
-
- return ret;
-}
-
-var gProgressBar;
-function setProgress(current, max)
-{
- if (gProgressBar) {
- gProgressBar.value = 100 * current / max;
- log("progress: " + gProgressBar.value + "%");
- }
- else
- log("unknow progress bar");
-}
-
-// fill the list of options
-function setOptionList(optionDOM)
-{
- var options = $("attrs", optionDOM).filter(function () {
- return $(this)
- .children("attr[name='_type']")
- .children("string[value='option']")
- .length != 0;
- });
-
- var max = options.length;
-
- log("Number of options: " + max);
-
- setProgress(0, max);
- gOptionListView.clear();
- options.each(function (index){
- var success = nixSelect(this, "config.success");
- var name = nixSelect(this, "name");
- var desc = nixSelect(this, "description");
- if (success && name && desc) {
- log("Add option '" + name + "' in the list.");
- gOptionListView.addRow([success, name, desc]);
- }
- else
- log("A problem occur while scanning an option.");
- setProgress(index + 1, max);
- });
-}
-
-
-function onload()
-{
- var optionList = document.getElementById("option-list");
- gProgressBar = document.getElementById("progress-bar");
- setProgress(0, 1);
- optionList.view = gOptionListView;
-
- // try to avoid blocking the rendering, unfortunately this is not perfect.
- setTimeout(function (){
- setOptionList(dumpOptions("hardware"));}
- , 100);
-}
diff --git a/gui/chrome/content/nixos-gui/myviewer.xul b/gui/chrome/content/nixos-gui/myviewer.xul
deleted file mode 100644
index c0c2bd6f822..00000000000
--- a/gui/chrome/content/nixos-gui/myviewer.xul
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/gui/chrome/content/nixos-gui/treeView.js b/gui/chrome/content/nixos-gui/treeView.js
deleted file mode 100644
index 8ab7c875e68..00000000000
--- a/gui/chrome/content/nixos-gui/treeView.js
+++ /dev/null
@@ -1,117 +0,0 @@
-// Taken from pageInfo.js
-
-//******** define a js object to implement nsITreeView
-function treeView(columnids, copycol)
-{
- // columnids is an array of strings indicating the names of the columns, in order
- this.columnids = columnids;
- this.colcount = columnids.length;
-
- // copycol is the index number for the column that we want to add to
- // the copy-n-paste buffer when the user hits accel-c
- this.copycol = copycol;
- this.rows = 0;
- this.tree = null;
- this.data = [ ];
- this.selection = null;
- this.sortcol = null;
- this.sortdir = 0;
-}
-
-treeView.prototype = {
- set rowCount(c) { throw "rowCount is a readonly property"; },
- get rowCount() { return this.rows; },
-
- setTree: function(tree)
- {
- this.tree = tree;
- },
-
- getCellText: function(row, column)
- {
- // row can be null, but js arrays are 0-indexed.
- // colidx cannot be null, but can be larger than the number
- // of columns in the array (when column is a string not in
- // this.columnids.) In this case it's the fault of
- // whoever typoed while calling this function.
- return this.data[row][column.index] || "";
- },
-
- setCellValue: function(row, column, value)
- {
- },
-
- setCellText: function(row, column, value)
- {
- this.data[row][column.index] = value;
- },
-
- addRow: function(row)
- {
- this.rows = this.data.push(row);
- this.rowCountChanged(this.rows - 1, 1);
- },
-
- addRows: function(rows)
- {
- var length = rows.length;
- for(var i = 0; i < length; i++)
- this.rows = this.data.push(rows[i]);
- this.rowCountChanged(this.rows - length, length);
- },
-
- rowCountChanged: function(index, count)
- {
- this.tree.rowCountChanged(index, count);
- },
-
- invalidate: function()
- {
- this.tree.invalidate();
- },
-
- clear: function()
- {
- if (this.tree)
- this.tree.rowCountChanged(0, -this.rows);
- this.rows = 0;
- this.data = [ ];
- },
-
- handleCopy: function(row)
- {
- return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || "");
- },
-
- performActionOnRow: function(action, row)
- {
- if (action == "copy") {
- var data = this.handleCopy(row)
- this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
- }
- },
-
- getRowProperties: function(row, prop) { },
- getCellProperties: function(row, column, prop) { },
- getColumnProperties: function(column, prop) { },
- isContainer: function(index) { return false; },
- isContainerOpen: function(index) { return false; },
- isSeparator: function(index) { return false; },
- isSorted: function() { },
- canDrop: function(index, orientation) { return false; },
- drop: function(row, orientation) { return false; },
- getParentIndex: function(index) { return 0; },
- hasNextSibling: function(index, after) { return false; },
- getLevel: function(index) { return 0; },
- getImageSrc: function(row, column) { },
- getProgressMode: function(row, column) { },
- getCellValue: function(row, column) { },
- toggleOpenState: function(index) { },
- cycleHeader: function(col) { },
- selectionChanged: function() { },
- cycleCell: function(row, column) { },
- isEditable: function(row, column) { return false; },
- isSelectable: function(row, column) { return false; },
- performAction: function(action) { },
- performActionOnCell: function(action, row, column) { }
-};
diff --git a/gui/chrome/content/nixos.js b/gui/chrome/content/nixos.js
new file mode 100644
index 00000000000..411dc844c4f
--- /dev/null
+++ b/gui/chrome/content/nixos.js
@@ -0,0 +1,205 @@
+
+function NixOS () {
+ var env = Components.classes["@mozilla.org/process/environment;1"].
+ getService(Components.interfaces.nsIEnvironment);
+
+ if (env.exists("NIXOS"))
+ this.nixos = env.get("NIXOS");
+ if (env.exists("NIXOS_CONFIG"))
+ this.config = env.get("NIXOS_CONFIG");
+ if (env.exists("NIXPKGS"))
+ this.nixpkgs = env.get("NIXPKGS");
+ if (env.exists("mountPoint"))
+ this.root = env.get("mountPoint");
+ if (env.exists("NIXOS_OPTION"))
+ this.optionBin = env.get("NIXOS_OPTION");
+ this.option = new Option("options", this, null);
+};
+
+NixOS.prototype = {
+ root: "",
+ nixos: "/etc/nixos/nixos",
+ nixpkgs: "/etc/nixos/nixpkgs",
+ config: "/etc/nixos/configuration.nix",
+ instantiateBin: "/var/run/current-system/sw/bin/nix-instantiate",
+ optionBin: "/var/run/current-system/sw/bin/nixos-option",
+ tmpFile: "nixos-gui",
+ option: null
+};
+
+function Option (name, context, parent) {
+ this.name = name;
+ this.context_ = context;
+ if (parent == null)
+ this.path = "";
+ else if (parent.path == "")
+ this.path = name;
+ else
+ this.path = parent.path + "." + name;
+};
+
+Option.prototype = {
+ load: function () {
+ var env = "";
+ env += "'NIXOS=" + this.context_.root + this.context_.nixos + "' ";
+ env += "'NIXOS_PKGS=" + this.context_.root + this.context_.nixpkgs + "' ";
+ env += "'NIXOS_CONFIG=" + this.context_.config + "' ";
+ var out = makeTempFile(this.context_.tmpFile);
+ var prog = this.context_.instantiateBin + " 2>&1 >" + out.path + " ";
+ var args = "";
+ args += " -A eval.options" + (this.path != "" ? "." : "") + this.path;
+ args += " --eval-only --xml --no-location";
+ args += " '" + this.context_.root + this.context_.nixos + "'";
+
+ runProgram(/*env +*/ prog + args);
+ var xml = readFromFile(out);
+ out.remove(false);
+
+ // jQuery does a stack overflow when converting a huge XML to a DOM.
+ var dom = DOMParser().parseFromString(xml, "text/xml");
+ var xmlAttrs = $("attr", dom);
+
+ this.isOption = xmlAttrs
+ .filter (
+ function (idx) {
+ return $(this).attr("name") == "_type";
+ // !!! We could not rely on the value of the attribute because it
+ // !!! may be unevaluated.
+ // $(this).children("string[value='option']").length != 0;
+ })
+ .length != 0;
+
+ if (!this.isOption)
+ {
+ var cur = this;
+ var attrs = new Array();
+
+ xmlAttrs.each(
+ function (index) {
+ var name = $(this).attr("name");
+ var attr = new Option(name, cur.context_, cur);
+ attrs.push(attr);
+ }
+ );
+
+ this.subOptions = attrs;
+ }
+ else
+ {
+ this.loadDesc();
+ // TODO: handle sub-options here.
+ }
+ this.isLoaded = true;
+ },
+
+ loadDesc: function () {
+ var env = "";
+ env += "'NIXOS=" + this.context_.root + this.context_.nixos + "' ";
+ env += "'NIXOS_PKGS=" + this.context_.root + this.context_.nixpkgs + "' ";
+ env += "'NIXOS_CONFIG=" + this.context_.config + "' ";
+ var out = makeTempFile(this.context_.tmpFile);
+ var prog = this.context_.optionBin + " 2>&1 >" + out.path + " ";
+ var args = " -vdl " + this.path;
+
+ runProgram(/*env + */ prog + args);
+ this.description = readFromFile(out);
+ out.remove(false);
+ },
+
+ // keep the context under which this option has been used.
+ context_: null,
+ // name of the option.
+ name: "",
+ // result of nixos-option.
+ description: "",
+ // path to reach this option
+ path: "",
+
+ // list of options accessible from here.
+ isLoaded: false,
+ isOption: false,
+ subOptions: []
+};
+
+
+/*
+// Pretty print Nix values.
+function nixPP(value, level)
+{
+ function indent(level) { ret = ""; while (level--) ret+= " "; return ret; }
+
+ if (!level) level = 0;
+ var ret = "";
+ if (value.is("attrs")) {
+ var content = "";
+ value.children().each(function (){
+ var name = $(this).attr("name");
+ var value = nixPP($(this).children(), level + 1);
+ content += indent(level + 1) + name + " = " + value + ";\n";
+ });
+ ret = "{\n" + content + indent(level) + "}";
+ }
+ else if (value.is("list")) {
+ var content = "";
+ value.children().each(function (){
+ content += indent(level + 1) + "(" + nixPP($(this), level + 1) + ")\n";
+ });
+ ret = "[\n" + content + indent(level) + "]";
+ }
+ else if (value.is("bool"))
+ ret = (value.attr("value") == "true");
+ else if (value.is("string"))
+ ret = '"' + value.attr("value") + '"';
+ else if (value.is("path"))
+ ret = value.attr("value");
+ else if (value.is("int"))
+ ret = parseInt(value.attr("value"));
+ else if (value.is("derivation"))
+ ret = value.attr("outPath");
+ else if (value.is("function"))
+ ret = "";
+ else {
+ var content = "";
+ value.children().each(function (){
+ content += indent(level + 1) + "(" + nixPP($(this), level + 1) + ")\n";
+ });
+ ret = "";
+ }
+ return ret;
+}
+
+// Function used to reproduce the select operator on the XML DOM.
+// It return the value contained in the targeted attribute.
+function nixSelect(attrs, selector)
+{
+ var names = selector.split(".");
+ var value = $(attrs);
+ for (var i = 0; i < names.length; i++) {
+ log(nixPP(value) + "." + names[i]);
+ if (value.is("attrs"))
+ value = value.children("attr[name='" + names[i] + "']").children();
+ else {
+ log("Cannot do an attribute selection.");
+ break;
+ }
+ }
+
+ log("nixSelect return: " + nixPP(value));
+
+ var ret;
+ if (value.is("attrs") || value.is("list"))
+ ret = value;
+ else if (value.is("bool"))
+ ret = value.attr("value") == "true";
+ else if (value.is("string"))
+ ret = value.attr("value");
+ else if (value.is("int"))
+ ret = parseInt(value.attr("value"));
+ else if (value.is("derivation"))
+ ret = value.attr("outPath");
+ else if (value.is("function"))
+ ret = "";
+
+ return ret;
+}
+*/
\ No newline at end of file
diff --git a/gui/chrome/content/optionView.js b/gui/chrome/content/optionView.js
new file mode 100644
index 00000000000..8e04e282828
--- /dev/null
+++ b/gui/chrome/content/optionView.js
@@ -0,0 +1,233 @@
+// extend NixOS options to handle the Tree View. Should be better to keep a
+// separation of concern here.
+
+Option.prototype.tv_opened = false;
+Option.prototype.tv_size = 1;
+
+Option.prototype.tv_open = function () {
+ this.tv_opened = true;
+ this.tv_size = 1;
+
+ // load an option if it is not loaded yet, and initialize them to be
+ // read by the Option view.
+ if (!this.isLoaded)
+ this.load();
+
+ // If this is not an option, then add it's lits of sub-options size.
+ if (!this.isOption)
+ {
+ for (var i = 0; i < this.subOptions.length; i++)
+ this.tv_size += this.subOptions[i].tv_size;
+ }
+};
+
+Option.prototype.tv_close = function () {
+ this.tv_opened = false;
+ this.tv_size = 1;
+};
+
+
+
+
+function OptionView (root, selCallback) {
+ root.tv_open();
+ this.rootOption = root;
+ this.selCallback = selCallback;
+}
+
+OptionView.prototype = {
+ rootOption: null,
+ selCallback: null,
+
+ // This function returns the path to option which is at the specified row.
+ reach_cache: null,
+ reachRow: function (row) {
+ var o = this.rootOption; // Current option.
+ var r = 0; // Number of rows traversed.
+ var c = 0; // Child index.
+ var path = [{ row: r, opt: o }]; // new Array();
+ // hypothesis: this.rootOption.tv_size is always open and bigger than
+
+ // Use the previous returned value to avoid making to many checks and to
+ // optimize for frequent access of near rows.
+ if (this.reach_cache != null)
+ {
+ for (var i = this.reach_cache.length - 2; i >= 0; i--) {
+ var p = this.reach_cache[i];
+ // If we will have to go the same path.
+ if (row >= p.row && row < p.row + p.opt.tv_size)
+ {
+ path.unshift(p);
+ r = path[0].row;
+ o = path[0].opt;
+ }
+ else
+ break;
+ };
+ }
+
+ while (r != row)
+ {
+ // Go deeper in the child which contains the requested row. The
+ // tv_size contains the size of the tree starting from each option.
+ c = 0;
+ while (c < o.subOptions.length && r + o.subOptions[c].tv_size < row)
+ {
+ r += o.subOptions[c].tv_size;
+ c += 1;
+ }
+ if (c < o.subOptions.length && r + o.subOptions[c].tv_size >= row)
+ {
+ // Count the current option as a row.
+ o = o.subOptions[c];
+ r += 1;
+ }
+ else
+ alert("WTF: " + o.name + " ask: " + row + " children: " + o.subOptions + " c: " + c);
+ path.unshift({ row: r, opt: o });
+ }
+
+ this.reach_cache = path;
+ return path;
+ },
+
+ // needs to return true if there is a /row/ at the same level /after/ a
+ // given row.
+ hasNextSibling: function(row, after) {
+ log("sibling " + row + " after " + after);
+ var path = reachRow(row);
+ if (path.length > 1)
+ {
+ var last = path[1].row + path[1].opt.tv_size;
+ // Has a next sibling if the row is not over the size of the
+ // parent and if the current one is not the last child.
+ return after + 1 < last && path[0].row + path[0].opt.tv_size < last;
+ }
+ else
+ // The top-level option has no sibling.
+ return false;
+ },
+
+ // Does the current row contain any sub-options?
+ isContainer: function(row) {
+ return !this.reachRow(row)[0].opt.isOption;
+ },
+ isContainerEmpty: function(row) {
+ return this.reachRow(row)[0].opt.subOptions.length == 0;
+ },
+ isContainerOpen: function(row) {
+ return this.reachRow(row)[0].opt.tv_opened;
+ },
+
+ // Open or close an option.
+ toggleOpenState: function (row) {
+ var path = this.reachRow(row);
+ var delta = -path[0].opt.tv_size;
+ if (path[0].opt.tv_opened)
+ path[0].opt.tv_close();
+ else
+ path[0].opt.tv_open();
+ delta += path[0].opt.tv_size;
+
+ // Parents are alreay opened, but we need to update the tv_size
+ // counters. Thus we have to invalidate the reach cache.
+ this.reach_cache = null;
+ for (var i = 1; i < path.length; i++)
+ path[i].opt.tv_open();
+
+ this.tree.rowCountChanged(row + 1, delta);
+ },
+
+ // Return the identation level of the option at the line /row/. The
+ // top-level level is 0.
+ getLevel: function(row) {
+ return this.reachRow(row).length - 1;
+ },
+
+ // Obtain the index of a parent row. If there is no parent row,
+ // returns -1.
+ getParentIndex: function(row) {
+ var path = this.reachRow(row);
+ if (path.length > 1)
+ return path[1].row;
+ else
+ return -1;
+ },
+
+
+ // Return the content of each row base on the column name.
+ getCellText: function(row, column) {
+ if (column.id == "opt-name")
+ return this.reachRow(row)[0].opt.name;
+ if (column.id == "dbg-size")
+ return this.reachRow(row)[0].opt.tv_size;
+ return "";
+ },
+
+ // We have no column with images.
+ getCellValue: function(row, column) { },
+
+
+ isSelectable: function(row, column) { return true; },
+
+ // Get the selection out of the tree and give options to the call back
+ // function.
+ selectionChanged: function() {
+ if (this.selCallback == null)
+ return;
+ var opts = [];
+ var start = new Object();
+ var end = new Object();
+ var numRanges = this.tree.view.selection.getRangeCount();
+
+ for (var t = 0; t < numRanges; t++) {
+ this.tree.view.selection.getRangeAt(t,start,end);
+ for (var v = start.value; v <= end.value; v++) {
+ var opt = this.reachRow(v)[0].opt;
+ if (!opt.isLoaded)
+ opt.load();
+ if (opt.isOption)
+ opts.push(opt);
+ }
+ }
+
+ if (opts.lenght != 0)
+ this.selCallback(opts);
+ },
+
+ set rowCount(c) { throw "rowCount is a readonly property"; },
+ get rowCount() { return this.rootOption.tv_size; },
+
+ // refuse drag-n-drop of options.
+ canDrop: function (index, orientation, dataTransfer) { return false; },
+ drop: function (index, orientation, dataTransfer) { },
+
+ // ?
+ getCellProperties: function(row, column, prop) { },
+ getColumnProperties: function(column, prop) { },
+ getRowProperties: function(row, prop) { },
+ getImageSrc: function(row, column) { },
+
+ // No progress columns are used.
+ getProgressMode: function(row, column) { },
+
+ // Do not add options yet.
+ isEditable: function(row, column) { return false; },
+ setCellValue: function(row, column, value) { },
+ setCellText: function(row, column, value) { },
+
+ // ...
+ isSeparator: function(index) { return false; },
+ isSorted: function() { return false; },
+ performAction: function(action) { },
+ performActionOnCell: function(action, row, column) { },
+ performActionOnRow: function(action, row) { }, // ??
+
+ // ??
+ cycleCell: function (row, col) { },
+ cycleHeader: function(col) { },
+
+ selection: null,
+ tree: null,
+ setTree: function(tree) { this.tree = tree; }
+};