toolkit/content/widgets/listbox.xml

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

mercurial