toolkit/content/widgets/listbox.xml

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial