diff options
Diffstat (limited to 'toolkit/content/widgets/listbox.xml')
-rw-r--r-- | toolkit/content/widgets/listbox.xml | 1144 |
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> |