PageRenderTime 63ms CodeModel.GetById 19ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 1ms

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