diff options
Diffstat (limited to 'nixos/gui/chrome/content/optionView.js')
-rw-r--r-- | nixos/gui/chrome/content/optionView.js | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/nixos/gui/chrome/content/optionView.js b/nixos/gui/chrome/content/optionView.js new file mode 100644 index 000000000000..0d093740fe25 --- /dev/null +++ b/nixos/gui/chrome/content/optionView.js @@ -0,0 +1,242 @@ +// 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); + + // FIXME: no need to make things slowing down, because our current + // callback do not handle multiple option display. + if (!opts.empty) + break; + } + // FIXME: no need to make things slowing down, because our current + // callback do not handle multiple option display. + if (!opts.empty) + break; + } + + if (!opts.empty) + 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; } +}; |