--- a/common/bindings/listbox.xml
+++ b/common/bindings/listbox.xml
@@ -6,29 +6,1125 @@
<!-- This files relies on these specific Chrome/XBL globals -->
<!-- globals ChromeNodeList -->
<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">
+#if 0
+ <!--
+ 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)
+
+ /** 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()
+ -->
+#endif
+ <binding id="listbox-base"
+ 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 is a micro-optimization so that a call to getIndexOfItem or
+ // getItemAtIndex caused by _fireOnSelect (especially for derived
+ // widgets) won't loop the children.
+ this._selecting = {
+ item: this.getItemAtIndex(val),
+ index: val
+ };
+ this.selectItem(this._selecting.item);
+ delete this._selecting;
+ } 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>
+
+ <!-- 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
+ Ci.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
+ Ci.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">
+
+ <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 ///////////////// -->
+
+ <field name="_touchY">-1</field>
+
+ <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>
+
+ <property name="itemCount" readonly="true"
+ onget="return this.listBoxObject.getRowCount()"/>
+
+ <!-- ///////////////// nsIListBoxObject ///////////////// -->
+ <method name="getIndexOfItem">
+ <parameter name="item"/>
+ <body>
+ <![CDATA[
+ if (this._selecting && this._selecting.item == item)
+ return this._selecting.index;
+ return this.listBoxObject.getIndexOfItem(item);
+ ]]>
+ </body>
+ </method>
+ <method name="getItemAtIndex">
+ <parameter name="index"/>
+ <body>
+ <![CDATA[
+ if (this._selecting && this._selecting.index == index)
+ return this._selecting.item;
+ 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>
+
+ <handler event="touchstart">
+ <![CDATA[
+ if (event.touches.length > 1) {
+ // Multiple touch points detected, abort. In particular this aborts
+ // the panning gesture when the user puts a second finger down after
+ // already panning with one finger. Aborting at this point prevents
+ // the pan gesture from being resumed until all fingers are lifted
+ // (as opposed to when the user is back down to one finger).
+ this._touchY = -1;
+ } else {
+ this._touchY = event.touches[0].screenY;
+ }
+ ]]>
+ </handler>
+ <handler event="touchmove">
+ <![CDATA[
+ if (event.touches.length == 1 &&
+ this._touchY >= 0) {
+ let deltaY = this._touchY - event.touches[0].screenY;
+ let lines = Math.trunc(deltaY / this.listBoxObject.getRowHeight());
+ if (Math.abs(lines) > 0) {
+ this.listBoxObject.scrollByLines(lines);
+ deltaY -= lines * this.listBoxObject.getRowHeight();
+ this._touchY = event.touches[0].screenY + deltaY;
+ }
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ <handler event="touchend">
+ <![CDATA[
+ this._touchY = -1;
+ ]]>
+ </handler>
+
+ </handlers>
+ </binding>
+
+ <binding id="listrows">
+
+ <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"
+ extends="chrome://global/content/bindings/general.xml#basetext">
+
+ <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 Ci.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">
+#ifdef MOZ_SUITE
+ extends="chrome://communicator/content/bindings/listbox.xml#listitem">
+#else
+ extends="chrome://messenger/content/listbox.xml#listitem">
+#endif
<content>
<children>
<xul:listcell class="listcell-iconic" xbl:inherits="label,image,crop,disabled,flexlabel"/>
</children>
</content>
</binding>
<binding id="listcell-iconic"
- extends="chrome://global/content/bindings/listbox.xml#listcell">
+#ifdef MOZ_SUITE
+ extends="chrome://communicator/content/bindings/listbox.xml#listcell">
+#else
+ extends="chrome://messenger/content/listbox.xml#listcell">
+#endif
<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="listitem-checkbox"
+#ifdef MOZ_SUITE
+ extends="chrome://communicator/content/bindings/listbox.xml#listitem">
+#else
+ extends="chrome://messenger/content/listbox.xml#listitem">
+#endif
+ <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="listcell"
+ extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+ <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-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="listhead">
+
+ <content>
+ <xul:listheaditem>
+ <children includes="listheader"/>
+ </xul:listheaditem>
+ </content>
+ </binding>
+
+ <binding id="listheader" display="xul:button">
+
+ <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>
--- a/mail/base/content/bindings.css
+++ b/mail/base/content/bindings.css
@@ -1,14 +1,15 @@
/* 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/. */
@import url("chrome://global/content/textbox.css");
@import url("chrome://global/skin/textbox.css");
+@import url("chrome://messenger/skin/listbox.css");
@import url("chrome://messenger/skin/numberbox.css");
@import url("chrome://messenger/skin/spinbuttons.css");
@import url("chrome://messenger/skin/datetimepicker.css");
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
toolbox {
-moz-binding: url("chrome://messenger/content/toolbar.xml#toolbox");
@@ -70,16 +71,107 @@ textbox[type="number"] {
spinbuttons {
-moz-binding: url("chrome://messenger/content/spinbuttons.xml#spinbuttons");
}
.spinbuttons-button {
-moz-user-focus: ignore;
}
+/******** listbox **********/
+
+listbox {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listbox");
+}
+
+listhead {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listhead");
+}
+
+listrows {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listrows");
+}
+
+listitem {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listitem");
+}
+
+listitem[type="checkbox"] {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listitem-checkbox");
+}
+
+listheader {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listheader");
+ -moz-box-ordinal-group: 2147483646;
+}
+
+richlistbox > listheader {
+ -moz-box-ordinal-group: 1;
+}
+
+listcell {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listcell");
+}
+
+listcell[type="checkbox"] {
+ -moz-binding: url("chrome://messenger/content/listbox.xml#listcell-checkbox");
+}
+
+listbox {
+ display: -moz-grid;
+}
+
+listbox[rows] {
+ height: auto;
+}
+
+listcols, listhead, listrows, listboxbody {
+ display: -moz-grid-group;
+}
+
+listcol, listitem, listheaditem {
+ display: -moz-grid-line;
+}
+
+listbox {
+ -moz-user-focus: normal;
+ -moz-box-orient: vertical;
+ min-width: 0;
+ min-height: 0;
+ width: 200px;
+ height: 200px;
+}
+
+listhead {
+ -moz-box-orient: vertical;
+}
+
+listrows {
+ -moz-box-orient: vertical;
+ -moz-box-flex: 1;
+}
+
+listboxbody {
+ -moz-box-orient: vertical;
+ -moz-box-flex: 1;
+ /* Don't permit a horizontal scrollbar. See bug 285449 */
+ overflow-x: hidden !important;
+ overflow-y: auto;
+ min-height: 0;
+}
+
+listcol {
+ -moz-box-orient: vertical;
+ min-width: 16px;
+}
+
+listcell {
+ -moz-box-align: center;
+}
+
.listitem-iconic:not([type="checkbox"]) {
-moz-binding: url("chrome://messenger/content/listbox.xml#listitem-iconic");
}
.listcell-iconic {
-moz-binding: url("chrome://messenger/content/listbox.xml#listcell-iconic");
}
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -29,17 +29,17 @@ messenger.jar:
content/messenger/mailWidgets.xml (content/mailWidgets.xml)
content/messenger/customizeToolbar.css (../../common/src/customizeToolbar.css)
content/messenger/customizeToolbar.js (../../common/src/customizeToolbar.js)
* content/messenger/customizeToolbar.xul (../../common/src/customizeToolbar.xul)
content/messenger/viewSource.css (../../common/src/viewSource.css)
content/messenger/viewSource.js (../../common/src/viewSource.js)
* content/messenger/viewSource.xul (../../common/src/viewSource.xul)
* content/messenger/datetimepicker.xml (../../common/bindings/datetimepicker.xml)
- content/messenger/listbox.xml (../../common/bindings/listbox.xml)
+* content/messenger/listbox.xml (../../common/bindings/listbox.xml)
content/messenger/numberbox.xml (../../common/bindings/numberbox.xml)
content/messenger/generalBindings.xml (../../common/bindings/generalBindings.xml)
content/messenger/spinbuttons.xml (../../common/bindings/spinbuttons.xml)
* content/messenger/textbox.xml (../../common/bindings/textbox.xml)
* content/messenger/toolbar.xml (../../common/bindings/toolbar.xml)
content/messenger/attachmentList.css (content/attachmentList.css)
* content/messenger/bindings.css (content/bindings.css)
content/messenger/nsDragAndDrop.js (content/nsDragAndDrop.js)
--- a/mail/themes/linux/jar.mn
+++ b/mail/themes/linux/jar.mn
@@ -21,16 +21,17 @@ classic.jar:
skin/classic/messenger/half-operator.png (../../components/im/themes/half-operator.png)
skin/classic/messenger/voice.png (../../components/im/themes/voice.png)
skin/classic/messenger/browserRequest.css (mail/browserRequest.css)
skin/classic/messenger/compacttheme.css (mail/compacttheme.css)
skin/classic/messenger/customizeToolbar.css (mail/customizeToolbar.css)
skin/classic/messenger/datetimepicker.css (mail/datetimepicker.css)
skin/classic/messenger/section_collapsed.png (mail/section_collapsed.png)
skin/classic/messenger/section_expanded.png (mail/section_expanded.png)
+ skin/classic/messenger/listbox.css (mail/listbox.css)
skin/classic/messenger/messageBody.css (mail/messageBody.css)
skin/classic/messenger/messageHeader.css (mail/messageHeader.css)
skin/classic/messenger/messageIcons.css (mail/messageIcons.css)
skin/classic/messenger/messageQuotes.css (mail/messageQuotes.css)
skin/classic/messenger/messenger.css (mail/messenger.css)
skin/classic/messenger/numberbox.css (mail/numberbox.css)
skin/classic/messenger/attachmentList.css (mail/attachmentList.css)
skin/classic/messenger/imageFilters.svg (mail/imageFilters.svg)
new file mode 100644
--- /dev/null
+++ b/mail/themes/linux/mail/listbox.css
@@ -0,0 +1,91 @@
+/* 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/. */
+
+/* ===== listbox.css =======================================================
+ == Styles used by XUL listbox-related elements.
+ ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: listbox ::::: */
+
+listbox {
+ -moz-appearance: listbox;
+ margin: 2px 4px;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+listbox[disabled="true"] {
+ color: GrayText;
+}
+
+/* ::::: listitem ::::: */
+
+listbox:focus > listitem[selected="true"][current="true"] {
+ outline: 1px dotted #F3D982;
+}
+
+listbox:focus > listitem[current="true"] {
+ outline: 1px dotted Highlight;
+ outline-offset: -1px;
+}
+
+listitem[selected="true"] {
+ background-color: -moz-cellhighlight;
+ color: -moz-cellhighlighttext;
+}
+
+listbox:focus > listitem[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+ -moz-appearance: treeheadercell;
+ -moz-box-align: center;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.listheader-icon {
+ margin-inline-end: 2px;
+}
+
+.listheader-label {
+ margin: 0 !important;
+}
+
+/* ..... sort direction icon ..... */
+
+.listheader-sortdirection {
+ -moz-appearance: treeheadersortarrow;
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+ margin: 0 !important;
+ padding-top: 0;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ padding-inline-end: 0;
+ white-space: nowrap;
+}
+
+.listcell-label[disabled="true"] {
+ color: GrayText;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+ -moz-appearance: checkbox;
+ -moz-box-align: center;
+ margin: 0 2px;
+ min-width: 13px;
+ min-height: 13px;
+}
--- a/mail/themes/osx/jar.mn
+++ b/mail/themes/osx/jar.mn
@@ -26,16 +26,17 @@ classic.jar:
skin/classic/messenger/half-operator.png (../../components/im/themes/half-operator.png)
skin/classic/messenger/voice.png (../../components/im/themes/voice.png)
skin/classic/messenger/browserRequest.css (mail/browserRequest.css)
skin/classic/messenger/compacttheme.css (mail/compacttheme.css)
skin/classic/messenger/customizeToolbar.css (mail/customizeToolbar.css)
skin/classic/messenger/datetimepicker.css (mail/datetimepicker.css)
skin/classic/messenger/section_collapsed.png (mail/section_collapsed.png)
skin/classic/messenger/section_expanded.png (mail/section_expanded.png)
+ skin/classic/messenger/listbox.css (mail/listbox.css)
skin/classic/messenger/messageBody.css (mail/messageBody.css)
skin/classic/messenger/messageHeader.css (mail/messageHeader.css)
skin/classic/messenger/messageIcons.css (mail/messageIcons.css)
skin/classic/messenger/messageQuotes.css (mail/messageQuotes.css)
skin/classic/messenger/messageWindow.css (mail/messageWindow.css)
skin/classic/messenger/numberbox.css (mail/numberbox.css)
skin/classic/messenger/attachmentList.css (mail/attachmentList.css)
skin/classic/messenger/msgSelectOffline.css (mail/msgSelectOffline.css)
new file mode 100644
--- /dev/null
+++ b/mail/themes/osx/mail/listbox.css
@@ -0,0 +1,95 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+listbox {
+ -moz-appearance: listbox;
+ margin: 2px 4px;
+ background-color: #FFFFFF;
+ color: -moz-FieldText;
+}
+
+.listcell-label {
+ margin: 0px !important;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ white-space: nowrap;
+}
+
+/* ::::: listitem ::::: */
+
+listitem {
+ border: 1px solid transparent;
+}
+
+listitem[selected="true"] {
+ background-color: -moz-mac-secondaryhighlight;
+ color: -moz-DialogText;
+}
+
+listbox:focus > listitem[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+ -moz-appearance: treeheadercell;
+ -moz-box-align: center;
+ color: -moz-DialogText;
+ padding: 0 4px;
+}
+
+listheader[sortable="true"]:hover:active {
+ padding-top: 1px;
+ padding-inline-start: 5px;
+ padding-inline-end: 4px;
+}
+
+.listheader-icon {
+ margin-inline-end: 2px;
+}
+
+.listheader-label {
+ margin: 0px !important;
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+ margin: 0px !important;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ white-space: nowrap;
+}
+
+.listcell-label[disabled="true"] {
+ color: GrayText;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+ -moz-appearance: checkbox;
+ -moz-box-align: center;
+ margin: 0px 2px;
+ border: 1px solid -moz-DialogText;
+ min-width: 13px;
+ min-height: 13px;
+ background: -moz-Field no-repeat 50% 50%;
+}
+
+.listcell-check[checked="true"] {
+ background-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
+.listcell-check[disabled="true"] {
+ border-color: GrayText;
+}
+
+.listcell-check[disabled="true"][checked="true"] {
+ background-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
+}
--- a/mail/themes/windows/jar.mn
+++ b/mail/themes/windows/jar.mn
@@ -21,16 +21,17 @@ classic.jar:
skin/classic/messenger/half-operator.png (../../components/im/themes/half-operator.png)
skin/classic/messenger/voice.png (../../components/im/themes/voice.png)
skin/classic/messenger/browserRequest.css (mail/browserRequest.css)
skin/classic/messenger/compacttheme.css (mail/compacttheme.css)
skin/classic/messenger/customizeToolbar.css (mail/customizeToolbar.css)
skin/classic/messenger/datetimepicker.css (mail/datetimepicker.css)
skin/classic/messenger/section_collapsed.png (mail/section_collapsed.png)
skin/classic/messenger/section_expanded.png (mail/section_expanded.png)
+ skin/classic/messenger/listbox.css (mail/listbox.css)
skin/classic/messenger/messageBody.css (mail/messageBody.css)
skin/classic/messenger/messageHeader.css (mail/messageHeader.css)
skin/classic/messenger/messageIcons.css (mail/messageIcons.css)
skin/classic/messenger/messageQuotes.css (mail/messageQuotes.css)
skin/classic/messenger/messenger.css (mail/messenger.css)
skin/classic/messenger/numberbox.css (mail/numberbox.css)
skin/classic/messenger/attachmentList.css (mail/attachmentList.css)
skin/classic/messenger/imageFilters.svg (mail/imageFilters.svg)
new file mode 100644
--- /dev/null
+++ b/mail/themes/windows/mail/listbox.css
@@ -0,0 +1,215 @@
+/* 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/. */
+
+/* ===== listbox.css =======================================================
+ == Styles used by XUL listbox-related elements.
+ ======================================================================= */
+
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: listbox ::::: */
+
+listbox {
+ -moz-appearance: listbox;
+ margin: 2px 4px;
+ color: -moz-FieldText;
+}
+
+listbox[disabled="true"] {
+ color: GrayText;
+}
+
+/* ::::: listitem ::::: */
+
+listitem {
+ border: 1px solid transparent;
+}
+
+listbox:focus > listitem[selected="true"][current="true"] {
+ outline: 1px dotted #F3D982;
+}
+
+listbox:focus > listitem[current="true"] {
+ outline: 1px dotted Highlight;
+ outline-offset: -1px;
+}
+
+listitem[selected="true"] {
+ background-color: -moz-cellhighlight;
+ color: -moz-cellhighlighttext;
+}
+
+listbox:focus > listitem[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+ -moz-appearance: treeheadercell;
+ -moz-box-align: center;
+ color: -moz-DialogText;
+ padding: 0 4px;
+}
+
+listheader[sortable="true"]:hover:active {
+ padding-top: 1px;
+ padding-bottom: 0px;
+ padding-inline-start: 5px;
+ padding-inline-end: 4px;
+}
+
+.listheader-icon {
+ margin-inline-end: 2px;
+}
+
+.listheader-label {
+ margin: 0px !important;
+}
+
+/* ..... sort direction icon ..... */
+
+.listheader-sortdirection {
+ list-style-image: none;
+}
+
+.listheader-sortdirection[sortDirection="ascending"] {
+ list-style-image: url("chrome://global/skin/tree/sort-asc.png");
+}
+
+.listheader-sortdirection[sortDirection="descending"] {
+ list-style-image: url("chrome://global/skin/tree/sort-dsc.png");
+}
+
+@media (-moz-windows-classic) {
+ .listheader-sortdirection[sortDirection="ascending"] {
+ list-style-image: url("chrome://global/skin/tree/sort-asc-classic.png");
+ }
+
+ .listheader-sortdirection[sortDirection="descending"] {
+ list-style-image: url("chrome://global/skin/tree/sort-dsc-classic.png");
+ }
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+ margin: 0px !important;
+ padding-top: 0px;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ padding-inline-end: 0px;
+ white-space: nowrap;
+}
+
+.listcell-label[disabled="true"] {
+ color: GrayText;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+ -moz-appearance: checkbox;
+ -moz-box-align: center;
+ margin: 0px 2px;
+ border: 1px solid -moz-DialogText;
+ min-width: 13px;
+ min-height: 13px;
+ background: -moz-Field no-repeat 50% 50%;
+}
+
+@media (-moz-windows-default-theme) {
+ listitem {
+ --listitem-selectedColor: rgb(217,217,217);
+ --listitem-selectedBorder: var(--listitem-selectedColor);
+ --listitem-selectedBottomBorder: rgb(204,204,204);
+ --listitem-selectedBackground: var(--listitem-selectedColor);
+ --listitem-selectedImage: none;
+ --listitem-selectedCurrentBorder: rgb(123,195,255);
+ --listitem-selectedFocusColor: rgb(205,232,255);
+ --listitem-selectedFocusBorder: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusBottomBorder: rgb(165,214,255);
+ --listitem-selectedFocusBackground: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusImage: none;
+ --listitem-selectedFocusCurrentBorder: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusCurrentBottomBorder: var(--listitem-selectedFocusBottomBorder);
+ --listitem-selectedFocusCurrentBackground: var(--listitem-selectedFocusColor);
+
+ color: -moz-FieldText;
+ margin-inline-start: 1px;
+ margin-inline-end: 1px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ border-width: 1px;
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
+ }
+
+ listitem[selected="true"] {
+ border-top-color: var(--listitem-selectedBorder);
+ border-right-color: var(--listitem-selectedBorder);
+ border-left-color: var(--listitem-selectedBorder);
+ border-bottom-color: var(--listitem-selectedBottomBorder);
+ background-image: var(--listitem-selectedImage);
+ background-color: var(--listitem-selectedBackground);
+ color: -moz-DialogText;
+ }
+
+ listbox:focus > listitem[selected="true"] {
+ border-top-color: var(--listitem-selectedFocusBorder);
+ border-right-color: var(--listitem-selectedFocusBorder);
+ border-left-color: var(--listitem-selectedFocusBorder);
+ border-bottom-color: var(--listitem-selectedFocusBottomBorder);
+ background-image: var(--listitem-selectedFocusImage);
+ background-color: var(--listitem-selectedFocusBackground);
+ color: -moz-DialogText;
+ }
+
+ listbox:focus > listitem[current="true"] {
+ border-color: var(--listitem-selectedCurrentBorder);
+ outline: none;
+ }
+
+ listbox:focus > listitem[selected="true"][current="true"] {
+ border-top-color: var(--listitem-selectedFocusCurrentBorder);
+ border-right-color: var(--listitem-selectedFocusCurrentBorder);
+ border-left-color: var(--listitem-selectedFocusCurrentBorder);
+ border-bottom-color: var(--listitem-selectedFocusCurrentBottomBorder);
+ background-color: var(--listitem-selectedFocusCurrentBackground);
+ outline: none;
+ }
+
+ @media (-moz-os-version: windows-win7) {
+ listitem {
+ --listitem-selectedBottomBorder: var(--listitem-selectedColor);
+ --listitem-selectedBackground: rgba(190,190,190,.15);
+ --listitem-selectedImage: linear-gradient(rgba(190,190,190,.1), rgba(190,190,190,.4));
+ --listitem-selectedCurrentBorder: rgb(125,162,206);
+ --listitem-selectedFocusColor: rgb(132,172,221);
+ --listitem-selectedFocusBottomBorder: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusBackground: rgba(131,183,249,.02);
+ --listitem-selectedFocusImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.375));
+ --listitem-selectedFocusCurrentBackground: rgba(131,183,249,.15);
+
+ border-radius: 3px;
+ box-shadow: inset 0 0 0 1px rgba(255,255,255,.4), inset 0 -1px 0 1px rgba(255,255,255,.2);
+ }
+ }
+
+ @media (-moz-os-version: windows-win8) {
+ listitem {
+ --listitem-selectedBottomBorder: var(--listitem-selectedColor);
+ --listitem-selectedBackground: rgba(190,190,190,.15);
+ --listitem-selectedImage: linear-gradient(rgba(190,190,190,.4), rgba(190,190,190,.4));
+ --listitem-selectedCurrentBorder: rgb(125,162,206);
+ --listitem-selectedFocusColor: rgb(132,172,221);
+ --listitem-selectedFocusBottomBorder: var(--listitem-selectedFocusColor);
+ --listitem-selectedFocusBackground: rgba(131,183,249,.02);
+ --listitem-selectedFocusImage: linear-gradient(rgba(131,183,249,.375), rgba(131,183,249,.375));
+ --listitem-selectedFocusCurrentBackground: rgba(131,183,249,.15);
+ }
+ }
+}