mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
0f167a14a3
--HG-- rename : layout/xul/nsIPopupBoxObject.idl => dom/webidl/PopupBoxObject.webidl rename : layout/xul/tree/nsITreeBoxObject.idl => dom/webidl/TreeBoxObject.webidl rename : layout/xul/nsBoxObject.cpp => layout/xul/BoxObject.cpp rename : layout/xul/nsBoxObject.h => layout/xul/BoxObject.h rename : layout/xul/nsListBoxObject.cpp => layout/xul/ListBoxObject.cpp rename : layout/xul/nsMenuBoxObject.cpp => layout/xul/MenuBoxObject.cpp rename : layout/xul/nsPopupBoxObject.cpp => layout/xul/PopupBoxObject.cpp
1535 lines
52 KiB
XML
1535 lines
52 KiB
XML
<?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[
|
|
# Workaround until bug 302174 is fixed
|
|
#ifdef XP_MACOSX
|
|
return aEvent.metaKey;
|
|
#else
|
|
return aEvent.ctrlKey;
|
|
#endif
|
|
]]></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">
|
|
#ifdef XP_MACOSX
|
|
false
|
|
#else
|
|
true
|
|
#endif
|
|
</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>
|
|
|
|
<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.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 + cellRect.width - textRect.x - textRect.width;
|
|
} 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");
|
|
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;
|
|
}
|
|
|
|
#ifdef XP_MACOSX
|
|
// 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;
|
|
}
|
|
#endif
|
|
return this.changeOpenState(this.currentIndex);
|
|
]]></body>
|
|
</method>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<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;
|
|
}
|
|
else {
|
|
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;
|
|
}
|
|
else {
|
|
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)) {
|
|
var 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) {
|
|
var 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) {
|
|
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")
|
|
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");
|
|
return "" + (val == "" ? 1 : (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;
|
|
} else {
|
|
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;
|
|
|
|
#ifdef XP_WIN
|
|
// On Windows multiple clicking on tree columns only cycles one time
|
|
// every 2 clicks.
|
|
if (event.detail % 2 == 0)
|
|
return;
|
|
#endif
|
|
|
|
var tree = this.parentNode.parentNode;
|
|
var column = tree.columns.getColumnFor(this);
|
|
tree.view.cycleHeader(column);
|
|
]]>
|
|
</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>
|