gecko-dev/toolkit/content/widgets/listbox.xml

915 lines
30 KiB
XML

<?xml version="1.0"?>
<bindings id="listboxBindings"
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="listbox-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
<resources>
<stylesheet src="chrome://global/skin/listbox.css"/>
</resources>
</binding>
<binding id="listbox"
extends="chrome://global/content/bindings/listbox.xml#listbox-base">
<content>
<children includes="listcols">
<xul:listcols>
<xul:listcol flex="1"/>
</xul:listcols>
</children>
<xul:listrows>
<children includes="listhead"/>
<xul:listboxbody xbl:inherits="rows,size,minheight">
<children includes="listitem"/>
</xul:listboxbody>
</xul:listrows>
</content>
<implementation implements="nsIDOMXULMultiSelectControlElement, nsIAccessibleProvider">
<field name="_suppressOnSelect">false</field>
<field name="_selectionStart">null</field>
<field name="_currentItem">null</field>
<field name="_selectTimeout">null</field>
<field name="_lastKeyTime">0</field>
<field name="_incrementalString">""</field>
<constructor>
<![CDATA[
var els = this.getElementsByAttribute("selected", "true");
for (var i = 0; i < els.length; ++i)
this.selectedItems.push(els[i]);
]]>
</constructor>
<!-- ///////////////// nsIAccessibleProvider ///////////////// -->
<property name="accessible">
<getter>
<![CDATA[
var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
return accService.createXULListboxAccessible(this);
]]>
</getter>
</property>
<!-- ///////////////// public listbox members ///////////////// -->
<property name="listBoxObject"
onget="return this.boxObject.QueryInterface(Components.interfaces.nsIListBoxObject);"
readonly="true"/>
<property name="disableKeyNavigation"
onget="return this.hasAttribute('disableKeyNavigation');"
onset="if (val) this.setAttribute('disableKeyNavigation', 'true');
else this.removeAttribute('disableKeyNavigation'); return val;"/>
<property name="_selectDelay"
onset="this.setAttribute('_selectDelay', val);"
onget="return this.getAttribute('_selectDelay') || 50;"/>
<method name="timedSelect">
<parameter name="item"/>
<parameter name="timeout"/>
<body>
<![CDATA[
var suppress = this._suppressOnSelect;
if (timeout != -1)
this._suppressOnSelect = true;
this.selectItem(item);
this._suppressOnSelect = suppress;
if (timeout != -1) {
if (this._selectTimeout)
window.clearTimeout(this._selectTimeout);
this._selectTimeout = window.setTimeout(this._selectTimeoutHandler, timeout, this);
}
]]>
</body>
</method>
<!-- ///////////////// private listbox members ///////////////// -->
<method name="_fireOnSelect">
<body>
<![CDATA[
if (!this._suppressOnSelect && this.getAttribute("suppressonselect") != "true") {
var event = document.createEvent("Events");
event.initEvent("select", false, true);
this.dispatchEvent(event);
}
]]>
</body>
</method>
<method name="_selectTimeoutHandler">
<parameter name="me"/>
<body>
<![CDATA[
me._fireOnSelect();
me._selectTimeout = null;
]]>
</body>
</method>
<!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
<property name="selType"
onget="return this.getAttribute('seltype')"
onset="this.setAttribute('seltype', val); return val;"/>
<property name="selectedIndex">
<getter><![CDATA[
return this.selectedItems.length > 0 ? this.getIndexOfItem(this.selectedItems[0]) : -1;
]]></getter>
<setter><![CDATA[
if (val >= 0)
this.selectItem(this.getItemAtIndex(val));
else
this.clearSelection();
]]></setter>
</property>
<field name="selectedItems">[]</field>
<property name="selectedItem">
<getter><![CDATA[
return this.selectedItems.length > 0 ? this.selectedItems[0] : null;
]]></getter>
<setter><![CDATA[
this.selectItem(val);
]]></setter>
</property>
<property name="value">
<getter>
<![CDATA[
if (this.selectedItems.length > 0)
return this.selectedItem.value;
else
return null;
]]>
</getter>
<setter>
<![CDATA[
var kids = this.getElementsByAttribute("value", val);
if (kids && kids.item(0))
this.selectItem(kids[0]);
return val;
]]>
</setter>
</property>
<method name="appendItem">
<parameter name="label"/>
<parameter name="value"/>
<body>
<![CDATA[
var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var item = document.createElementNS(XULNS, "listitem");
item.setAttribute("label", label);
item.setAttribute("value", value);
this.appendChild(item);
return item;
]]>
</body>
</method>
<method name="insertItemAt">
<parameter name="index"/>
<parameter name="label"/>
<parameter name="value"/>
<body>
<![CDATA[
var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var item = document.createElementNS(XULNS, "listitem");
item.setAttribute("label", label);
item.setAttribute("value", value);
var before = this.getItemAtIndex(index);
if (before)
this.insertBefore(item, before);
else
this.appendChild(item);
return item;
]]>
</body>
</method>
<method name="removeItemAt">
<parameter name="index"/>
<body>
<![CDATA[
var remove = this.getItemAtIndex(index);
if (remove)
this.removeChild(remove);
return remove;
]]>
</body>
</method>
<!-- ///////////////// nsIDOMXULSelectMultipleControlElement ///////////////// -->
<property name="currentItem" onget="return this._currentItem;">
<setter>
<![CDATA[
if (this._currentItem)
this._currentItem.current = false;
this._currentItem = val;
if (val) {
val.current = true;
var event = document.createEvent("Events");
event.initEvent("DOMMenuItemActive", true, true);
val.dispatchEvent(event);
}
return val;
]]>
</setter>
</property>
<property name="currentIndex">
<getter><![CDATA[
return this.currentItem ? this.getIndexOfItem(this.currentItem) : -1;
]]></getter>
<setter><![CDATA[
if (val >= 0)
this.currentItem = this.getItemAtIndex(val);
else
this.currentItem = null;
]]></setter>
</property>
<property name="selectedCount" onget="return this.selectedItems.length;"/>
<method name="getSelectedItem">
<parameter name="index"/>
<body>
<![CDATA[
return index < this.selectedItems.length ? this.selectedItems[index] : null;
]]>
</body>
</method>
<method name="addItemToSelection">
<parameter name="item"/>
<body>
<![CDATA[
if (this.selType != "multiple" && this.selectedCount)
return;
if (item.selected)
return;
this.selectedItems.push(item);
item.selected = true;
this._fireOnSelect();
]]>
</body>
</method>
<method name="removeItemFromSelection">
<parameter name="item"/>
<body>
<![CDATA[
if (!item.selected)
return;
for (var i = 0; i < this.selectedItems.length; ++i) {
if (this.selectedItems[i] == item) {
this.selectedItems.splice(i, 1);
item.selected = false;
break;
}
}
this._fireOnSelect();
]]>
</body>
</method>
<method name="toggleItemSelection">
<parameter name="item"/>
<body>
<![CDATA[
if (item.selected)
this.removeItemFromSelection(item);
else
this.addItemToSelection(item);
]]>
</body>
</method>
<method name="selectItem">
<parameter name="item"/>
<body>
<![CDATA[
if (!item)
return;
if (this.selectedItems.length == 1 && this.selectedItems[0] == item)
return;
this._selectionStart = null;
var suppress = this._suppressOnSelect;
this._suppressOnSelect = true;
this.clearSelection();
this.addItemToSelection(item);
this.currentItem = item;
this._suppressOnSelect = suppress;
this._fireOnSelect();
]]>
</body>
</method>
<method name="selectItemRange">
<parameter name="startItem"/>
<parameter name="endItem"/>
<body>
<![CDATA[
if (this.selType != "multiple")
return;
if (!startItem)
startItem = this._selectionStart ? this._selectionStart : this.currentItem;
if (!startItem)
startItem = endItem;
var suppressSelect = this._suppressOnSelect;
this._suppressOnSelect = true;
this._selectionStart = startItem;
var currentItem;
var startIndex = this.getIndexOfItem(startItem);
var endIndex = this.getIndexOfItem(endItem);
if (endIndex < startIndex) {
currentItem = endItem;
endItem = startItem;
startItem = currentItem;
} else {
currentItem = startItem;
}
while (currentItem) {
if (currentItem.localName == "listitem")
this.addItemToSelection(currentItem);
if (currentItem == endItem) {
currentItem = this.getNextItem(currentItem, 1);
break;
}
currentItem = this.getNextItem(currentItem, 1);
}
// Clear around new selection
// Don't use clearSelection() because it causes a lot of noise
// with respect to selection removed notifications used by the
// accessibility API support.
for (; currentItem; currentItem = this.getNextItem(currentItem, 1))
this.removeItemFromSelection(currentItem);
for (currentItem = this.getItemAtIndex(0); currentItem != startItem;
currentItem = this.getNextItem(currentItem, 1))
this.removeItemFromSelection(currentItem);
this._suppressOnSelect = suppressSelect;
this._fireOnSelect();
]]>
</body>
</method>
<method name="selectAll">
<body>
<![CDATA[
var suppress = this._suppressOnSelect;
this._suppressOnSelect = true;
var item = this.getItemAtIndex(0);
while (item) {
this.addItemToSelection(item);
item = this.getNextItem(item, 1);
}
this._suppressOnSelect = suppress;
this._fireOnSelect();
]]>
</body>
</method>
<method name="invertSelection">
<body>
<![CDATA[
var suppress = this._suppressOnSelect;
this._suppressOnSelect = true;
var item = this.getItemAtIndex(0);
while (item) {
if (item.selected)
this.removeItemFromSelection(item);
else
this.addItemToSelection(item);
item = this.getNextItem(item, 1);
}
this._suppressOnSelect = suppress;
this._fireOnSelect();
]]>
</body>
</method>
<method name="clearSelection">
<body>
<![CDATA[
if (this.selectedItems)
{
for (var i = this.selectedItems.length-1; i >= 0; --i)
this.selectedItems[i].selected = false;
this.selectedItems.splice(0, this.selectedItems.length);
}
this._selectionStart = null;
this._fireOnSelect();
]]>
</body>
</method>
<!-- ///////////////// nsIListBoxObject ///////////////// -->
<method name="getNextItem">
<parameter name="startItem"/>
<parameter name="delta"/>
<body><![CDATA[
while (startItem) {
startItem = startItem.nextSibling;
if (startItem && startItem.localName == "listitem") {
--delta;
if (delta == 0)
return startItem;
}
}
return null;
]]></body>
</method>
<method name="getPreviousItem">
<parameter name="startItem"/>
<parameter name="delta"/>
<body><![CDATA[
while (startItem) {
startItem = startItem.previousSibling;
if (startItem && startItem.localName == "listitem") {
--delta;
if (delta == 0)
return startItem;
}
}
return null;
]]></body>
</method>
<method name="getIndexOfItem">
<parameter name="item"/>
<body>
return this.listBoxObject.getIndexOfItem(item);
</body>
</method>
<method name="getItemAtIndex">
<parameter name="index"/>
<body>
return this.listBoxObject.getItemAtIndex(index);
</body>
</method>
<method name="ensureIndexIsVisible">
<parameter name="index"/>
<body>
return this.listBoxObject.ensureIndexIsVisible(index);
</body>
</method>
<method name="ensureElementIsVisible">
<parameter name="element"/>
<body>
return this.ensureIndexIsVisible(this.listBoxObject.getIndexOfItem(element));
</body>
</method>
<method name="scrollToIndex">
<parameter name="index"/>
<body>
return this.listBoxObject.scrollToIndex(index);
</body>
</method>
<method name="getNumberOfVisibleRows">
<body>
return this.listBoxObject.getNumberOfVisibleRows();
</body>
</method>
<method name="getIndexOfFirstVisibleRow">
<body>
return this.listBoxObject.getIndexOfFirstVisibleRow();
</body>
</method>
<method name="getRowCount">
<body>
return this.listBoxObject.getRowCount();
</body>
</method>
<method name="moveByOffset">
<parameter name="offset"/>
<parameter name="isSelecting"/>
<parameter name="isSelectingRange"/>
<body>
<![CDATA[
if ((isSelectingRange || !isSelecting) && this.selType != "multiple")
return;
var newIndex = this.currentIndex + offset;
if (newIndex < 0)
newIndex = 0;
var numItems = this.getRowCount();
if (newIndex > numItems - 1)
var newIndex = numItems - 1;
var newItem = this.getItemAtIndex(newIndex);
if (newItem) {
this.ensureIndexIsVisible(newIndex);
if (isSelectingRange) {
this.selectItemRange(null, newItem);
}
else if (isSelecting) {
this.selectItem(newItem);
}
this.currentItem = newItem;
}
]]>
</body>
</method>
<method name="scrollOnePage">
<parameter name="direction"/> <!-- Must be -1 or 1 -->
<body>
<![CDATA[
var pageOffset = this.getNumberOfVisibleRows() * direction;
var newTop = this.getIndexOfFirstVisibleRow() + pageOffset;
if (direction == 1) {
var maxTop = this.getRowCount() - pageOffset;
if (newTop >= maxTop && maxTop > this.currentIndex) {
newTop = maxTop;
}
}
else if (newTop < 0)
newTop = 0;
this.scrollToIndex(newTop);
return pageOffset;
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="keypress" keycode="vk_up" modifiers="control shift any"
phase="target" action="moveByOffset(-1, !event.ctrlKey, event.shiftKey);"/>
<handler event="keypress" keycode="vk_down" modifiers="control shift any"
phase="target" action="moveByOffset(1, !event.ctrlKey, event.shiftKey);"/>
<handler event="keypress" keycode="vk_home" modifiers="control shift any"
phase="target" action="moveByOffset(-this.currentIndex, !event.ctrlKey, event.shiftKey);"/>
<handler event="keypress" keycode="vk_end" modifiers="control shift any"
phase="target" action="moveByOffset(this.getRowCount() - this.currentIndex - 1, !event.ctrlKey, event.shiftKey);"/>
<handler event="keypress" keycode="vk_page_up" modifiers="control shift any"
phase="target" action="moveByOffset(this.scrollOnePage(-1), !event.ctrlKey, event.shiftKey);"/>
<handler event="keypress" keycode="vk_page_down" modifiers="control shift any"
phase="target" action="moveByOffset(this.scrollOnePage(1), !event.ctrlKey, event.shiftKey);"/>
<handler event="keypress" key=" " phase="target">
<![CDATA[
if (this.currentItem) {
if (this.currentItem.getAttribute("type") != "checkbox")
this.addItemToSelection(this.currentItem);
else if (!this.currentItem.disabled)
this.currentItem.checked = !this.currentItem.checked;
}
]]>
</handler>
<handler event="focus">
<![CDATA[
if (this.currentIndex == -1 && this.getRowCount() > 0) {
this.currentIndex = this.getIndexOfFirstVisibleRow();
}
]]>
</handler>
<handler event="keypress" key=" " modifiers="control" phase="target">
<![CDATA[
if (this.currentItem && this.selType == "multiple")
this.toggleItemSelection(this.currentItem);
]]>
</handler>
<handler event="keypress" phase="target">
<![CDATA[
if (!this.disableKeyNavigation && event.charCode > 0 &&
!event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
var key = String.fromCharCode(event.charCode);
key = key.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 l = this.selectedItems.length;
var c = -1;
if (l > 0)
c = this.getIndexOfItem(this.selectedItems[l-1]);
var rowCount = this.getRowCount();
var start = 1;
if (length > 1) {
start = 0;
if (c < 0)
c = 0;
}
for (var i = 0; i < rowCount; i++) {
var k = (i + start + c) % rowCount;
var item = this.getItemAtIndex(k); //listitem
var cellText = item.getAttribute("label");
cellText = cellText.substring(0, length).toLowerCase();
if (cellText == incrementalString) {
this.ensureIndexIsVisible(k);
this.timedSelect(item, this._selectDelay);
break;
}
}
}
]]>
</handler>
</handlers>
</binding>
<binding id="listrows"
extends="chrome://global/content/bindings/listbox.xml#listbox-base">
<handlers>
<handler event="DOMMouseScroll" phase="capturing">
<![CDATA[
var listBox = this.parentNode.listBoxObject;
var rows = event.detail;
if (rows == NSUIEvent.SCROLL_PAGE_UP)
rows = -listBox.getNumberOfVisibleRows();
else if (rows == NSUIEvent.SCROLL_PAGE_DOWN)
rows = listBox.getNumberOfVisibleRows();
listBox.scrollByLines(rows);
event.preventDefault();
]]>
</handler>
</handlers>
</binding>
<binding id="listitem"
extends="chrome://global/content/bindings/general.xml#basetext">
<resources>
<stylesheet src="chrome://global/skin/listbox.css"/>
</resources>
<content>
<children>
<xul:listcell xbl:inherits="label,crop,disabled,flexlabel"/>
</children>
</content>
<implementation implements="nsIDOMXULSelectControlItemElement, nsIAccessibleProvider">
<property name="current" onget="return this.getAttribute('current') == 'true';">
<setter><![CDATA[
if (val)
this.setAttribute("current", "true");
else
this.removeAttribute("current");
return val;
]]></setter>
</property>
<!-- ///////////////// nsIAccessibleProvider ///////////////// -->
<property name="accessible">
<getter>
<![CDATA[
var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
return accService.createXULListitemAccessible(this);
]]>
</getter>
</property>
<!-- ///////////////// nsIDOMXULSelectControlItemElement ///////////////// -->
<property name="value" onget="return this.getAttribute('value');"
onset="this.setAttribute('value', val); return val;"/>
<property name="label" onget="return this.getAttribute('label');"
onset="this.setAttribute('label', val); return val;"/>
<property name="selected" onget="return this.getAttribute('selected') == 'true';">
<setter><![CDATA[
if (val)
this.setAttribute("selected", "true");
else
this.removeAttribute("selected");
var event = document.createEvent("Events");
event.initEvent("DOMMenuItemActive", true, true);
this.dispatchEvent(event);
return val;
]]></setter>
</property>
<property name="control">
<getter><![CDATA[
var parent = this.parentNode;
while (parent) {
if (parent.localName == "listbox")
return parent;
parent = parent.parentNode;
}
return null;
]]></getter>
</property>
</implementation>
<handlers>
<!-- If there is no modifier key, we select on mousedown, not
click, so that drags work correctly. -->
<handler event="mousedown">
<![CDATA[
if (this.parentNode.disabled) return;
if (!event.ctrlKey && !event.shiftKey && !event.metaKey) {
if (!this.selected) {
parentNode.selectItem(this);
}
parentNode.currentItem = this;
}
]]>
</handler>
<!-- On a click (up+down on the same item), deselect everything
except this item. -->
<handler event="click" button="0">
<![CDATA[
if (this.parentNode.disabled) return;
if (parentNode.selType != "multiple") {
parentNode.selectItem(this);
}
else if (event.ctrlKey || event.metaKey) {
parentNode.toggleItemSelection(this);
parentNode.currentItem = this;
}
else if (event.shiftKey) {
parentNode.selectItemRange(null, this);
parentNode.currentItem = this;
}
else {
/* 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 */
var selectedItems = parentNode.selectedItems;
var didSuppressSelect = false;
var i = 0;
while (i < selectedItems.length) {
if (selectedItems[i] != this) {
if (!didSuppressSelect) {
parentNode._suppressOnSelect = true;
didSuppressSelect = true;
}
parentNode.removeItemFromSelection(selectedItems[i]);
}
else
i++;
}
if (didSuppressSelect) {
parentNode._suppressOnSelect = false;
parentNode._fireOnSelect();
}
}
]]>
</handler>
</handlers>
</binding>
<binding id="listitem-iconic"
extends="chrome://global/content/bindings/listbox.xml#listitem">
<content>
<children>
<xul:listcell class="listcell-iconic" xbl:inherits="label,image,crop,disabled,flexlabel"/>
</children>
</content>
</binding>
<binding id="listitem-checkbox"
extends="chrome://global/content/bindings/listbox.xml#listitem">
<content>
<children>
<xul:listcell type="checkbox" xbl:inherits="label,crop,checked,disabled,flexlabel"/>
</children>
</content>
<implementation>
<property name="checked"
onget="return this.getAttribute('checked') == 'true';">
<setter><![CDATA[
if (val)
this.setAttribute('checked', 'true');
else
this.removeAttribute('checked');
var event = document.createEvent('Events');
event.initEvent('CheckboxStateChange', false, true);
this.dispatchEvent(event);
return val;
]]></setter>
</property>
</implementation>
<handlers>
<handler event="mousedown" button="0">
<![CDATA[
if (!this.disabled && !this.parentNode.disabled)
this.checked = !this.checked;
]]>
</handler>
</handlers>
</binding>
<binding id="listitem-checkbox-iconic"
extends="chrome://global/content/bindings/listbox.xml#listitem-checkbox">
<content>
<children>
<xul:listcell type="checkbox" class="listcell-iconic" xbl:inherits="label,image,crop,checked,disabled,flexlabel"/>
</children>
</content>
</binding>
<binding id="listcell"
extends="chrome://global/content/bindings/listbox.xml#listbox-base">
<content>
<children>
<xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
</children>
</content>
</binding>
<binding id="listcell-iconic"
extends="chrome://global/content/bindings/listbox.xml#listcell">
<content>
<children>
<xul:image class="listcell-icon" xbl:inherits="src=image"/>
<xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
</children>
</content>
</binding>
<binding id="listcell-checkbox"
extends="chrome://global/content/bindings/listbox.xml#listcell">
<content>
<children>
<xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
<xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabelcrop,disabled" flex="1" crop="right"/>
</children>
</content>
</binding>
<binding id="listcell-checkbox-iconic"
extends="chrome://global/content/bindings/listbox.xml#listcell-checkbox">
<content>
<children>
<xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
<xul:image class="listcell-icon" xbl:inherits="src=image"/>
<xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabelcrop,disabled" flex="1" crop="right"/>
</children>
</content>
</binding>
<binding id="listhead"
extends="chrome://global/content/bindings/listbox.xml#listbox-base">
<content>
<xul:listheaditem>
<children includes="listheader"/>
</xul:listheaditem>
</content>
</binding>
<binding id="listheader" display="xul:button"
extends="chrome://global/content/bindings/listbox.xml#listbox-base">
<content>
<xul:image class="listheader-icon"/>
<xul:label class="listheader-label" xbl:inherits="value=label,crop" flex="1" crop="right"/>
<xul:image class="listheader-sortdirection" xbl:inherits="sortDirection"/>
</content>
</binding>
</bindings>