/toolkit/content/widgets/listbox.xml

http://github.com/zpao/v8monkey · XML · 1245 lines · 979 code · 148 blank · 118 comment · 0 complexity · 3010321d759d8264e5fc106c5a3a2b01 MD5 · raw file

  1. <?xml version="1.0"?>
  2. <!-- ***** BEGIN LICENSE BLOCK *****
  3. - Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. -
  5. - The contents of this file are subject to the Mozilla Public License Version
  6. - 1.1 (the "License"); you may not use this file except in compliance with
  7. - the License. You may obtain a copy of the License at
  8. - http://www.mozilla.org/MPL/
  9. -
  10. - Software distributed under the License is distributed on an "AS IS" basis,
  11. - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. - for the specific language governing rights and limitations under the
  13. - License.
  14. -
  15. - The Original Code is toolkit code.
  16. -
  17. - The Initial Developer of the Original Code is
  18. - Netscape Communications Corporation.
  19. - Portions created by the Initial Developer are Copyright (C) 2002
  20. - the Initial Developer. All Rights Reserved.
  21. -
  22. - Contributor(s):
  23. - Joe Hewitt <hewitt@netscape.com> (original author)
  24. - Simon Bünzli <zeniko@gmail.com>
  25. - Alexander Surkov <surkov.alexander@gmail.com>
  26. -
  27. - Alternatively, the contents of this file may be used under the terms of
  28. - either the GNU General Public License Version 2 or later (the "GPL"), or
  29. - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. - in which case the provisions of the GPL or the LGPL are applicable instead
  31. - of those above. If you wish to allow use of your version of this file only
  32. - under the terms of either the GPL or the LGPL, and not to allow others to
  33. - use your version of this file under the terms of the MPL, indicate your
  34. - decision by deleting the provisions above and replace them with the notice
  35. - and other provisions required by the GPL or the LGPL. If you do not delete
  36. - the provisions above, a recipient may use your version of this file under
  37. - the terms of any one of the MPL, the GPL or the LGPL.
  38. -
  39. - ***** END LICENSE BLOCK ***** -->
  40. <bindings id="listboxBindings"
  41. xmlns="http://www.mozilla.org/xbl"
  42. xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  43. xmlns:xbl="http://www.mozilla.org/xbl">
  44. <!--
  45. Interface binding that is base for bindings of xul:listbox and
  46. xul:richlistbox elements. This binding assumes that successors bindings
  47. will implement the following properties and methods:
  48. /** Return the number of items */
  49. readonly itemCount
  50. /** Return index of given item
  51. * @param aItem - given item element */
  52. getIndexOfItem(aItem)
  53. /** Return item at given index
  54. * @param aIndex - index of item element */
  55. getItemAtIndex(aIndex)
  56. /** Return count of item elements */
  57. getRowCount()
  58. /** Return count of visible item elements */
  59. getNumberOfVisibleRows()
  60. /** Return index of first visible item element */
  61. getIndexOfFirstVisibleRow()
  62. /** Return true if item of given index is visible
  63. * @param aIndex - index of item element
  64. *
  65. * @note XXX: this method should be removed after bug 364612 is fixed
  66. */
  67. ensureIndexIsVisible(aIndex)
  68. /** Return true if item element is visible
  69. * @param aElement - given item element */
  70. ensureElementIsVisible(aElement)
  71. /** Scroll list control to make visible item of given index
  72. * @param aIndex - index of item element
  73. *
  74. * @note XXX: this method should be removed after bug 364612 is fixed
  75. */
  76. scrollToIndex(aIndex)
  77. /** Create item element and append it to the end of listbox
  78. * @param aLabel - label of new item element
  79. * @param aValue - value of new item element */
  80. appendItem(aLabel, aValue)
  81. /** Create item element and insert it to given position
  82. * @param aIndex - insertion position
  83. * @param aLabel - label of new item element
  84. * @param aValue - value of new item element */
  85. insertItemAt(aIndex, aLabel, aValue)
  86. /** Scroll up/down one page
  87. * @param aDirection - specifies scrolling direction, should be either -1 or 1
  88. * @return the number of elements the selection scrolled
  89. */
  90. scrollOnePage(aDirection)
  91. /** Fire "select" event */
  92. _fireOnSelect()
  93. -->
  94. <binding id="listbox-base"
  95. extends="chrome://global/content/bindings/general.xml#basecontrol">
  96. <implementation implements="nsIDOMXULMultiSelectControlElement, nsIAccessibleProvider">
  97. <field name="_lastKeyTime">0</field>
  98. <field name="_incrementalString">""</field>
  99. <!-- nsIAccessibleProvider -->
  100. <property name="accessibleType" readonly="true">
  101. <getter>
  102. return Components.interfaces.nsIAccessibleProvider.XULListbox;
  103. </getter>
  104. </property>
  105. <!-- nsIDOMXULSelectControlElement -->
  106. <property name="selectedItem"
  107. onset="this.selectItem(val);">
  108. <getter>
  109. <![CDATA[
  110. return this.selectedItems.length > 0 ? this.selectedItems[0] : null;
  111. ]]>
  112. </getter>
  113. </property>
  114. <property name="selectedIndex">
  115. <getter>
  116. <![CDATA[
  117. if (this.selectedItems.length > 0)
  118. return this.getIndexOfItem(this.selectedItems[0]);
  119. return -1;
  120. ]]>
  121. </getter>
  122. <setter>
  123. <![CDATA[
  124. if (val >= 0) {
  125. this.selectItem(this.getItemAtIndex(val));
  126. } else {
  127. this.clearSelection();
  128. this.currentItem = null;
  129. }
  130. ]]>
  131. </setter>
  132. </property>
  133. <property name="value">
  134. <getter>
  135. <![CDATA[
  136. if (this.selectedItems.length > 0)
  137. return this.selectedItem.value;
  138. return null;
  139. ]]>
  140. </getter>
  141. <setter>
  142. <![CDATA[
  143. var kids = this.getElementsByAttribute("value", val);
  144. if (kids && kids.item(0))
  145. this.selectItem(kids[0]);
  146. return val;
  147. ]]>
  148. </setter>
  149. </property>
  150. <method name="removeItemAt">
  151. <parameter name="index"/>
  152. <body>
  153. <![CDATA[
  154. var remove = this.getItemAtIndex(index);
  155. if (remove)
  156. this.removeChild(remove);
  157. return remove;
  158. ]]>
  159. </body>
  160. </method>
  161. <!-- nsIDOMXULMultiSelectControlElement -->
  162. <property name="selType"
  163. onget="return this.getAttribute('seltype');"
  164. onset="this.setAttribute('seltype', val); return val;"/>
  165. <property name="currentItem" onget="return this._currentItem;">
  166. <setter>
  167. if (this._currentItem == val)
  168. return val;
  169. if (this._currentItem)
  170. this._currentItem.current = false;
  171. this._currentItem = val;
  172. if (val)
  173. val.current = true;
  174. return val;
  175. </setter>
  176. </property>
  177. <property name="currentIndex">
  178. <getter>
  179. return this.currentItem ? this.getIndexOfItem(this.currentItem) : -1;
  180. </getter>
  181. <setter>
  182. <![CDATA[
  183. if (val >= 0)
  184. this.currentItem = this.getItemAtIndex(val);
  185. else
  186. this.currentItem = null;
  187. ]]>
  188. </setter>
  189. </property>
  190. <field name="selectedItems">[]</field>
  191. <method name="addItemToSelection">
  192. <parameter name="aItem"/>
  193. <body>
  194. <![CDATA[
  195. if (this.selType != "multiple" && this.selectedCount)
  196. return;
  197. if (aItem.selected)
  198. return;
  199. this.selectedItems.push(aItem);
  200. aItem.selected = true;
  201. this._fireOnSelect();
  202. ]]>
  203. </body>
  204. </method>
  205. <method name="removeItemFromSelection">
  206. <parameter name="aItem"/>
  207. <body>
  208. <![CDATA[
  209. if (!aItem.selected)
  210. return;
  211. for (var i = 0; i < this.selectedItems.length; ++i) {
  212. if (this.selectedItems[i] == aItem) {
  213. this.selectedItems.splice(i, 1);
  214. aItem.selected = false;
  215. break;
  216. }
  217. }
  218. this._fireOnSelect();
  219. ]]>
  220. </body>
  221. </method>
  222. <method name="toggleItemSelection">
  223. <parameter name="aItem"/>
  224. <body>
  225. <![CDATA[
  226. if (aItem.selected)
  227. this.removeItemFromSelection(aItem);
  228. else
  229. this.addItemToSelection(aItem);
  230. ]]>
  231. </body>
  232. </method>
  233. <method name="selectItem">
  234. <parameter name="aItem"/>
  235. <body>
  236. <![CDATA[
  237. if (!aItem)
  238. return;
  239. if (this.selectedItems.length == 1 && this.selectedItems[0] == aItem)
  240. return;
  241. this._selectionStart = null;
  242. var suppress = this._suppressOnSelect;
  243. this._suppressOnSelect = true;
  244. this.clearSelection();
  245. this.addItemToSelection(aItem);
  246. this.currentItem = aItem;
  247. this._suppressOnSelect = suppress;
  248. this._fireOnSelect();
  249. ]]>
  250. </body>
  251. </method>
  252. <method name="selectItemRange">
  253. <parameter name="aStartItem"/>
  254. <parameter name="aEndItem"/>
  255. <body>
  256. <![CDATA[
  257. if (this.selType != "multiple")
  258. return;
  259. if (!aStartItem)
  260. aStartItem = this._selectionStart ?
  261. this._selectionStart : this.currentItem;
  262. if (!aStartItem)
  263. aStartItem = aEndItem;
  264. var suppressSelect = this._suppressOnSelect;
  265. this._suppressOnSelect = true;
  266. this._selectionStart = aStartItem;
  267. var currentItem;
  268. var startIndex = this.getIndexOfItem(aStartItem);
  269. var endIndex = this.getIndexOfItem(aEndItem);
  270. if (endIndex < startIndex) {
  271. currentItem = aEndItem;
  272. aEndItem = aStartItem;
  273. aStartItem = currentItem;
  274. } else {
  275. currentItem = aStartItem;
  276. }
  277. while (currentItem) {
  278. this.addItemToSelection(currentItem);
  279. if (currentItem == aEndItem) {
  280. currentItem = this.getNextItem(currentItem, 1);
  281. break;
  282. }
  283. currentItem = this.getNextItem(currentItem, 1);
  284. }
  285. // Clear around new selection
  286. // Don't use clearSelection() because it causes a lot of noise
  287. // with respect to selection removed notifications used by the
  288. // accessibility API support.
  289. var userSelecting = this._userSelecting;
  290. this._userSelecting = false; // that's US automatically unselecting
  291. for (; currentItem; currentItem = this.getNextItem(currentItem, 1))
  292. this.removeItemFromSelection(currentItem);
  293. for (currentItem = this.getItemAtIndex(0); currentItem != aStartItem;
  294. currentItem = this.getNextItem(currentItem, 1))
  295. this.removeItemFromSelection(currentItem);
  296. this._userSelecting = userSelecting;
  297. this._suppressOnSelect = suppressSelect;
  298. this._fireOnSelect();
  299. ]]>
  300. </body>
  301. </method>
  302. <method name="selectAll">
  303. <body>
  304. this._selectionStart = null;
  305. var suppress = this._suppressOnSelect;
  306. this._suppressOnSelect = true;
  307. var item = this.getItemAtIndex(0);
  308. while (item) {
  309. this.addItemToSelection(item);
  310. item = this.getNextItem(item, 1);
  311. }
  312. this._suppressOnSelect = suppress;
  313. this._fireOnSelect();
  314. </body>
  315. </method>
  316. <method name="invertSelection">
  317. <body>
  318. this._selectionStart = null;
  319. var suppress = this._suppressOnSelect;
  320. this._suppressOnSelect = true;
  321. var item = this.getItemAtIndex(0);
  322. while (item) {
  323. if (item.selected)
  324. this.removeItemFromSelection(item);
  325. else
  326. this.addItemToSelection(item);
  327. item = this.getNextItem(item, 1);
  328. }
  329. this._suppressOnSelect = suppress;
  330. this._fireOnSelect();
  331. </body>
  332. </method>
  333. <method name="clearSelection">
  334. <body>
  335. <![CDATA[
  336. if (this.selectedItems) {
  337. for (var i = this.selectedItems.length - 1; i >= 0; --i)
  338. this.selectedItems[i].selected = false;
  339. this.selectedItems.length = 0;
  340. }
  341. this._selectionStart = null;
  342. this._fireOnSelect();
  343. ]]>
  344. </body>
  345. </method>
  346. <property name="selectedCount" readonly="true"
  347. onget="return this.selectedItems.length;"/>
  348. <method name="getSelectedItem">
  349. <parameter name="aIndex"/>
  350. <body>
  351. <![CDATA[
  352. return aIndex < this.selectedItems.length ?
  353. this.selectedItems[aIndex] : null;
  354. ]]>
  355. </body>
  356. </method>
  357. <!-- Other public members -->
  358. <property name="disableKeyNavigation"
  359. onget="return this.hasAttribute('disableKeyNavigation');">
  360. <setter>
  361. if (val)
  362. this.setAttribute("disableKeyNavigation", "true");
  363. else
  364. this.removeAttribute("disableKeyNavigation");
  365. return val;
  366. </setter>
  367. </property>
  368. <property name="suppressOnSelect"
  369. onget="return this.getAttribute('suppressonselect') == 'true';"
  370. onset="this.setAttribute('suppressonselect', val);"/>
  371. <property name="_selectDelay"
  372. onset="this.setAttribute('_selectDelay', val);"
  373. onget="return this.getAttribute('_selectDelay') || 50;"/>
  374. <method name="timedSelect">
  375. <parameter name="aItem"/>
  376. <parameter name="aTimeout"/>
  377. <body>
  378. <![CDATA[
  379. var suppress = this._suppressOnSelect;
  380. if (aTimeout != -1)
  381. this._suppressOnSelect = true;
  382. this.selectItem(aItem);
  383. this._suppressOnSelect = suppress;
  384. if (aTimeout != -1) {
  385. if (this._selectTimeout)
  386. window.clearTimeout(this._selectTimeout);
  387. this._selectTimeout =
  388. window.setTimeout(this._selectTimeoutHandler, aTimeout, this);
  389. }
  390. ]]>
  391. </body>
  392. </method>
  393. <method name="moveByOffset">
  394. <parameter name="aOffset"/>
  395. <parameter name="aIsSelecting"/>
  396. <parameter name="aIsSelectingRange"/>
  397. <body>
  398. <![CDATA[
  399. if ((aIsSelectingRange || !aIsSelecting) &&
  400. this.selType != "multiple")
  401. return;
  402. var newIndex = this.currentIndex + aOffset;
  403. if (newIndex < 0)
  404. newIndex = 0;
  405. var numItems = this.getRowCount();
  406. if (newIndex > numItems - 1)
  407. newIndex = numItems - 1;
  408. var newItem = this.getItemAtIndex(newIndex);
  409. // make sure that the item is actually visible/selectable
  410. if (this._userSelecting && newItem && !this._canUserSelect(newItem))
  411. newItem =
  412. aOffset > 0 ? this.getNextItem(newItem, 1) || this.getPreviousItem(newItem, 1) :
  413. this.getPreviousItem(newItem, 1) || this.getNextItem(newItem, 1);
  414. if (newItem) {
  415. this.ensureIndexIsVisible(this.getIndexOfItem(newItem));
  416. if (aIsSelectingRange)
  417. this.selectItemRange(null, newItem);
  418. else if (aIsSelecting)
  419. this.selectItem(newItem);
  420. this.currentItem = newItem;
  421. }
  422. ]]>
  423. </body>
  424. </method>
  425. <!-- Private -->
  426. <method name="getNextItem">
  427. <parameter name="aStartItem"/>
  428. <parameter name="aDelta"/>
  429. <body>
  430. <![CDATA[
  431. while (aStartItem) {
  432. aStartItem = aStartItem.nextSibling;
  433. if (aStartItem && aStartItem instanceof
  434. Components.interfaces.nsIDOMXULSelectControlItemElement &&
  435. (!this._userSelecting || this._canUserSelect(aStartItem))) {
  436. --aDelta;
  437. if (aDelta == 0)
  438. return aStartItem;
  439. }
  440. }
  441. return null;
  442. ]]></body>
  443. </method>
  444. <method name="getPreviousItem">
  445. <parameter name="aStartItem"/>
  446. <parameter name="aDelta"/>
  447. <body>
  448. <![CDATA[
  449. while (aStartItem) {
  450. aStartItem = aStartItem.previousSibling;
  451. if (aStartItem && aStartItem instanceof
  452. Components.interfaces.nsIDOMXULSelectControlItemElement &&
  453. (!this._userSelecting || this._canUserSelect(aStartItem))) {
  454. --aDelta;
  455. if (aDelta == 0)
  456. return aStartItem;
  457. }
  458. }
  459. return null;
  460. ]]>
  461. </body>
  462. </method>
  463. <method name="_moveByOffsetFromUserEvent">
  464. <parameter name="aOffset"/>
  465. <parameter name="aEvent"/>
  466. <body>
  467. <![CDATA[
  468. if (!aEvent.defaultPrevented) {
  469. this._userSelecting = true;
  470. this._mayReverse = true;
  471. this.moveByOffset(aOffset, !aEvent.ctrlKey, aEvent.shiftKey);
  472. this._userSelecting = false;
  473. this._mayReverse = false;
  474. aEvent.preventDefault();
  475. }
  476. ]]>
  477. </body>
  478. </method>
  479. <method name="_canUserSelect">
  480. <parameter name="aItem"/>
  481. <body>
  482. <![CDATA[
  483. var style = document.defaultView.getComputedStyle(aItem, "");
  484. return style.display != "none" && style.visibility == "visible";
  485. ]]>
  486. </body>
  487. </method>
  488. <method name="_selectTimeoutHandler">
  489. <parameter name="aMe"/>
  490. <body>
  491. aMe._fireOnSelect();
  492. aMe._selectTimeout = null;
  493. </body>
  494. </method>
  495. <field name="_suppressOnSelect">false</field>
  496. <field name="_userSelecting">false</field>
  497. <field name="_mayReverse">false</field>
  498. <field name="_selectTimeout">null</field>
  499. <field name="_currentItem">null</field>
  500. <field name="_selectionStart">null</field>
  501. </implementation>
  502. <handlers>
  503. <handler event="keypress" keycode="VK_UP" modifiers="control shift any"
  504. action="this._moveByOffsetFromUserEvent(-1, event);"
  505. group="system"/>
  506. <handler event="keypress" keycode="VK_DOWN" modifiers="control shift any"
  507. action="this._moveByOffsetFromUserEvent(1, event);"
  508. group="system"/>
  509. <handler event="keypress" keycode="VK_HOME" modifiers="control shift any"
  510. group="system">
  511. <![CDATA[
  512. this._mayReverse = true;
  513. this._moveByOffsetFromUserEvent(-this.currentIndex, event);
  514. this._mayReverse = false;
  515. ]]>
  516. </handler>
  517. <handler event="keypress" keycode="VK_END" modifiers="control shift any"
  518. group="system">
  519. <![CDATA[
  520. this._mayReverse = true;
  521. this._moveByOffsetFromUserEvent(this.getRowCount() - this.currentIndex - 1, event);
  522. this._mayReverse = false;
  523. ]]>
  524. </handler>
  525. <handler event="keypress" keycode="VK_PAGE_UP" modifiers="control shift any"
  526. group="system">
  527. <![CDATA[
  528. this._mayReverse = true;
  529. this._moveByOffsetFromUserEvent(this.scrollOnePage(-1), event);
  530. this._mayReverse = false;
  531. ]]>
  532. </handler>
  533. <handler event="keypress" keycode="VK_PAGE_DOWN" modifiers="control shift any"
  534. group="system">
  535. <![CDATA[
  536. this._mayReverse = true;
  537. this._moveByOffsetFromUserEvent(this.scrollOnePage(1), event);
  538. this._mayReverse = false;
  539. ]]>
  540. </handler>
  541. <handler event="keypress" key=" " modifiers="control" phase="target">
  542. <![CDATA[
  543. if (this.currentItem && this.selType == "multiple")
  544. this.toggleItemSelection(this.currentItem);
  545. ]]>
  546. </handler>
  547. <handler event="focus">
  548. <![CDATA[
  549. if (this.getRowCount() > 0) {
  550. if (this.currentIndex == -1) {
  551. this.currentIndex = this.getIndexOfFirstVisibleRow();
  552. }
  553. else {
  554. this.currentItem._fireEvent("DOMMenuItemActive");
  555. }
  556. }
  557. this._lastKeyTime = 0;
  558. ]]>
  559. </handler>
  560. <handler event="keypress" phase="target">
  561. <![CDATA[
  562. if (this.disableKeyNavigation || !event.charCode ||
  563. event.altKey || event.ctrlKey || event.metaKey)
  564. return;
  565. if (event.timeStamp - this._lastKeyTime > 1000)
  566. this._incrementalString = "";
  567. var key = String.fromCharCode(event.charCode).toLowerCase();
  568. this._incrementalString += key;
  569. this._lastKeyTime = event.timeStamp;
  570. // If all letters in the incremental string are the same, just
  571. // try to match the first one
  572. var incrementalString = /^(.)\1+$/.test(this._incrementalString) ?
  573. RegExp.$1 : this._incrementalString;
  574. var length = incrementalString.length;
  575. var rowCount = this.getRowCount();
  576. var l = this.selectedItems.length;
  577. var start = l > 0 ? this.getIndexOfItem(this.selectedItems[l - 1]) : -1;
  578. // start from the first element if none was selected or from the one
  579. // following the selected one if it's a new or a repeated-letter search
  580. if (start == -1 || length == 1)
  581. start++;
  582. for (var i = 0; i < rowCount; i++) {
  583. var k = (start + i) % rowCount;
  584. var listitem = this.getItemAtIndex(k);
  585. if (!this._canUserSelect(listitem))
  586. continue;
  587. // allow richlistitems to specify the string being searched for
  588. var searchText = "searchLabel" in listitem ? listitem.searchLabel :
  589. listitem.getAttribute("label"); // (see also bug 250123)
  590. searchText = searchText.substring(0, length).toLowerCase();
  591. if (searchText == incrementalString) {
  592. this.ensureIndexIsVisible(k);
  593. this.timedSelect(listitem, this._selectDelay);
  594. break;
  595. }
  596. }
  597. ]]>
  598. </handler>
  599. </handlers>
  600. </binding>
  601. <!-- Binding for xul:listbox element.
  602. -->
  603. <binding id="listbox"
  604. extends="#listbox-base">
  605. <resources>
  606. <stylesheet src="chrome://global/skin/listbox.css"/>
  607. </resources>
  608. <content>
  609. <children includes="listcols">
  610. <xul:listcols>
  611. <xul:listcol flex="1"/>
  612. </xul:listcols>
  613. </children>
  614. <xul:listrows>
  615. <children includes="listhead"/>
  616. <xul:listboxbody xbl:inherits="rows,size,minheight">
  617. <children includes="listitem"/>
  618. </xul:listboxbody>
  619. </xul:listrows>
  620. </content>
  621. <implementation>
  622. <!-- ///////////////// public listbox members ///////////////// -->
  623. <property name="listBoxObject"
  624. onget="return this.boxObject.QueryInterface(Components.interfaces.nsIListBoxObject);"
  625. readonly="true"/>
  626. <!-- ///////////////// private listbox members ///////////////// -->
  627. <method name="_fireOnSelect">
  628. <body>
  629. <![CDATA[
  630. if (!this._suppressOnSelect && !this.suppressOnSelect) {
  631. var event = document.createEvent("Events");
  632. event.initEvent("select", true, true);
  633. this.dispatchEvent(event);
  634. }
  635. ]]>
  636. </body>
  637. </method>
  638. <constructor>
  639. <![CDATA[
  640. var count = this.itemCount;
  641. for (var index = 0; index < count; index++) {
  642. var item = this.getItemAtIndex(index);
  643. if (item.getAttribute("selected") == "true")
  644. this.selectedItems.push(item);
  645. }
  646. ]]>
  647. </constructor>
  648. <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
  649. <method name="appendItem">
  650. <parameter name="aLabel"/>
  651. <parameter name="aValue"/>
  652. <body>
  653. const XULNS =
  654. "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  655. var item = this.ownerDocument.createElementNS(XULNS, "listitem");
  656. item.setAttribute("label", aLabel);
  657. item.setAttribute("value", aValue);
  658. this.appendChild(item);
  659. return item;
  660. </body>
  661. </method>
  662. <method name="insertItemAt">
  663. <parameter name="aIndex"/>
  664. <parameter name="aLabel"/>
  665. <parameter name="aValue"/>
  666. <body>
  667. const XULNS =
  668. "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  669. var item = this.ownerDocument.createElementNS(XULNS, "listitem");
  670. item.setAttribute("label", aLabel);
  671. item.setAttribute("value", aValue);
  672. var before = this.getItemAtIndex(aIndex);
  673. if (before)
  674. this.insertBefore(item, before);
  675. else
  676. this.appendChild(item);
  677. return item;
  678. </body>
  679. </method>
  680. <property name="itemCount" readonly="true"
  681. onget="return this.listBoxObject.getRowCount()"/>
  682. <!-- ///////////////// nsIListBoxObject ///////////////// -->
  683. <method name="getIndexOfItem">
  684. <parameter name="item"/>
  685. <body>
  686. return this.listBoxObject.getIndexOfItem(item);
  687. </body>
  688. </method>
  689. <method name="getItemAtIndex">
  690. <parameter name="index"/>
  691. <body>
  692. return this.listBoxObject.getItemAtIndex(index);
  693. </body>
  694. </method>
  695. <method name="ensureIndexIsVisible">
  696. <parameter name="index"/>
  697. <body>
  698. return this.listBoxObject.ensureIndexIsVisible(index);
  699. </body>
  700. </method>
  701. <method name="ensureElementIsVisible">
  702. <parameter name="element"/>
  703. <body>
  704. return this.ensureIndexIsVisible(this.listBoxObject.getIndexOfItem(element));
  705. </body>
  706. </method>
  707. <method name="scrollToIndex">
  708. <parameter name="index"/>
  709. <body>
  710. return this.listBoxObject.scrollToIndex(index);
  711. </body>
  712. </method>
  713. <method name="getNumberOfVisibleRows">
  714. <body>
  715. return this.listBoxObject.getNumberOfVisibleRows();
  716. </body>
  717. </method>
  718. <method name="getIndexOfFirstVisibleRow">
  719. <body>
  720. return this.listBoxObject.getIndexOfFirstVisibleRow();
  721. </body>
  722. </method>
  723. <method name="getRowCount">
  724. <body>
  725. return this.listBoxObject.getRowCount();
  726. </body>
  727. </method>
  728. <method name="scrollOnePage">
  729. <parameter name="direction"/> <!-- Must be -1 or 1 -->
  730. <body>
  731. <![CDATA[
  732. var pageOffset = this.getNumberOfVisibleRows() * direction;
  733. // skip over invisible elements - the user won't care about them
  734. for (var i = 0; i != pageOffset; i += direction) {
  735. var item = this.getItemAtIndex(this.currentIndex + i);
  736. if (item && !this._canUserSelect(item))
  737. pageOffset += direction;
  738. }
  739. var newTop = this.getIndexOfFirstVisibleRow() + pageOffset;
  740. if (direction == 1) {
  741. var maxTop = this.getRowCount() - this.getNumberOfVisibleRows();
  742. for (i = this.getRowCount(); i >= 0 && i > maxTop; i--) {
  743. item = this.getItemAtIndex(i);
  744. if (item && !this._canUserSelect(item))
  745. maxTop--;
  746. }
  747. if (newTop >= maxTop)
  748. newTop = maxTop;
  749. }
  750. if (newTop < 0)
  751. newTop = 0;
  752. this.scrollToIndex(newTop);
  753. return pageOffset;
  754. ]]>
  755. </body>
  756. </method>
  757. </implementation>
  758. <handlers>
  759. <handler event="keypress" key=" " phase="target">
  760. <![CDATA[
  761. if (this.currentItem) {
  762. if (this.currentItem.getAttribute("type") != "checkbox")
  763. this.addItemToSelection(this.currentItem);
  764. else if (!this.currentItem.disabled) {
  765. this.currentItem.checked = !this.currentItem.checked;
  766. this.currentItem.doCommand();
  767. }
  768. }
  769. ]]>
  770. </handler>
  771. <handler event="MozSwipeGesture">
  772. <![CDATA[
  773. // Figure out which index to show
  774. let targetIndex = 0;
  775. // Only handle swipe gestures up and down
  776. switch (event.direction) {
  777. case event.DIRECTION_DOWN:
  778. targetIndex = this.itemCount - 1;
  779. // Fall through for actual action
  780. case event.DIRECTION_UP:
  781. this.ensureIndexIsVisible(targetIndex);
  782. break;
  783. }
  784. ]]>
  785. </handler>
  786. </handlers>
  787. </binding>
  788. <binding id="listrows">
  789. <resources>
  790. <stylesheet src="chrome://global/skin/listbox.css"/>
  791. </resources>
  792. <handlers>
  793. <handler event="DOMMouseScroll" phase="capturing">
  794. <![CDATA[
  795. if (event.axis == event.HORIZONTAL_AXIS)
  796. return;
  797. var listBox = this.parentNode.listBoxObject;
  798. var rows = event.detail;
  799. if (rows == UIEvent.SCROLL_PAGE_UP)
  800. rows = -listBox.getNumberOfVisibleRows();
  801. else if (rows == UIEvent.SCROLL_PAGE_DOWN)
  802. rows = listBox.getNumberOfVisibleRows();
  803. listBox.scrollByLines(rows);
  804. event.preventDefault();
  805. ]]>
  806. </handler>
  807. <handler event="MozMousePixelScroll" phase="capturing">
  808. <![CDATA[
  809. if (event.axis == event.HORIZONTAL_AXIS)
  810. return;
  811. // shouldn't be scrolled by pixel scrolling events before a line/page
  812. // scrolling event.
  813. event.preventDefault();
  814. ]]>
  815. </handler>
  816. </handlers>
  817. </binding>
  818. <binding id="listitem"
  819. extends="chrome://global/content/bindings/general.xml#basetext">
  820. <resources>
  821. <stylesheet src="chrome://global/skin/listbox.css"/>
  822. </resources>
  823. <content>
  824. <children>
  825. <xul:listcell xbl:inherits="label,crop,disabled,flexlabel"/>
  826. </children>
  827. </content>
  828. <implementation implements="nsIDOMXULSelectControlItemElement, nsIAccessibleProvider">
  829. <property name="current" onget="return this.getAttribute('current') == 'true';">
  830. <setter><![CDATA[
  831. if (val)
  832. this.setAttribute("current", "true");
  833. else
  834. this.removeAttribute("current");
  835. this._fireEvent(val ? "DOMMenuItemActive" : "DOMMenuItemInactive");
  836. return val;
  837. ]]></setter>
  838. </property>
  839. <!-- ///////////////// nsIAccessibleProvider ///////////////// -->
  840. <property name="accessibleType" readonly="true">
  841. <getter>
  842. <![CDATA[
  843. return Components.interfaces.nsIAccessibleProvider.XULListitem;
  844. ]]>
  845. </getter>
  846. </property>
  847. <!-- ///////////////// nsIDOMXULSelectControlItemElement ///////////////// -->
  848. <property name="value" onget="return this.getAttribute('value');"
  849. onset="this.setAttribute('value', val); return val;"/>
  850. <property name="label" onget="return this.getAttribute('label');"
  851. onset="this.setAttribute('label', val); return val;"/>
  852. <property name="selected" onget="return this.getAttribute('selected') == 'true';">
  853. <setter><![CDATA[
  854. if (val)
  855. this.setAttribute("selected", "true");
  856. else
  857. this.removeAttribute("selected");
  858. return val;
  859. ]]></setter>
  860. </property>
  861. <property name="control">
  862. <getter><![CDATA[
  863. var parent = this.parentNode;
  864. while (parent) {
  865. if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
  866. return parent;
  867. parent = parent.parentNode;
  868. }
  869. return null;
  870. ]]></getter>
  871. </property>
  872. <method name="_fireEvent">
  873. <parameter name="name"/>
  874. <body>
  875. <![CDATA[
  876. var event = document.createEvent("Events");
  877. event.initEvent(name, true, true);
  878. this.dispatchEvent(event);
  879. ]]>
  880. </body>
  881. </method>
  882. </implementation>
  883. <handlers>
  884. <!-- If there is no modifier key, we select on mousedown, not
  885. click, so that drags work correctly. -->
  886. <handler event="mousedown">
  887. <![CDATA[
  888. var control = this.control;
  889. if (!control || control.disabled)
  890. return;
  891. if ((!event.ctrlKey
  892. #ifdef XP_MACOSX
  893. || event.button == 2
  894. #endif
  895. ) && !event.shiftKey && !event.metaKey) {
  896. if (!this.selected) {
  897. control.selectItem(this);
  898. }
  899. control.currentItem = this;
  900. }
  901. ]]>
  902. </handler>
  903. <!-- On a click (up+down on the same item), deselect everything
  904. except this item. -->
  905. <handler event="click" button="0">
  906. <![CDATA[
  907. var control = this.control;
  908. if (!control || control.disabled)
  909. return;
  910. control._userSelecting = true;
  911. if (control.selType != "multiple") {
  912. control.selectItem(this);
  913. }
  914. else if (event.ctrlKey || event.metaKey) {
  915. control.toggleItemSelection(this);
  916. control.currentItem = this;
  917. }
  918. else if (event.shiftKey) {
  919. control.selectItemRange(null, this);
  920. control.currentItem = this;
  921. }
  922. else {
  923. /* We want to deselect all the selected items except what was
  924. clicked, UNLESS it was a right-click. We have to do this
  925. in click rather than mousedown so that you can drag a
  926. selected group of items */
  927. // use selectItemRange instead of selectItem, because this
  928. // doesn't de- and reselect this item if it is selected
  929. control.selectItemRange(this, this);
  930. }
  931. control._userSelecting = false;
  932. ]]>
  933. </handler>
  934. </handlers>
  935. </binding>
  936. <binding id="listitem-iconic"
  937. extends="chrome://global/content/bindings/listbox.xml#listitem">
  938. <content>
  939. <children>
  940. <xul:listcell class="listcell-iconic" xbl:inherits="label,image,crop,disabled,flexlabel"/>
  941. </children>
  942. </content>
  943. </binding>
  944. <binding id="listitem-checkbox"
  945. extends="chrome://global/content/bindings/listbox.xml#listitem">
  946. <content>
  947. <children>
  948. <xul:listcell type="checkbox" xbl:inherits="label,crop,checked,disabled,flexlabel"/>
  949. </children>
  950. </content>
  951. <implementation>
  952. <property name="checked"
  953. onget="return this.getAttribute('checked') == 'true';">
  954. <setter><![CDATA[
  955. if (val)
  956. this.setAttribute('checked', 'true');
  957. else
  958. this.removeAttribute('checked');
  959. var event = document.createEvent('Events');
  960. event.initEvent('CheckboxStateChange', true, true);
  961. this.dispatchEvent(event);
  962. return val;
  963. ]]></setter>
  964. </property>
  965. </implementation>
  966. <handlers>
  967. <handler event="mousedown" button="0">
  968. <![CDATA[
  969. if (!this.disabled && !this.control.disabled) {
  970. this.checked = !this.checked;
  971. this.doCommand();
  972. }
  973. ]]>
  974. </handler>
  975. </handlers>
  976. </binding>
  977. <binding id="listitem-checkbox-iconic"
  978. extends="chrome://global/content/bindings/listbox.xml#listitem-checkbox">
  979. <content>
  980. <children>
  981. <xul:listcell type="checkbox" class="listcell-iconic" xbl:inherits="label,image,crop,checked,disabled,flexlabel"/>
  982. </children>
  983. </content>
  984. </binding>
  985. <binding id="listcell"
  986. extends="chrome://global/content/bindings/general.xml#basecontrol">
  987. <resources>
  988. <stylesheet src="chrome://global/skin/listbox.css"/>
  989. </resources>
  990. <content>
  991. <children>
  992. <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
  993. </children>
  994. </content>
  995. <implementation implements="nsIAccessibleProvider">
  996. <property name="accessibleType" readonly="true">
  997. <getter>
  998. <![CDATA[
  999. // Don't expose xul:listcell as cell accessible until listbox is
  1000. // multicolumn.
  1001. const Ci = Components.interfaces;
  1002. const kNoAccessible = Ci.nsIAccessibleProvider.NoAccessible;
  1003. const kListCellAccessible = Ci.nsIAccessibleProvider.XULListCell;
  1004. var listitem = this.parentNode;
  1005. if (!(listitem instanceof Ci.nsIDOMXULSelectControlItemElement))
  1006. return kNoAccessible;
  1007. var list = listitem.control;
  1008. if (!list)
  1009. return kNoAccessible;
  1010. var listcolsElm = list.getElementsByTagName("listcols")[0];
  1011. if (!listcolsElm)
  1012. return kNoAccessible;
  1013. var listcolElms = listcolsElm.getElementsByTagName("listcol");
  1014. if (listcolElms.length <= 1)
  1015. return kNoAccessible;
  1016. return kListCellAccessible;
  1017. ]]>
  1018. </getter>
  1019. </property>
  1020. </implementation>
  1021. </binding>
  1022. <binding id="listcell-iconic"
  1023. extends="chrome://global/content/bindings/listbox.xml#listcell">
  1024. <content>
  1025. <children>
  1026. <xul:image class="listcell-icon" xbl:inherits="src=image"/>
  1027. <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
  1028. </children>
  1029. </content>
  1030. </binding>
  1031. <binding id="listcell-checkbox"
  1032. extends="chrome://global/content/bindings/listbox.xml#listcell">
  1033. <content>
  1034. <children>
  1035. <xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
  1036. <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
  1037. </children>
  1038. </content>
  1039. </binding>
  1040. <binding id="listcell-checkbox-iconic"
  1041. extends="chrome://global/content/bindings/listbox.xml#listcell-checkbox">
  1042. <content>
  1043. <children>
  1044. <xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
  1045. <xul:image class="listcell-icon" xbl:inherits="src=image"/>
  1046. <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
  1047. </children>
  1048. </content>
  1049. </binding>
  1050. <binding id="listhead">
  1051. <resources>
  1052. <stylesheet src="chrome://global/skin/listbox.css"/>
  1053. </resources>
  1054. <content>
  1055. <xul:listheaditem>
  1056. <children includes="listheader"/>
  1057. </xul:listheaditem>
  1058. </content>
  1059. <implementation implements="nsIAccessibleProvider">
  1060. <property name="accessibleType" readonly="true">
  1061. <getter>
  1062. return Components.interfaces.nsIAccessibleProvider.XULListHead;
  1063. </getter>
  1064. </property>
  1065. </implementation>
  1066. </binding>
  1067. <binding id="listheader" display="xul:button">
  1068. <resources>
  1069. <stylesheet src="chrome://global/skin/listbox.css"/>
  1070. </resources>
  1071. <content>
  1072. <xul:image class="listheader-icon"/>
  1073. <xul:label class="listheader-label" xbl:inherits="value=label,crop" flex="1" crop="right"/>
  1074. <xul:image class="listheader-sortdirection" xbl:inherits="sortDirection"/>
  1075. </content>
  1076. <implementation implements="nsIAccessibleProvider">
  1077. <property name="accessibleType" readonly="true">
  1078. <getter>
  1079. return Components.interfaces.nsIAccessibleProvider.XULListHeader;
  1080. </getter>
  1081. </property>
  1082. </implementation>
  1083. </binding>
  1084. </bindings>