summaryrefslogtreecommitdiff
path: root/toolkit/content/widgets/tree.xml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/tree.xml')
-rw-r--r--toolkit/content/widgets/tree.xml1561
1 files changed, 1561 insertions, 0 deletions
diff --git a/toolkit/content/widgets/tree.xml b/toolkit/content/widgets/tree.xml
new file mode 100644
index 0000000000..aa17172575
--- /dev/null
+++ b/toolkit/content/widgets/tree.xml
@@ -0,0 +1,1561 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<!DOCTYPE bindings [
+<!ENTITY % treeDTD SYSTEM "chrome://global/locale/tree.dtd">
+%treeDTD;
+]>
+
+<bindings id="treeBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="tree-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
+ <resources>
+ <stylesheet src="chrome://global/skin/tree.css"/>
+ </resources>
+ <implementation>
+ <method name="_isAccelPressed">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ return aEvent.getModifierState("Accel");
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base" role="xul:tree">
+ <content hidevscroll="true" hidehscroll="true" clickthrough="never">
+ <children includes="treecols"/>
+ <xul:stack class="tree-stack" flex="1">
+ <xul:treerows class="tree-rows" flex="1" xbl:inherits="hidevscroll">
+ <children/>
+ </xul:treerows>
+ <xul:textbox anonid="input" class="tree-input" left="0" top="0" hidden="true"/>
+ </xul:stack>
+ <xul:hbox xbl:inherits="collapsed=hidehscroll">
+ <xul:scrollbar orient="horizontal" flex="1" increment="16" style="position:relative; z-index:2147483647;"/>
+ <xul:scrollcorner xbl:inherits="collapsed=hidevscroll"/>
+ </xul:hbox>
+ </content>
+
+ <implementation implements="nsIDOMXULTreeElement, nsIDOMXULMultiSelectControlElement">
+
+ <!-- ///////////////// nsIDOMXULTreeElement ///////////////// -->
+
+ <property name="columns"
+ onget="return this.treeBoxObject.columns;"/>
+
+ <property name="view"
+ onget="return this.treeBoxObject.view ?
+ this.treeBoxObject.view.QueryInterface(Components.interfaces.nsITreeView) :
+ null;"
+ onset="return this.treeBoxObject.view = val;"/>
+
+ <property name="body"
+ onget="return this.treeBoxObject.treeBody;"/>
+
+ <property name="editable"
+ onget="return this.getAttribute('editable') == 'true';"
+ onset="if (val) this.setAttribute('editable', 'true');
+ else this.removeAttribute('editable'); return val;"/>
+
+ <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
+
+ <!-- ///////////////// nsIDOMXULMultiSelectControlElement ///////////////// -->
+
+ <property name="selType"
+ onget="return this.getAttribute('seltype')"
+ onset="this.setAttribute('seltype', val); return val;"/>
+
+ <property name="currentIndex"
+ onget="return this.view ? this.view.selection.currentIndex: - 1;"
+ onset="if (this.view) return this.view.selection.currentIndex = val; return val;"/>
+
+ <property name="treeBoxObject"
+ onget="return this.boxObject;"
+ readonly="true"/>
+# contentView is obsolete (see bug 202391)
+ <property name="contentView"
+ onget="return this.view; /*.QueryInterface(Components.interfaces.nsITreeContentView)*/"
+ readonly="true"/>
+# builderView is obsolete (see bug 202393)
+ <property name="builderView"
+ onget="return this.view; /*.QueryInterface(Components.interfaces.nsIXULTreeBuilder)*/"
+ readonly="true"/>
+ <field name="pageUpOrDownMovesSelection">
+ !/Mac/.test(navigator.platform)
+ </field>
+ <property name="keepCurrentInView"
+ onget="return (this.getAttribute('keepcurrentinview') == 'true');"
+ onset="if (val) this.setAttribute('keepcurrentinview', 'true');
+ else this.removeAttribute('keepcurrentinview'); return val;"/>
+
+ <property name="enableColumnDrag"
+ onget="return this.hasAttribute('enableColumnDrag');"
+ onset="if (val) this.setAttribute('enableColumnDrag', 'true');
+ else this.removeAttribute('enableColumnDrag'); return val;"/>
+
+ <field name="_inputField">null</field>
+
+ <property name="inputField" readonly="true">
+ <getter><![CDATA[
+ if (!this._inputField)
+ this._inputField = document.getAnonymousElementByAttribute(this, "anonid", "input");
+ return this._inputField;
+ ]]></getter>
+ </property>
+
+ <property name="disableKeyNavigation"
+ onget="return this.hasAttribute('disableKeyNavigation');"
+ onset="if (val) this.setAttribute('disableKeyNavigation', 'true');
+ else this.removeAttribute('disableKeyNavigation'); return val;"/>
+
+ <field name="_editingRow">-1</field>
+ <field name="_editingColumn">null</field>
+
+ <property name="editingRow" readonly="true"
+ onget="return this._editingRow;"/>
+ <property name="editingColumn" readonly="true"
+ onget="return this._editingColumn;"/>
+
+ <property name="_selectDelay"
+ onset="this.setAttribute('_selectDelay', val);"
+ onget="return this.getAttribute('_selectDelay') || 50;"/>
+ <field name="_columnsDirty">true</field>
+ <field name="_lastKeyTime">0</field>
+ <field name="_incrementalString">""</field>
+
+ <field name="_touchY">-1</field>
+
+ <method name="_ensureColumnOrder">
+ <body><![CDATA[
+ if (!this._columnsDirty)
+ return;
+
+ if (this.columns) {
+ // update the ordinal position of each column to assure that it is
+ // an odd number and 2 positions above its next sibling
+ var cols = [];
+ var i;
+ for (var col = this.columns.getFirstColumn(); col; col = col.getNext())
+ cols.push(col.element);
+ for (i = 0; i < cols.length; ++i)
+ cols[i].setAttribute("ordinal", (i*2)+1);
+
+ // update the ordinal positions of splitters to even numbers, so that
+ // they are in between columns
+ var splitters = this.getElementsByTagName("splitter");
+ for (i = 0; i < splitters.length; ++i)
+ splitters[i].setAttribute("ordinal", (i+1)*2);
+ }
+ this._columnsDirty = false;
+ ]]></body>
+ </method>
+
+ <method name="_reorderColumn">
+ <parameter name="aColMove"/>
+ <parameter name="aColBefore"/>
+ <parameter name="aBefore"/>
+ <body><![CDATA[
+ this._ensureColumnOrder();
+
+ var i;
+ var cols = [];
+ var col = this.columns.getColumnFor(aColBefore);
+ if (parseInt(aColBefore.ordinal) < parseInt(aColMove.ordinal)) {
+ if (aBefore)
+ cols.push(aColBefore);
+ for (col = col.getNext(); col.element != aColMove;
+ col = col.getNext())
+ cols.push(col.element);
+
+ aColMove.ordinal = cols[0].ordinal;
+ for (i = 0; i < cols.length; ++i)
+ cols[i].ordinal = parseInt(cols[i].ordinal) + 2;
+ } else if (aColBefore.ordinal != aColMove.ordinal) {
+ if (!aBefore)
+ cols.push(aColBefore);
+ for (col = col.getPrevious(); col.element != aColMove;
+ col = col.getPrevious())
+ cols.push(col.element);
+
+ aColMove.ordinal = cols[0].ordinal;
+ for (i = 0; i < cols.length; ++i)
+ cols[i].ordinal = parseInt(cols[i].ordinal) - 2;
+ }
+ ]]></body>
+ </method>
+
+ <method name="_getColumnAtX">
+ <parameter name="aX"/>
+ <parameter name="aThresh"/>
+ <parameter name="aPos"/>
+ <body><![CDATA[
+ var isRTL = document.defaultView.getComputedStyle(this, "")
+ .direction == "rtl";
+
+ if (aPos)
+ aPos.value = isRTL ? "after" : "before";
+
+ var columns = [];
+ var col = this.columns.getFirstColumn();
+ while (col) {
+ columns.push(col);
+ col = col.getNext();
+ }
+ if (isRTL)
+ columns.reverse();
+ var currentX = this.boxObject.x;
+ var adjustedX = aX + this.treeBoxObject.horizontalPosition;
+ for (var i = 0; i < columns.length; ++i) {
+ col = columns[i];
+ var cw = col.element.boxObject.width;
+ if (cw > 0) {
+ currentX += cw;
+ if (currentX - (cw * aThresh) > adjustedX)
+ return col.element;
+ }
+ }
+
+ if (aPos)
+ aPos.value = isRTL ? "before" : "after";
+ return columns.pop().element;
+ ]]></body>
+ </method>
+
+ <method name="changeOpenState">
+ <parameter name="row"/>
+ <!-- Optional parameter openState == true or false to set.
+ No openState param == toggle -->
+ <parameter name="openState"/>
+ <body><![CDATA[
+ if (row < 0 || !this.view.isContainer(row)) {
+ return false;
+ }
+
+ if (this.view.isContainerOpen(row) != openState) {
+ this.view.toggleOpenState(row);
+ if (row == this.currentIndex) {
+ // Only fire event when current row is expanded or collapsed
+ // because that's all the assistive technology really cares about.
+ var event = document.createEvent('Events');
+ event.initEvent('OpenStateChange', true, true);
+ this.dispatchEvent(event);
+ }
+ return true;
+ }
+ return false;
+ ]]></body>
+ </method>
+
+ <property name="_cellSelType">
+ <getter>
+ <![CDATA[
+ var seltype = this.selType;
+ if (seltype == "cell" || seltype == "text")
+ return seltype;
+ return null;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="_getNextColumn">
+ <parameter name="row"/>
+ <parameter name="left"/>
+ <body><![CDATA[
+ var col = this.view.selection.currentColumn;
+ if (col) {
+ col = left ? col.getPrevious() : col.getNext();
+ }
+ else {
+ col = this.columns.getKeyColumn();
+ }
+ while (col && (col.width == 0 || !col.selectable ||
+ !this.view.isSelectable(row, col)))
+ col = left ? col.getPrevious() : col.getNext();
+ return col;
+ ]]></body>
+ </method>
+
+ <method name="_keyNavigate">
+ <parameter name="event"/>
+ <body><![CDATA[
+ var key = String.fromCharCode(event.charCode).toLowerCase();
+ if (event.timeStamp - this._lastKeyTime > 1000)
+ this._incrementalString = key;
+ else
+ this._incrementalString += key;
+ this._lastKeyTime = event.timeStamp;
+
+ var length = this._incrementalString.length;
+ var incrementalString = this._incrementalString;
+ var charIndex = 1;
+ while (charIndex < length && incrementalString[charIndex] == incrementalString[charIndex - 1])
+ charIndex++;
+ // If all letters in incremental string are same, just try to match the first one
+ if (charIndex == length) {
+ length = 1;
+ incrementalString = incrementalString.substring(0, length);
+ }
+
+ var keyCol = this.columns.getKeyColumn();
+ var rowCount = this.view.rowCount;
+ var start = 1;
+
+ var c = this.currentIndex;
+ if (length > 1) {
+ start = 0;
+ if (c < 0)
+ c = 0;
+ }
+
+ for (var i = 0; i < rowCount; i++) {
+ var l = (i + start + c) % rowCount;
+ var cellText = this.view.getCellText(l, keyCol);
+ cellText = cellText.substring(0, length).toLowerCase();
+ if (cellText == incrementalString)
+ return l;
+ }
+ return -1;
+ ]]></body>
+ </method>
+
+ <method name="startEditing">
+ <parameter name="row"/>
+ <parameter name="column"/>
+ <body>
+ <![CDATA[
+ if (!this.editable)
+ return false;
+ if (row < 0 || row >= this.view.rowCount || !column)
+ return false;
+ if (column.type != Components.interfaces.nsITreeColumn.TYPE_TEXT &&
+ column.type != Components.interfaces.nsITreeColumn.TYPE_PASSWORD)
+ return false;
+ if (column.cycler || !this.view.isEditable(row, column))
+ return false;
+
+ // Beyond this point, we are going to edit the cell.
+ if (this._editingColumn)
+ this.stopEditing();
+
+ var input = this.inputField;
+
+ var box = this.treeBoxObject;
+ box.ensureCellIsVisible(row, column);
+
+ // Get the coordinates of the text inside the cell.
+ var textRect = box.getCoordsForCellItem(row, column, "text");
+
+ // Get the coordinates of the cell itself.
+ var cellRect = box.getCoordsForCellItem(row, column, "cell");
+
+ // Calculate the top offset of the textbox.
+ var style = window.getComputedStyle(input, "");
+ var topadj = parseInt(style.borderTopWidth) + parseInt(style.paddingTop);
+ input.top = textRect.y - topadj;
+
+ // The leftside of the textbox is aligned to the left side of the text
+ // in LTR mode, and left side of the cell in RTL mode.
+ var left, widthdiff;
+ if (style.direction == "rtl") {
+ left = cellRect.x;
+ widthdiff = cellRect.x - textRect.x;
+ } else {
+ left = textRect.x;
+ widthdiff = textRect.x - cellRect.x;
+ }
+
+ input.left = left;
+ input.height = textRect.height + topadj +
+ parseInt(style.borderBottomWidth) +
+ parseInt(style.paddingBottom);
+ input.width = cellRect.width - widthdiff;
+ input.hidden = false;
+
+ input.value = this.view.getCellText(row, column);
+ var selectText = function selectText() {
+ input.select();
+ input.inputField.focus();
+ }
+ setTimeout(selectText, 0);
+
+ this._editingRow = row;
+ this._editingColumn = column;
+ this.setAttribute("editing", "true");
+
+ box.invalidateCell(row, column);
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <method name="stopEditing">
+ <parameter name="accept"/>
+ <body>
+ <![CDATA[
+ if (!this._editingColumn)
+ return;
+
+ var input = this.inputField;
+ var editingRow = this._editingRow;
+ var editingColumn = this._editingColumn;
+ this._editingRow = -1;
+ this._editingColumn = null;
+
+ if (accept) {
+ var value = input.value;
+ this.view.setCellText(editingRow, editingColumn, value);
+ }
+ input.hidden = true;
+ input.value = "";
+ this.removeAttribute("editing");
+ ]]>
+ </body>
+ </method>
+
+ <method name="_moveByOffset">
+ <parameter name="offset"/>
+ <parameter name="edge"/>
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ event.preventDefault();
+
+ if (this.view.rowCount == 0)
+ return;
+
+ if (this._isAccelPressed(event) && this.view.selection.single) {
+ this.treeBoxObject.scrollByLines(offset);
+ return;
+ }
+
+ var c = this.currentIndex + offset;
+ if (offset > 0 ? c > edge : c < edge) {
+ if (this.view.selection.isSelected(edge) && this.view.selection.count <= 1)
+ return;
+ c = edge;
+ }
+
+ var cellSelType = this._cellSelType;
+ if (cellSelType) {
+ var column = this.view.selection.currentColumn;
+ if (!column)
+ return;
+
+ while ((offset > 0 ? c <= edge : c >= edge) && !this.view.isSelectable(c, column))
+ c += offset;
+ if (offset > 0 ? c > edge : c < edge)
+ return;
+ }
+
+ if (!this._isAccelPressed(event))
+ this.view.selection.timedSelect(c, this._selectDelay);
+ else // Ctrl+Up/Down moves the anchor without selecting
+ this.currentIndex = c;
+ this.treeBoxObject.ensureRowIsVisible(c);
+ ]]>
+ </body>
+ </method>
+
+ <method name="_moveByOffsetShift">
+ <parameter name="offset"/>
+ <parameter name="edge"/>
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ event.preventDefault();
+
+ if (this.view.rowCount == 0)
+ return;
+
+ if (this.view.selection.single) {
+ this.treeBoxObject.scrollByLines(offset);
+ return;
+ }
+
+ if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
+ this.view.selection.timedSelect(0, this._selectDelay);
+ return;
+ }
+
+ var c = this.currentIndex;
+ if (c == -1)
+ c = 0;
+
+ if (c == edge) {
+ if (this.view.selection.isSelected(c))
+ return;
+ }
+
+ // Extend the selection from the existing pivot, if any
+ this.view.selection.rangedSelect(-1, c + offset,
+ this._isAccelPressed(event));
+ this.treeBoxObject.ensureRowIsVisible(c + offset);
+
+ ]]>
+ </body>
+ </method>
+
+ <method name="_moveByPage">
+ <parameter name="offset"/>
+ <parameter name="edge"/>
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ event.preventDefault();
+
+ if (this.view.rowCount == 0)
+ return;
+
+ if (this.pageUpOrDownMovesSelection == this._isAccelPressed(event)) {
+ this.treeBoxObject.scrollByPages(offset);
+ return;
+ }
+
+ if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
+ this.view.selection.timedSelect(0, this._selectDelay);
+ return;
+ }
+
+ var c = this.currentIndex;
+ if (c == -1)
+ return;
+
+ if (c == edge && this.view.selection.isSelected(c)) {
+ this.treeBoxObject.ensureRowIsVisible(c);
+ return;
+ }
+ var i = this.treeBoxObject.getFirstVisibleRow();
+ var p = this.treeBoxObject.getPageLength();
+
+ if (offset > 0) {
+ i += p - 1;
+ if (c >= i) {
+ i = c + p;
+ this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
+ }
+ i = i > edge ? edge : i;
+
+ } else if (c <= i) {
+ i = c <= p ? 0 : c - p;
+ this.treeBoxObject.ensureRowIsVisible(i);
+ }
+ this.view.selection.timedSelect(i, this._selectDelay);
+ ]]>
+ </body>
+ </method>
+
+ <method name="_moveByPageShift">
+ <parameter name="offset"/>
+ <parameter name="edge"/>
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ event.preventDefault();
+
+ if (this.view.rowCount == 0)
+ return;
+
+ if (this.view.rowCount == 1 && !this.view.selection.isSelected(0) &&
+ !(this.pageUpOrDownMovesSelection == this._isAccelPressed(event))) {
+ this.view.selection.timedSelect(0, this._selectDelay);
+ return;
+ }
+
+ if (this.view.selection.single)
+ return;
+
+ var c = this.currentIndex;
+ if (c == -1)
+ return;
+ if (c == edge && this.view.selection.isSelected(c)) {
+ this.treeBoxObject.ensureRowIsVisible(edge);
+ return;
+ }
+ var i = this.treeBoxObject.getFirstVisibleRow();
+ var p = this.treeBoxObject.getPageLength();
+
+ if (offset > 0) {
+ i += p - 1;
+ if (c >= i) {
+ i = c + p;
+ this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
+ }
+ // Extend the selection from the existing pivot, if any
+ this.view.selection.rangedSelect(-1, i > edge ? edge : i, this._isAccelPressed(event));
+
+ } else {
+
+ if (c <= i) {
+ i = c <= p ? 0 : c - p;
+ this.treeBoxObject.ensureRowIsVisible(i);
+ }
+ // Extend the selection from the existing pivot, if any
+ this.view.selection.rangedSelect(-1, i, this._isAccelPressed(event));
+ }
+
+ ]]>
+ </body>
+ </method>
+
+ <method name="_moveToEdge">
+ <parameter name="edge"/>
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ event.preventDefault();
+
+ if (this.view.rowCount == 0)
+ return;
+
+ if (this.view.selection.isSelected(edge) && this.view.selection.count == 1) {
+ this.currentIndex = edge;
+ return;
+ }
+
+ // Normal behaviour is to select the first/last row
+ if (!this._isAccelPressed(event))
+ this.view.selection.timedSelect(edge, this._selectDelay);
+
+ // In a multiselect tree Ctrl+Home/End moves the anchor
+ else if (!this.view.selection.single)
+ this.currentIndex = edge;
+
+ this.treeBoxObject.ensureRowIsVisible(edge);
+ ]]>
+ </body>
+ </method>
+
+ <method name="_moveToEdgeShift">
+ <parameter name="edge"/>
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ event.preventDefault();
+
+ if (this.view.rowCount == 0)
+ return;
+
+ if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
+ this.view.selection.timedSelect(0, this._selectDelay);
+ return;
+ }
+
+ if (this.view.selection.single ||
+ (this.view.selection.isSelected(edge)) && this.view.selection.isSelected(this.currentIndex))
+ return;
+
+ // Extend the selection from the existing pivot, if any.
+ // -1 doesn't work here, so using currentIndex instead
+ this.view.selection.rangedSelect(this.currentIndex, edge, this._isAccelPressed(event));
+
+ this.treeBoxObject.ensureRowIsVisible(edge);
+ ]]>
+ </body>
+ </method>
+ <method name="_handleEnter">
+ <parameter name="event"/>
+ <body><![CDATA[
+ if (this._editingColumn) {
+ this.stopEditing(true);
+ this.focus();
+ return true;
+ }
+
+ if (/Mac/.test(navigator.platform)) {
+ // See if we can edit the cell.
+ var row = this.currentIndex;
+ if (this._cellSelType) {
+ var column = this.view.selection.currentColumn;
+ var startedEditing = this.startEditing(row, column);
+ if (startedEditing)
+ return true;
+ }
+ }
+ return this.changeOpenState(this.currentIndex);
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="touchstart">
+ <![CDATA[
+ if (event.touches.length > 1) {
+ // Multiple touch points detected, abort. In particular this aborts
+ // the panning gesture when the user puts a second finger down after
+ // already panning with one finger. Aborting at this point prevents
+ // the pan gesture from being resumed until all fingers are lifted
+ // (as opposed to when the user is back down to one finger).
+ this._touchY = -1;
+ } else {
+ this._touchY = event.touches[0].screenY;
+ }
+ ]]>
+ </handler>
+ <handler event="touchmove">
+ <![CDATA[
+ if (event.touches.length == 1 &&
+ this._touchY >= 0) {
+ var deltaY = this._touchY - event.touches[0].screenY;
+ var lines = Math.trunc(deltaY / this.treeBoxObject.rowHeight);
+ if (Math.abs(lines) > 0) {
+ this.treeBoxObject.scrollByLines(lines);
+ deltaY -= lines * this.treeBoxObject.rowHeight;
+ this._touchY = event.touches[0].screenY + deltaY;
+ }
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ <handler event="touchend">
+ <![CDATA[
+ this._touchY = -1;
+ ]]>
+ </handler>
+ <handler event="MozMousePixelScroll" preventdefault="true"/>
+ <handler event="DOMMouseScroll" preventdefault="true">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ if (event.axis == event.HORIZONTAL_AXIS)
+ return;
+
+ var rows = event.detail;
+ if (rows == UIEvent.SCROLL_PAGE_UP)
+ this.treeBoxObject.scrollByPages(-1);
+ else if (rows == UIEvent.SCROLL_PAGE_DOWN)
+ this.treeBoxObject.scrollByPages(1);
+ else
+ this.treeBoxObject.scrollByLines(rows);
+ ]]>
+ </handler>
+ <handler event="MozSwipeGesture" preventdefault="true">
+ <![CDATA[
+ // Figure out which row to show
+ let targetRow = 0;
+
+ // Only handle swipe gestures up and down
+ switch (event.direction) {
+ case event.DIRECTION_DOWN:
+ targetRow = this.view.rowCount - 1;
+ // Fall through for actual action
+ case event.DIRECTION_UP:
+ this.treeBoxObject.ensureRowIsVisible(targetRow);
+ break;
+ }
+ ]]>
+ </handler>
+ <handler event="select" phase="target"
+ action="if (event.originalTarget == this) this.stopEditing(true);"/>
+ <handler event="focus">
+ <![CDATA[
+ this.treeBoxObject.focused = true;
+ if (this.currentIndex == -1 && this.view.rowCount > 0) {
+ this.currentIndex = this.treeBoxObject.getFirstVisibleRow();
+ }
+ if (this._cellSelType && !this.view.selection.currentColumn) {
+ var col = this._getNextColumn(this.currentIndex, false);
+ this.view.selection.currentColumn = col;
+ }
+ ]]>
+ </handler>
+ <handler event="blur" action="this.treeBoxObject.focused = false;"/>
+ <handler event="blur" phase="capturing"
+ action="if (event.originalTarget == this.inputField.inputField) this.stopEditing(true);"/>
+ <handler event="keydown" keycode="VK_RETURN">
+ if (this._handleEnter(event)) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ </handler>
+#ifndef XP_MACOSX
+ <!-- Use F2 key to enter text editing. -->
+ <handler event="keydown" keycode="VK_F2">
+ <![CDATA[
+ if (!this._cellSelType)
+ return;
+ var row = this.currentIndex;
+ var column = this.view.selection.currentColumn;
+ if (this.startEditing(row, column))
+ event.preventDefault();
+ ]]>
+ </handler>
+#endif // XP_MACOSX
+
+ <handler event="keydown" keycode="VK_ESCAPE">
+ <![CDATA[
+ if (this._editingColumn) {
+ this.stopEditing(false);
+ this.focus();
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_LEFT">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+
+ var row = this.currentIndex;
+ if (row < 0)
+ return;
+
+ var cellSelType = this._cellSelType;
+ var checkContainers = true;
+
+ var currentColumn;
+ if (cellSelType) {
+ currentColumn = this.view.selection.currentColumn;
+ if (currentColumn && !currentColumn.primary)
+ checkContainers = false;
+ }
+
+ if (checkContainers) {
+ if (this.changeOpenState(this.currentIndex, false)) {
+ event.preventDefault();
+ return;
+ }
+ var parentIndex = this.view.getParentIndex(this.currentIndex);
+ if (parentIndex >= 0) {
+ if (cellSelType && !this.view.isSelectable(parentIndex, currentColumn)) {
+ return;
+ }
+ this.view.selection.select(parentIndex);
+ this.treeBoxObject.ensureRowIsVisible(parentIndex);
+ event.preventDefault();
+ return;
+ }
+ }
+
+ if (cellSelType) {
+ var col = this._getNextColumn(row, true);
+ if (col) {
+ this.view.selection.currentColumn = col;
+ this.treeBoxObject.ensureCellIsVisible(row, col);
+ event.preventDefault();
+ }
+ }
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_RIGHT">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+
+ var row = this.currentIndex;
+ if (row < 0)
+ return;
+
+ var cellSelType = this._cellSelType;
+ var checkContainers = true;
+
+ var currentColumn;
+ if (cellSelType) {
+ currentColumn = this.view.selection.currentColumn;
+ if (currentColumn && !currentColumn.primary)
+ checkContainers = false;
+ }
+
+ if (checkContainers) {
+ if (this.changeOpenState(row, true)) {
+ event.preventDefault();
+ return;
+ }
+ var c = row + 1;
+ var view = this.view;
+ if (c < view.rowCount &&
+ view.getParentIndex(c) == row) {
+ // If already opened, select the first child.
+ // The getParentIndex test above ensures that the children
+ // are already populated and ready.
+ if (cellSelType && !this.view.isSelectable(c, currentColumn)) {
+ let col = this._getNextColumn(c, false);
+ if (col) {
+ this.view.selection.currentColumn = col;
+ }
+ }
+ this.view.selection.timedSelect(c, this._selectDelay);
+ this.treeBoxObject.ensureRowIsVisible(c);
+ event.preventDefault();
+ return;
+ }
+ }
+
+ if (cellSelType) {
+ let col = this._getNextColumn(row, false);
+ if (col) {
+ this.view.selection.currentColumn = col;
+ this.treeBoxObject.ensureCellIsVisible(row, col);
+ event.preventDefault();
+ }
+ }
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_UP" modifiers="accel any">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByOffset(-1, 0, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_DOWN" modifiers="accel any">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByOffset(1, this.view.rowCount - 1, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_UP" modifiers="accel any, shift">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByOffsetShift(-1, 0, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_DOWN" modifiers="accel any, shift">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByOffsetShift(1, this.view.rowCount - 1, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_PAGE_UP" modifiers="accel any">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByPage(-1, 0, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_PAGE_DOWN" modifiers="accel any">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByPage(1, this.view.rowCount - 1, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_PAGE_UP" modifiers="accel any, shift">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByPageShift(-1, 0, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_PAGE_DOWN" modifiers="accel any, shift">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveByPageShift(1, this.view.rowCount - 1, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_HOME" modifiers="accel any">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveToEdge(0, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_END" modifiers="accel any">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveToEdge(this.view.rowCount - 1, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_HOME" modifiers="accel any, shift">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveToEdgeShift(0, event);
+ ]]>
+ </handler>
+ <handler event="keydown" keycode="VK_END" modifiers="accel any, shift">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+ _moveToEdgeShift(this.view.rowCount - 1, event);
+ ]]>
+ </handler>
+ <handler event="keypress">
+ <![CDATA[
+ if (this._editingColumn)
+ return;
+
+ if (event.charCode == ' '.charCodeAt(0)) {
+ var c = this.currentIndex;
+ if (!this.view.selection.isSelected(c) ||
+ (!this.view.selection.single && this._isAccelPressed(event))) {
+ this.view.selection.toggleSelect(c);
+ event.preventDefault();
+ }
+ }
+ else if (!this.disableKeyNavigation && event.charCode > 0 &&
+ !event.altKey && !this._isAccelPressed(event) &&
+ !event.metaKey && !event.ctrlKey) {
+ var l = this._keyNavigate(event);
+ if (l >= 0) {
+ this.view.selection.timedSelect(l, this._selectDelay);
+ this.treeBoxObject.ensureRowIsVisible(l);
+ }
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="treecols" role="xul:treecolumns">
+ <resources>
+ <stylesheet src="chrome://global/skin/tree.css"/>
+ </resources>
+ <content orient="horizontal">
+ <xul:hbox class="tree-scrollable-columns" flex="1">
+ <children includes="treecol|splitter"/>
+ </xul:hbox>
+ <xul:treecolpicker class="treecol-image" fixed="true" xbl:inherits="tooltiptext=pickertooltiptext"/>
+ </content>
+ <implementation>
+ <constructor><![CDATA[
+ // Set resizeafter="farthest" on the splitters if nothing else has been
+ // specified.
+ Array.forEach(this.getElementsByTagName("splitter"), function (splitter) {
+ if (!splitter.hasAttribute("resizeafter"))
+ splitter.setAttribute("resizeafter", "farthest");
+ });
+ ]]></constructor>
+ </implementation>
+ </binding>
+
+ <binding id="treerows" extends="chrome://global/content/bindings/tree.xml#tree-base">
+ <content>
+ <xul:hbox flex="1" class="tree-bodybox">
+ <children/>
+ </xul:hbox>
+ <xul:scrollbar height="0" minwidth="0" minheight="0" orient="vertical" xbl:inherits="collapsed=hidevscroll" style="position:relative; z-index:2147483647;"/>
+ </content>
+ <handlers>
+ <handler event="underflow">
+ <![CDATA[
+ // Scrollport event orientation
+ // 0: vertical
+ // 1: horizontal
+ // 2: both (not used)
+ var tree = document.getBindingParent(this);
+ if (event.detail == 1)
+ tree.setAttribute("hidehscroll", "true");
+ else if (event.detail == 0)
+ tree.setAttribute("hidevscroll", "true");
+ event.stopPropagation();
+ ]]>
+ </handler>
+ <handler event="overflow">
+ <![CDATA[
+ var tree = document.getBindingParent(this);
+ if (event.detail == 1)
+ tree.removeAttribute("hidehscroll");
+ else if (event.detail == 0)
+ tree.removeAttribute("hidevscroll");
+ event.stopPropagation();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="treebody" extends="chrome://global/content/bindings/tree.xml#tree-base">
+ <implementation>
+ <constructor>
+ if ("_ensureColumnOrder" in this.parentNode)
+ this.parentNode._ensureColumnOrder();
+ </constructor>
+
+ <field name="_lastSelectedRow">
+ -1
+ </field>
+ </implementation>
+ <handlers>
+ <!-- If there is no modifier key, we select on mousedown, not
+ click, so that drags work correctly. -->
+ <handler event="mousedown" clickcount="1">
+ <![CDATA[
+ if (this.parentNode.disabled)
+ return;
+ if (((!this._isAccelPressed(event) ||
+ !this.parentNode.pageUpOrDownMovesSelection) &&
+ !event.shiftKey && !event.metaKey) ||
+ this.parentNode.view.selection.single) {
+ var b = this.parentNode.treeBoxObject;
+ var cell = b.getCellAt(event.clientX, event.clientY);
+ var view = this.parentNode.view;
+
+ // save off the last selected row
+ this._lastSelectedRow = cell.row;
+
+ if (cell.row == -1)
+ return;
+
+ if (cell.childElt == "twisty")
+ return;
+
+ if (cell.col && event.button == 0) {
+ if (cell.col.cycler) {
+ view.cycleCell(cell.row, cell.col);
+ return;
+ } else if (cell.col.type == Components.interfaces.nsITreeColumn.TYPE_CHECKBOX) {
+ if (this.parentNode.editable && cell.col.editable &&
+ view.isEditable(cell.row, cell.col)) {
+ var value = view.getCellValue(cell.row, cell.col);
+ value = value == "true" ? "false" : "true";
+ view.setCellValue(cell.row, cell.col, value);
+ return;
+ }
+ }
+ }
+
+ var cellSelType = this.parentNode._cellSelType;
+ if (cellSelType == "text" && cell.childElt != "text" && cell.childElt != "image")
+ return;
+
+ if (cellSelType) {
+ if (!cell.col.selectable ||
+ !view.isSelectable(cell.row, cell.col)) {
+ return;
+ }
+ }
+
+ if (!view.selection.isSelected(cell.row)) {
+ view.selection.select(cell.row);
+ b.ensureRowIsVisible(cell.row);
+ }
+
+ if (cellSelType) {
+ view.selection.currentColumn = cell.col;
+ }
+ }
+ ]]>
+ </handler>
+
+ <!-- On a click (up+down on the same item), deselect everything
+ except this item. -->
+ <handler event="click" button="0" clickcount="1">
+ <![CDATA[
+ if (this.parentNode.disabled)
+ return;
+ var b = this.parentNode.treeBoxObject;
+ var cell = b.getCellAt(event.clientX, event.clientY);
+ var view = this.parentNode.view;
+
+ if (cell.row == -1)
+ return;
+
+ if (cell.childElt == "twisty") {
+ if (view.selection.currentIndex >= 0 &&
+ view.isContainerOpen(cell.row)) {
+ var parentIndex = view.getParentIndex(view.selection.currentIndex);
+ while (parentIndex >= 0 && parentIndex != cell.row)
+ parentIndex = view.getParentIndex(parentIndex);
+ if (parentIndex == cell.row) {
+ var parentSelectable = true;
+ if (this.parentNode._cellSelType) {
+ var currentColumn = view.selection.currentColumn;
+ if (!view.isSelectable(parentIndex, currentColumn))
+ parentSelectable = false;
+ }
+ if (parentSelectable)
+ view.selection.select(parentIndex);
+ }
+ }
+ this.parentNode.changeOpenState(cell.row);
+ return;
+ }
+
+ if (! view.selection.single) {
+ var augment = this._isAccelPressed(event);
+ if (event.shiftKey) {
+ view.selection.rangedSelect(-1, cell.row, augment);
+ b.ensureRowIsVisible(cell.row);
+ return;
+ }
+ if (augment) {
+ view.selection.toggleSelect(cell.row);
+ b.ensureRowIsVisible(cell.row);
+ view.selection.currentIndex = cell.row;
+ return;
+ }
+ }
+
+ /* We want to deselect all the selected items except what was
+ clicked, UNLESS it was a right-click. We have to do this
+ in click rather than mousedown so that you can drag a
+ selected group of items */
+
+ if (!cell.col) return;
+
+ // if the last row has changed in between the time we
+ // mousedown and the time we click, don't fire the select handler.
+ // see bug #92366
+ if (!cell.col.cycler && this._lastSelectedRow == cell.row &&
+ cell.col.type != Components.interfaces.nsITreeColumn.TYPE_CHECKBOX) {
+
+ var cellSelType = this.parentNode._cellSelType;
+ if (cellSelType == "text" && cell.childElt != "text" && cell.childElt != "image")
+ return;
+
+ if (cellSelType) {
+ if (!cell.col.selectable ||
+ !view.isSelectable(cell.row, cell.col)) {
+ return;
+ }
+ }
+
+ view.selection.select(cell.row);
+ b.ensureRowIsVisible(cell.row);
+
+ if (cellSelType) {
+ view.selection.currentColumn = cell.col;
+ }
+ }
+ ]]>
+ </handler>
+
+ <!-- double-click -->
+ <handler event="click" clickcount="2">
+ <![CDATA[
+ if (this.parentNode.disabled)
+ return;
+ var tbo = this.parentNode.treeBoxObject;
+ var view = this.parentNode.view;
+ var row = view.selection.currentIndex;
+
+ if (row == -1)
+ return;
+
+ var cell = tbo.getCellAt(event.clientX, event.clientY);
+
+ if (cell.childElt != "twisty") {
+ view.selection.currentColumn = cell.col;
+ this.parentNode.startEditing(row, cell.col);
+ }
+
+ if (this.parentNode._editingColumn || !view.isContainer(row))
+ return;
+
+ // Cyclers and twisties respond to single clicks, not double clicks
+ if (cell.col && !cell.col.cycler && cell.childElt != "twisty")
+ this.parentNode.changeOpenState(row);
+ ]]>
+ </handler>
+
+ </handlers>
+ </binding>
+
+ <binding id="treecol-base" role="xul:treecolumnitem"
+ extends="chrome://global/content/bindings/tree.xml#tree-base">
+ <implementation>
+ <constructor>
+ this.parentNode.parentNode._columnsDirty = true;
+ </constructor>
+
+ <property name="ordinal">
+ <getter><![CDATA[
+ var val = this.getAttribute("ordinal");
+ if (val == "")
+ return "1";
+
+ return "" + (val == "0" ? 0 : parseInt(val));
+ ]]></getter>
+ <setter><![CDATA[
+ this.setAttribute("ordinal", val);
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="_previousVisibleColumn">
+ <getter><![CDATA[
+ var sib = this.boxObject.previousSibling;
+ while (sib) {
+ if (sib.localName == "treecol" && sib.boxObject.width > 0 && sib.parentNode == this.parentNode)
+ return sib;
+ sib = sib.boxObject.previousSibling;
+ }
+ return null;
+ ]]></getter>
+ </property>
+
+ <method name="_onDragMouseMove">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var col = document.treecolDragging;
+ if (!col) return;
+
+ // determine if we have moved the mouse far enough
+ // to initiate a drag
+ if (col.mDragGesturing) {
+ if (Math.abs(aEvent.clientX - col.mStartDragX) < 5 &&
+ Math.abs(aEvent.clientY - col.mStartDragY) < 5) {
+ return;
+ }
+ col.mDragGesturing = false;
+ col.setAttribute("dragging", "true");
+ window.addEventListener("click", col._onDragMouseClick, true);
+ }
+
+ var pos = {};
+ var targetCol = col.parentNode.parentNode._getColumnAtX(aEvent.clientX, 0.5, pos);
+
+ // bail if we haven't mousemoved to a different column
+ if (col.mTargetCol == targetCol && col.mTargetDir == pos.value)
+ return;
+
+ var tree = col.parentNode.parentNode;
+ var sib;
+ var column;
+ if (col.mTargetCol) {
+ // remove previous insertbefore/after attributes
+ col.mTargetCol.removeAttribute("insertbefore");
+ col.mTargetCol.removeAttribute("insertafter");
+ column = tree.columns.getColumnFor(col.mTargetCol);
+ tree.treeBoxObject.invalidateColumn(column);
+ sib = col.mTargetCol._previousVisibleColumn;
+ if (sib) {
+ sib.removeAttribute("insertafter");
+ column = tree.columns.getColumnFor(sib);
+ tree.treeBoxObject.invalidateColumn(column);
+ }
+ col.mTargetCol = null;
+ col.mTargetDir = null;
+ }
+
+ if (targetCol) {
+ // set insertbefore/after attributes
+ if (pos.value == "after") {
+ targetCol.setAttribute("insertafter", "true");
+ } else {
+ targetCol.setAttribute("insertbefore", "true");
+ sib = targetCol._previousVisibleColumn;
+ if (sib) {
+ sib.setAttribute("insertafter", "true");
+ column = tree.columns.getColumnFor(sib);
+ tree.treeBoxObject.invalidateColumn(column);
+ }
+ }
+ column = tree.columns.getColumnFor(targetCol);
+ tree.treeBoxObject.invalidateColumn(column);
+ col.mTargetCol = targetCol;
+ col.mTargetDir = pos.value;
+ }
+ ]]></body>
+ </method>
+
+ <method name="_onDragMouseUp">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var col = document.treecolDragging;
+ if (!col) return;
+
+ if (!col.mDragGesturing) {
+ if (col.mTargetCol) {
+ // remove insertbefore/after attributes
+ var before = col.mTargetCol.hasAttribute("insertbefore");
+ col.mTargetCol.removeAttribute(before ? "insertbefore" : "insertafter");
+
+ var sib = col.mTargetCol._previousVisibleColumn;
+ if (before && sib) {
+ sib.removeAttribute("insertafter");
+ }
+
+ // Move the column only if it will result in a different column
+ // ordering
+ var move = true;
+
+ // If this is a before move and the previous visible column is
+ // the same as the column we're moving, don't move
+ if (before && col == sib) {
+ move = false;
+ }
+ else if (!before && col == col.mTargetCol) {
+ // If this is an after move and the column we're moving is
+ // the same as the target column, don't move.
+ move = false;
+ }
+
+ if (move) {
+ col.parentNode.parentNode._reorderColumn(col, col.mTargetCol, before);
+ }
+
+ // repaint to remove lines
+ col.parentNode.parentNode.treeBoxObject.invalidate();
+
+ col.mTargetCol = null;
+ }
+ } else
+ col.mDragGesturing = false;
+
+ document.treecolDragging = null;
+ col.removeAttribute("dragging");
+
+ window.removeEventListener("mousemove", col._onDragMouseMove, true);
+ window.removeEventListener("mouseup", col._onDragMouseUp, true);
+ // we have to wait for the click event to fire before removing
+ // cancelling handler
+ var clickHandler = function(handler) {
+ window.removeEventListener("click", handler, true);
+ };
+ window.setTimeout(clickHandler, 0, col._onDragMouseClick);
+ ]]></body>
+ </method>
+
+ <method name="_onDragMouseClick">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ // prevent click event from firing after column drag and drop
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="mousedown" button="0"><![CDATA[
+ if (this.parentNode.parentNode.enableColumnDrag) {
+ var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var cols = this.parentNode.getElementsByTagNameNS(xulns, "treecol");
+
+ // only start column drag operation if there are at least 2 visible columns
+ var visible = 0;
+ for (var i = 0; i < cols.length; ++i)
+ if (cols[i].boxObject.width > 0) ++visible;
+
+ if (visible > 1) {
+ window.addEventListener("mousemove", this._onDragMouseMove, true);
+ window.addEventListener("mouseup", this._onDragMouseUp, true);
+ document.treecolDragging = this;
+ this.mDragGesturing = true;
+ this.mStartDragX = event.clientX;
+ this.mStartDragY = event.clientY;
+ }
+ }
+ ]]></handler>
+ <handler event="click" button="0" phase="target">
+ <![CDATA[
+ if (event.target != event.originalTarget)
+ return;
+
+ // On Windows multiple clicking on tree columns only cycles one time
+ // every 2 clicks.
+ if (/Win/.test(navigator.platform) && event.detail % 2 == 0)
+ return;
+
+ var tree = this.parentNode.parentNode;
+ if (tree.columns) {
+ tree.view.cycleHeader(tree.columns.getColumnFor(this));
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="treecol" extends="chrome://global/content/bindings/tree.xml#treecol-base">
+ <content>
+ <xul:label class="treecol-text" xbl:inherits="crop,value=label" flex="1" crop="right"/>
+ <xul:image class="treecol-sortdirection" xbl:inherits="sortDirection,hidden=hideheader"/>
+ </content>
+ </binding>
+
+ <binding id="treecol-image" extends="chrome://global/content/bindings/tree.xml#treecol-base">
+ <content>
+ <xul:image class="treecol-icon" xbl:inherits="src"/>
+ </content>
+ </binding>
+
+ <binding id="columnpicker" display="xul:button" role="xul:button"
+ extends="chrome://global/content/bindings/tree.xml#tree-base">
+ <content>
+ <xul:image class="tree-columnpicker-icon"/>
+ <xul:menupopup anonid="popup">
+ <xul:menuseparator anonid="menuseparator"/>
+ <xul:menuitem anonid="menuitem" label="&restoreColumnOrder.label;"/>
+ </xul:menupopup>
+ </content>
+
+ <implementation>
+ <method name="buildPopup">
+ <parameter name="aPopup"/>
+ <body>
+ <![CDATA[
+ // We no longer cache the picker content, remove the old content.
+ while (aPopup.childNodes.length > 2)
+ aPopup.removeChild(aPopup.firstChild);
+
+ var refChild = aPopup.firstChild;
+
+ var tree = this.parentNode.parentNode;
+ for (var currCol = tree.columns.getFirstColumn(); currCol;
+ currCol = currCol.getNext()) {
+ // Construct an entry for each column in the row, unless
+ // it is not being shown.
+ var currElement = currCol.element;
+ if (!currElement.hasAttribute("ignoreincolumnpicker")) {
+ var popupChild = document.createElement("menuitem");
+ popupChild.setAttribute("type", "checkbox");
+ var columnName = currElement.getAttribute("display") ||
+ currElement.getAttribute("label");
+ popupChild.setAttribute("label", columnName);
+ popupChild.setAttribute("colindex", currCol.index);
+ if (currElement.getAttribute("hidden") != "true")
+ popupChild.setAttribute("checked", "true");
+ if (currCol.primary)
+ popupChild.setAttribute("disabled", "true");
+ aPopup.insertBefore(popupChild, refChild);
+ }
+ }
+
+ var hidden = !tree.enableColumnDrag;
+ const anonids = ["menuseparator", "menuitem"];
+ for (var i = 0; i < anonids.length; i++) {
+ var element = document.getAnonymousElementByAttribute(this, "anonid", anonids[i]);
+ element.hidden = hidden;
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="command">
+ <![CDATA[
+ if (event.originalTarget == this) {
+ var popup = document.getAnonymousElementByAttribute(this, "anonid", "popup");
+ this.buildPopup(popup);
+ popup.showPopup(this, -1, -1, "popup", "bottomright", "topright");
+ }
+ else {
+ var tree = this.parentNode.parentNode;
+ tree.stopEditing(true);
+ var menuitem = document.getAnonymousElementByAttribute(this, "anonid", "menuitem");
+ if (event.originalTarget == menuitem) {
+ tree.columns.restoreNaturalOrder();
+ tree._ensureColumnOrder();
+ }
+ else {
+ var colindex = event.originalTarget.getAttribute("colindex");
+ var column = tree.columns[colindex];
+ if (column) {
+ var element = column.element;
+ if (element.getAttribute("hidden") == "true")
+ element.setAttribute("hidden", "false");
+ else
+ element.setAttribute("hidden", "true");
+ }
+ }
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+</bindings>