summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/listbox.xml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/listbox.xml')
-rw-r--r--toolkit/content/widgets/listbox.xml1144
1 files changed, 1144 insertions, 0 deletions
diff --git a/toolkit/content/widgets/listbox.xml b/toolkit/content/widgets/listbox.xml
new file mode 100644
index 000000000..9fae61669
--- /dev/null
+++ b/toolkit/content/widgets/listbox.xml
@@ -0,0 +1,1144 @@
+<?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/. -->
+
+<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">
+
+ <!--
+ Interface binding that is base for bindings of xul:listbox and
+ xul:richlistbox elements. This binding assumes that successors bindings
+ will implement the following properties and methods:
+
+ /** Return the number of items */
+ readonly itemCount
+
+ /** Return index of given item
+ * @param aItem - given item element */
+ getIndexOfItem(aItem)
+
+ /** Return item at given index
+ * @param aIndex - index of item element */
+ getItemAtIndex(aIndex)
+
+ /** Return count of item elements */
+ getRowCount()
+
+ /** Return count of visible item elements */
+ getNumberOfVisibleRows()
+
+ /** Return index of first visible item element */
+ getIndexOfFirstVisibleRow()
+
+ /** Return true if item of given index is visible
+ * @param aIndex - index of item element
+ *
+ * @note XXX: this method should be removed after bug 364612 is fixed
+ */
+ ensureIndexIsVisible(aIndex)
+
+ /** Return true if item element is visible
+ * @param aElement - given item element */
+ ensureElementIsVisible(aElement)
+
+ /** Scroll list control to make visible item of given index
+ * @param aIndex - index of item element
+ *
+ * @note XXX: this method should be removed after bug 364612 is fixed
+ */
+ scrollToIndex(aIndex)
+
+ /** Create item element and append it to the end of listbox
+ * @param aLabel - label of new item element
+ * @param aValue - value of new item element */
+ appendItem(aLabel, aValue)
+
+ /** Create item element and insert it to given position
+ * @param aIndex - insertion position
+ * @param aLabel - label of new item element
+ * @param aValue - value of new item element */
+ insertItemAt(aIndex, aLabel, aValue)
+
+ /** Scroll up/down one page
+ * @param aDirection - specifies scrolling direction, should be either -1 or 1
+ * @return the number of elements the selection scrolled
+ */
+ scrollOnePage(aDirection)
+
+ /** Fire "select" event */
+ _fireOnSelect()
+ -->
+ <binding id="listbox-base" role="xul:listbox"
+ extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+ <implementation implements="nsIDOMXULMultiSelectControlElement">
+ <field name="_lastKeyTime">0</field>
+ <field name="_incrementalString">""</field>
+
+ <!-- nsIDOMXULSelectControlElement -->
+ <property name="selectedItem"
+ onset="this.selectItem(val);">
+ <getter>
+ <![CDATA[
+ return this.selectedItems.length > 0 ? this.selectedItems[0] : null;
+ ]]>
+ </getter>
+ </property>
+
+ <property name="selectedIndex">
+ <getter>
+ <![CDATA[
+ if (this.selectedItems.length > 0)
+ return this.getIndexOfItem(this.selectedItems[0]);
+ return -1;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ if (val >= 0) {
+ this.selectItem(this.getItemAtIndex(val));
+ } else {
+ this.clearSelection();
+ this.currentItem = null;
+ }
+ ]]>
+ </setter>
+ </property>
+
+ <property name="value">
+ <getter>
+ <![CDATA[
+ if (this.selectedItems.length > 0)
+ return this.selectedItem.value;
+ 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="removeItemAt">
+ <parameter name="index"/>
+ <body>
+ <![CDATA[
+ var remove = this.getItemAtIndex(index);
+ if (remove)
+ this.removeChild(remove);
+ return remove;
+ ]]>
+ </body>
+ </method>
+
+ <!-- nsIDOMXULMultiSelectControlElement -->
+ <property name="selType"
+ onget="return this.getAttribute('seltype');"
+ onset="this.setAttribute('seltype', val); return val;"/>
+
+ <property name="currentItem" onget="return this._currentItem;">
+ <setter>
+ if (this._currentItem == val)
+ return val;
+
+ if (this._currentItem)
+ this._currentItem.current = false;
+ this._currentItem = val;
+
+ if (val)
+ val.current = true;
+
+ return val;
+ </setter>
+ </property>
+
+ <property name="currentIndex">
+ <getter>
+ 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>
+
+ <field name="selectedItems">new ChromeNodeList()</field>
+
+ <method name="addItemToSelection">
+ <parameter name="aItem"/>
+ <body>
+ <![CDATA[
+ if (this.selType != "multiple" && this.selectedCount)
+ return;
+
+ if (aItem.selected)
+ return;
+
+ this.selectedItems.append(aItem);
+ aItem.selected = true;
+
+ this._fireOnSelect();
+ ]]>
+ </body>
+ </method>
+
+ <method name="removeItemFromSelection">
+ <parameter name="aItem"/>
+ <body>
+ <![CDATA[
+ if (!aItem.selected)
+ return;
+
+ this.selectedItems.remove(aItem);
+ aItem.selected = false;
+ this._fireOnSelect();
+ ]]>
+ </body>
+ </method>
+
+ <method name="toggleItemSelection">
+ <parameter name="aItem"/>
+ <body>
+ <![CDATA[
+ if (aItem.selected)
+ this.removeItemFromSelection(aItem);
+ else
+ this.addItemToSelection(aItem);
+ ]]>
+ </body>
+ </method>
+
+ <method name="selectItem">
+ <parameter name="aItem"/>
+ <body>
+ <![CDATA[
+ if (!aItem)
+ return;
+
+ if (this.selectedItems.length == 1 && this.selectedItems[0] == aItem)
+ return;
+
+ this._selectionStart = null;
+
+ var suppress = this._suppressOnSelect;
+ this._suppressOnSelect = true;
+
+ this.clearSelection();
+ this.addItemToSelection(aItem);
+ this.currentItem = aItem;
+
+ this._suppressOnSelect = suppress;
+ this._fireOnSelect();
+ ]]>
+ </body>
+ </method>
+
+ <method name="selectItemRange">
+ <parameter name="aStartItem"/>
+ <parameter name="aEndItem"/>
+ <body>
+ <![CDATA[
+ if (this.selType != "multiple")
+ return;
+
+ if (!aStartItem)
+ aStartItem = this._selectionStart ?
+ this._selectionStart : this.currentItem;
+
+ if (!aStartItem)
+ aStartItem = aEndItem;
+
+ var suppressSelect = this._suppressOnSelect;
+ this._suppressOnSelect = true;
+
+ this._selectionStart = aStartItem;
+
+ var currentItem;
+ var startIndex = this.getIndexOfItem(aStartItem);
+ var endIndex = this.getIndexOfItem(aEndItem);
+ if (endIndex < startIndex) {
+ currentItem = aEndItem;
+ aEndItem = aStartItem;
+ aStartItem = currentItem;
+ } else {
+ currentItem = aStartItem;
+ }
+
+ while (currentItem) {
+ this.addItemToSelection(currentItem);
+ if (currentItem == aEndItem) {
+ 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.
+ var userSelecting = this._userSelecting;
+ this._userSelecting = false; // that's US automatically unselecting
+ for (; currentItem; currentItem = this.getNextItem(currentItem, 1))
+ this.removeItemFromSelection(currentItem);
+
+ for (currentItem = this.getItemAtIndex(0); currentItem != aStartItem;
+ currentItem = this.getNextItem(currentItem, 1))
+ this.removeItemFromSelection(currentItem);
+ this._userSelecting = userSelecting;
+
+ this._suppressOnSelect = suppressSelect;
+
+ this._fireOnSelect();
+ ]]>
+ </body>
+ </method>
+
+ <method name="selectAll">
+ <body>
+ this._selectionStart = null;
+
+ 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>
+ this._selectionStart = null;
+
+ 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) {
+ while (this.selectedItems.length > 0) {
+ let item = this.selectedItems[0];
+ item.selected = false;
+ this.selectedItems.remove(item);
+ }
+ }
+
+ this._selectionStart = null;
+ this._fireOnSelect();
+ ]]>
+ </body>
+ </method>
+
+ <property name="selectedCount" readonly="true"
+ onget="return this.selectedItems.length;"/>
+
+ <method name="getSelectedItem">
+ <parameter name="aIndex"/>
+ <body>
+ <![CDATA[
+ return aIndex < this.selectedItems.length ?
+ this.selectedItems[aIndex] : null;
+ ]]>
+ </body>
+ </method>
+
+ <!-- Other public members -->
+ <property name="disableKeyNavigation"
+ onget="return this.hasAttribute('disableKeyNavigation');">
+ <setter>
+ if (val)
+ this.setAttribute("disableKeyNavigation", "true");
+ else
+ this.removeAttribute("disableKeyNavigation");
+ return val;
+ </setter>
+ </property>
+
+ <property name="suppressOnSelect"
+ onget="return this.getAttribute('suppressonselect') == 'true';"
+ onset="this.setAttribute('suppressonselect', val);"/>
+
+ <property name="_selectDelay"
+ onset="this.setAttribute('_selectDelay', val);"
+ onget="return this.getAttribute('_selectDelay') || 50;"/>
+
+ <method name="timedSelect">
+ <parameter name="aItem"/>
+ <parameter name="aTimeout"/>
+ <body>
+ <![CDATA[
+ var suppress = this._suppressOnSelect;
+ if (aTimeout != -1)
+ this._suppressOnSelect = true;
+
+ this.selectItem(aItem);
+
+ this._suppressOnSelect = suppress;
+
+ if (aTimeout != -1) {
+ if (this._selectTimeout)
+ window.clearTimeout(this._selectTimeout);
+ this._selectTimeout =
+ window.setTimeout(this._selectTimeoutHandler, aTimeout, this);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="moveByOffset">
+ <parameter name="aOffset"/>
+ <parameter name="aIsSelecting"/>
+ <parameter name="aIsSelectingRange"/>
+ <body>
+ <![CDATA[
+ if ((aIsSelectingRange || !aIsSelecting) &&
+ this.selType != "multiple")
+ return;
+
+ var newIndex = this.currentIndex + aOffset;
+ if (newIndex < 0)
+ newIndex = 0;
+
+ var numItems = this.getRowCount();
+ if (newIndex > numItems - 1)
+ newIndex = numItems - 1;
+
+ var newItem = this.getItemAtIndex(newIndex);
+ // make sure that the item is actually visible/selectable
+ if (this._userSelecting && newItem && !this._canUserSelect(newItem))
+ newItem =
+ aOffset > 0 ? this.getNextItem(newItem, 1) || this.getPreviousItem(newItem, 1) :
+ this.getPreviousItem(newItem, 1) || this.getNextItem(newItem, 1);
+ if (newItem) {
+ this.ensureIndexIsVisible(this.getIndexOfItem(newItem));
+ if (aIsSelectingRange)
+ this.selectItemRange(null, newItem);
+ else if (aIsSelecting)
+ this.selectItem(newItem);
+
+ this.currentItem = newItem;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <!-- Private -->
+ <method name="getNextItem">
+ <parameter name="aStartItem"/>
+ <parameter name="aDelta"/>
+ <body>
+ <![CDATA[
+ while (aStartItem) {
+ aStartItem = aStartItem.nextSibling;
+ if (aStartItem && aStartItem instanceof
+ Components.interfaces.nsIDOMXULSelectControlItemElement &&
+ (!this._userSelecting || this._canUserSelect(aStartItem))) {
+ --aDelta;
+ if (aDelta == 0)
+ return aStartItem;
+ }
+ }
+ return null;
+ ]]></body>
+ </method>
+
+ <method name="getPreviousItem">
+ <parameter name="aStartItem"/>
+ <parameter name="aDelta"/>
+ <body>
+ <![CDATA[
+ while (aStartItem) {
+ aStartItem = aStartItem.previousSibling;
+ if (aStartItem && aStartItem instanceof
+ Components.interfaces.nsIDOMXULSelectControlItemElement &&
+ (!this._userSelecting || this._canUserSelect(aStartItem))) {
+ --aDelta;
+ if (aDelta == 0)
+ return aStartItem;
+ }
+ }
+ return null;
+ ]]>
+ </body>
+ </method>
+
+ <method name="_moveByOffsetFromUserEvent">
+ <parameter name="aOffset"/>
+ <parameter name="aEvent"/>
+ <body>
+ <![CDATA[
+ if (!aEvent.defaultPrevented) {
+ this._userSelecting = true;
+ this._mayReverse = true;
+ this.moveByOffset(aOffset, !aEvent.ctrlKey, aEvent.shiftKey);
+ this._userSelecting = false;
+ this._mayReverse = false;
+ aEvent.preventDefault();
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_canUserSelect">
+ <parameter name="aItem"/>
+ <body>
+ <![CDATA[
+ var style = document.defaultView.getComputedStyle(aItem, "");
+ return style.display != "none" && style.visibility == "visible";
+ ]]>
+ </body>
+ </method>
+
+ <method name="_selectTimeoutHandler">
+ <parameter name="aMe"/>
+ <body>
+ aMe._fireOnSelect();
+ aMe._selectTimeout = null;
+ </body>
+ </method>
+
+ <field name="_suppressOnSelect">false</field>
+ <field name="_userSelecting">false</field>
+ <field name="_mayReverse">false</field>
+ <field name="_selectTimeout">null</field>
+ <field name="_currentItem">null</field>
+ <field name="_selectionStart">null</field>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress" keycode="VK_UP" modifiers="control shift any"
+ action="this._moveByOffsetFromUserEvent(-1, event);"
+ group="system"/>
+ <handler event="keypress" keycode="VK_DOWN" modifiers="control shift any"
+ action="this._moveByOffsetFromUserEvent(1, event);"
+ group="system"/>
+ <handler event="keypress" keycode="VK_HOME" modifiers="control shift any"
+ group="system">
+ <![CDATA[
+ this._mayReverse = true;
+ this._moveByOffsetFromUserEvent(-this.currentIndex, event);
+ this._mayReverse = false;
+ ]]>
+ </handler>
+ <handler event="keypress" keycode="VK_END" modifiers="control shift any"
+ group="system">
+ <![CDATA[
+ this._mayReverse = true;
+ this._moveByOffsetFromUserEvent(this.getRowCount() - this.currentIndex - 1, event);
+ this._mayReverse = false;
+ ]]>
+ </handler>
+ <handler event="keypress" keycode="VK_PAGE_UP" modifiers="control shift any"
+ group="system">
+ <![CDATA[
+ this._mayReverse = true;
+ this._moveByOffsetFromUserEvent(this.scrollOnePage(-1), event);
+ this._mayReverse = false;
+ ]]>
+ </handler>
+ <handler event="keypress" keycode="VK_PAGE_DOWN" modifiers="control shift any"
+ group="system">
+ <![CDATA[
+ this._mayReverse = true;
+ this._moveByOffsetFromUserEvent(this.scrollOnePage(1), event);
+ this._mayReverse = false;
+ ]]>
+ </handler>
+ <handler event="keypress" key=" " modifiers="control" phase="target">
+ <![CDATA[
+ if (this.currentItem && this.selType == "multiple")
+ this.toggleItemSelection(this.currentItem);
+ ]]>
+ </handler>
+ <handler event="focus">
+ <![CDATA[
+ if (this.getRowCount() > 0) {
+ if (this.currentIndex == -1) {
+ this.currentIndex = this.getIndexOfFirstVisibleRow();
+ }
+ else {
+ this.currentItem._fireEvent("DOMMenuItemActive");
+ }
+ }
+ this._lastKeyTime = 0;
+ ]]>
+ </handler>
+ <handler event="keypress" phase="target">
+ <![CDATA[
+ if (this.disableKeyNavigation || !event.charCode ||
+ event.altKey || event.ctrlKey || event.metaKey)
+ return;
+
+ if (event.timeStamp - this._lastKeyTime > 1000)
+ this._incrementalString = "";
+
+ var key = String.fromCharCode(event.charCode).toLowerCase();
+ this._incrementalString += key;
+ this._lastKeyTime = event.timeStamp;
+
+ // If all letters in the incremental string are the same, just
+ // try to match the first one
+ var incrementalString = /^(.)\1+$/.test(this._incrementalString) ?
+ RegExp.$1 : this._incrementalString;
+ var length = incrementalString.length;
+
+ var rowCount = this.getRowCount();
+ var l = this.selectedItems.length;
+ var start = l > 0 ? this.getIndexOfItem(this.selectedItems[l - 1]) : -1;
+ // start from the first element if none was selected or from the one
+ // following the selected one if it's a new or a repeated-letter search
+ if (start == -1 || length == 1)
+ start++;
+
+ for (var i = 0; i < rowCount; i++) {
+ var k = (start + i) % rowCount;
+ var listitem = this.getItemAtIndex(k);
+ if (!this._canUserSelect(listitem))
+ continue;
+ // allow richlistitems to specify the string being searched for
+ var searchText = "searchLabel" in listitem ? listitem.searchLabel :
+ listitem.getAttribute("label"); // (see also bug 250123)
+ searchText = searchText.substring(0, length).toLowerCase();
+ if (searchText == incrementalString) {
+ this.ensureIndexIsVisible(k);
+ this.timedSelect(listitem, this._selectDelay);
+ break;
+ }
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+
+ <!-- Binding for xul:listbox element.
+ -->
+ <binding id="listbox"
+ extends="#listbox-base">
+
+ <resources>
+ <stylesheet src="chrome://global/skin/listbox.css"/>
+ </resources>
+
+ <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>
+
+ <!-- ///////////////// public listbox members ///////////////// -->
+
+ <property name="listBoxObject"
+ onget="return this.boxObject;"
+ readonly="true"/>
+
+ <!-- ///////////////// private listbox members ///////////////// -->
+
+ <method name="_fireOnSelect">
+ <body>
+ <![CDATA[
+ if (!this._suppressOnSelect && !this.suppressOnSelect) {
+ var event = document.createEvent("Events");
+ event.initEvent("select", true, true);
+ this.dispatchEvent(event);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <constructor>
+ <![CDATA[
+ var count = this.itemCount;
+ for (var index = 0; index < count; index++) {
+ var item = this.getItemAtIndex(index);
+ if (item.getAttribute("selected") == "true")
+ this.selectedItems.append(item);
+ }
+ ]]>
+ </constructor>
+
+ <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
+
+ <method name="appendItem">
+ <parameter name="aLabel"/>
+ <parameter name="aValue"/>
+ <body>
+ const XULNS =
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ var item = this.ownerDocument.createElementNS(XULNS, "listitem");
+ item.setAttribute("label", aLabel);
+ item.setAttribute("value", aValue);
+ this.appendChild(item);
+ return item;
+ </body>
+ </method>
+
+ <method name="insertItemAt">
+ <parameter name="aIndex"/>
+ <parameter name="aLabel"/>
+ <parameter name="aValue"/>
+ <body>
+ const XULNS =
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ var item = this.ownerDocument.createElementNS(XULNS, "listitem");
+ item.setAttribute("label", aLabel);
+ item.setAttribute("value", aValue);
+ var before = this.getItemAtIndex(aIndex);
+ if (before)
+ this.insertBefore(item, before);
+ else
+ this.appendChild(item);
+ return item;
+ </body>
+ </method>
+
+ <property name="itemCount" readonly="true"
+ onget="return this.listBoxObject.getRowCount()"/>
+
+ <!-- ///////////////// nsIListBoxObject ///////////////// -->
+ <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="scrollOnePage">
+ <parameter name="direction"/> <!-- Must be -1 or 1 -->
+ <body>
+ <![CDATA[
+ var pageOffset = this.getNumberOfVisibleRows() * direction;
+ // skip over invisible elements - the user won't care about them
+ for (var i = 0; i != pageOffset; i += direction) {
+ var item = this.getItemAtIndex(this.currentIndex + i);
+ if (item && !this._canUserSelect(item))
+ pageOffset += direction;
+ }
+ var newTop = this.getIndexOfFirstVisibleRow() + pageOffset;
+ if (direction == 1) {
+ var maxTop = this.getRowCount() - this.getNumberOfVisibleRows();
+ for (i = this.getRowCount(); i >= 0 && i > maxTop; i--) {
+ item = this.getItemAtIndex(i);
+ if (item && !this._canUserSelect(item))
+ maxTop--;
+ }
+ if (newTop >= maxTop)
+ newTop = maxTop;
+ }
+ if (newTop < 0)
+ newTop = 0;
+ this.scrollToIndex(newTop);
+ return pageOffset;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress" key=" " phase="target">
+ <![CDATA[
+ if (this.currentItem) {
+ if (this.currentItem.getAttribute("type") != "checkbox") {
+ this.addItemToSelection(this.currentItem);
+ // Prevent page from scrolling on the space key.
+ event.preventDefault();
+ }
+ else if (!this.currentItem.disabled) {
+ this.currentItem.checked = !this.currentItem.checked;
+ this.currentItem.doCommand();
+ // Prevent page from scrolling on the space key.
+ event.preventDefault();
+ }
+ }
+ ]]>
+ </handler>
+
+ <handler event="MozSwipeGesture">
+ <![CDATA[
+ // Figure out which index to show
+ let targetIndex = 0;
+
+ // Only handle swipe gestures up and down
+ switch (event.direction) {
+ case event.DIRECTION_DOWN:
+ targetIndex = this.itemCount - 1;
+ // Fall through for actual action
+ case event.DIRECTION_UP:
+ this.ensureIndexIsVisible(targetIndex);
+ break;
+ }
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="listrows">
+
+ <resources>
+ <stylesheet src="chrome://global/skin/listbox.css"/>
+ </resources>
+
+ <handlers>
+ <handler event="DOMMouseScroll" phase="capturing">
+ <![CDATA[
+ if (event.axis == event.HORIZONTAL_AXIS)
+ return;
+
+ var listBox = this.parentNode.listBoxObject;
+ var rows = event.detail;
+ if (rows == UIEvent.SCROLL_PAGE_UP)
+ rows = -listBox.getNumberOfVisibleRows();
+ else if (rows == UIEvent.SCROLL_PAGE_DOWN)
+ rows = listBox.getNumberOfVisibleRows();
+
+ listBox.scrollByLines(rows);
+ event.preventDefault();
+ ]]>
+ </handler>
+
+ <handler event="MozMousePixelScroll" phase="capturing">
+ <![CDATA[
+ if (event.axis == event.HORIZONTAL_AXIS)
+ return;
+
+ // shouldn't be scrolled by pixel scrolling events before a line/page
+ // scrolling event.
+ event.preventDefault();
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="listitem" role="xul: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">
+ <property name="current" onget="return this.getAttribute('current') == 'true';">
+ <setter><![CDATA[
+ if (val)
+ this.setAttribute("current", "true");
+ else
+ this.removeAttribute("current");
+
+ let control = this.control;
+ if (!control || !control.suppressMenuItemEvent) {
+ this._fireEvent(val ? "DOMMenuItemActive" : "DOMMenuItemInactive");
+ }
+
+ return val;
+ ]]></setter>
+ </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");
+
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="control">
+ <getter><![CDATA[
+ var parent = this.parentNode;
+ while (parent) {
+ if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
+ return parent;
+ parent = parent.parentNode;
+ }
+ return null;
+ ]]></getter>
+ </property>
+
+ <method name="_fireEvent">
+ <parameter name="name"/>
+ <body>
+ <![CDATA[
+ var event = document.createEvent("Events");
+ event.initEvent(name, true, true);
+ this.dispatchEvent(event);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ <handlers>
+ <!-- If there is no modifier key, we select on mousedown, not
+ click, so that drags work correctly. -->
+ <handler event="mousedown">
+ <![CDATA[
+ var control = this.control;
+ if (!control || control.disabled)
+ return;
+ if ((!event.ctrlKey || (/Mac/.test(navigator.platform) && event.button == 2)) &&
+ !event.shiftKey && !event.metaKey) {
+ if (!this.selected) {
+ control.selectItem(this);
+ }
+ control.currentItem = this;
+ }
+ ]]>
+ </handler>
+
+ <!-- On a click (up+down on the same item), deselect everything
+ except this item. -->
+ <handler event="click" button="0">
+ <![CDATA[
+ var control = this.control;
+ if (!control || control.disabled)
+ return;
+ control._userSelecting = true;
+ if (control.selType != "multiple") {
+ control.selectItem(this);
+ }
+ else if (event.ctrlKey || event.metaKey) {
+ control.toggleItemSelection(this);
+ control.currentItem = this;
+ }
+ else if (event.shiftKey) {
+ control.selectItemRange(null, this);
+ control.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 */
+
+ // use selectItemRange instead of selectItem, because this
+ // doesn't de- and reselect this item if it is selected
+ control.selectItemRange(this, this);
+ }
+ control._userSelecting = false;
+ ]]>
+ </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', true, true);
+ this.dispatchEvent(event);
+ return val;
+ ]]></setter>
+ </property>
+ </implementation>
+
+ <handlers>
+ <handler event="mousedown" button="0">
+ <![CDATA[
+ if (!this.disabled && !this.control.disabled) {
+ this.checked = !this.checked;
+ this.doCommand();
+ }
+ ]]>
+ </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" role="xul:listcell"
+ extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+ <resources>
+ <stylesheet src="chrome://global/skin/listbox.css"/>
+ </resources>
+
+ <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=flexlabel,crop,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=flexlabel,crop,disabled" flex="1" crop="right"/>
+ </children>
+ </content>
+ </binding>
+
+ <binding id="listhead" role="xul:listhead">
+
+ <resources>
+ <stylesheet src="chrome://global/skin/listbox.css"/>
+ </resources>
+
+ <content>
+ <xul:listheaditem>
+ <children includes="listheader"/>
+ </xul:listheaditem>
+ </content>
+ </binding>
+
+ <binding id="listheader" display="xul:button" role="xul:listheader">
+
+ <resources>
+ <stylesheet src="chrome://global/skin/listbox.css"/>
+ </resources>
+
+ <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>