Attachment #251637: patch11 for bug #298371

View | Details | Raw Unified | Return to bug 298371 | Differences between
and this patch

Collapse All | Expand All

(-)toolkit/content/widgets/listbox.xml (-357 / +516 lines)
Line     Link Here 
 Lines 1-484    Link Here 
1
<?xml version="1.0"?>
1
<?xml version="1.0"?>
2
2
3
<bindings id="listboxBindings"
3
<!-- ***** BEGIN LICENSE BLOCK *****
4
   xmlns="http://www.mozilla.org/xbl"
4
   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
5
   -
6
   xmlns:xbl="http://www.mozilla.org/xbl">
6
   - The contents of this file are subject to the Mozilla Public License Version
7
  
7
   - 1.1 (the "License"); you may not use this file except in compliance with
8
  <binding id="listbox-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
8
   - the License. You may obtain a copy of the License at
9
    <resources>
9
   - http://www.mozilla.org/MPL/
10
      <stylesheet src="chrome://global/skin/listbox.css"/>
10
   -
11
    </resources>
11
   - Software distributed under the License is distributed on an "AS IS" basis,
12
  </binding>
12
   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
  
13
   - for the specific language governing rights and limitations under the
14
  <binding id="listbox"
14
   - License.
15
           extends="chrome://global/content/bindings/listbox.xml#listbox-base">
15
   -
16
    <content>
16
   - The Original Code is toolkit code.
17
      <children includes="listcols">
17
   -
18
        <xul:listcols>
18
   - The Initial Developer of the Original Code is
19
          <xul:listcol flex="1"/>
19
   - Netscape Communications Corporation.
20
        </xul:listcols>
20
   - Portions created by the Initial Developer are Copyright (C) 2002
21
      </children>
21
   - the Initial Developer. All Rights Reserved.
22
      <xul:listrows>
22
   -
23
        <children includes="listhead"/>
23
   - Contributor(s):
24
        <xul:listboxbody xbl:inherits="rows,size,minheight">
24
   -   Joe Hewitt <hewitt@netscape.com> (original author)
25
          <children includes="listitem"/>
25
   -   Simon Bünzli <zeniko@gmail.com>
26
        </xul:listboxbody> 
26
   -   Alexander Surkov <surkov.alexander@gmail.com>
27
      </xul:listrows>
27
   -
28
    </content>
28
   - Alternatively, the contents of this file may be used under the terms of
29
    
29
   - either the GNU General Public License Version 2 or later (the "GPL"), or
30
    <implementation implements="nsIDOMXULMultiSelectControlElement, nsIAccessibleProvider">
30
   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31
      <field name="_suppressOnSelect">false</field>
31
   - in which case the provisions of the GPL or the LGPL are applicable instead
32
      <field name="_selectionStart">null</field>
32
   - of those above. If you wish to allow use of your version of this file only
33
      <field name="_currentItem">null</field>
33
   - under the terms of either the GPL or the LGPL, and not to allow others to
34
      <field name="_selectTimeout">null</field>
34
   - use your version of this file under the terms of the MPL, indicate your
35
      <field name="_lastKeyTime">0</field>
35
   - decision by deleting the provisions above and replace them with the notice
36
      <field name="_incrementalString">""</field>
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 ***** -->
37
41
38
      <constructor>
42
<bindings id="listboxBindings"
39
      <![CDATA[
43
          xmlns="http://www.mozilla.org/xbl"
40
        var els = this.getElementsByAttribute("selected", "true");
44
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
41
        for (var i = 0; i < els.length; ++i)
45
          xmlns:xbl="http://www.mozilla.org/xbl">
42
          this.selectedItems.push(els[i]);
46
43
      ]]>
47
  <!--
44
      </constructor>
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 methods:
51
52
    /** Return index of given item
53
    * @param aItem - given item element */
54
    getIndexOfItem(aItem)
55
56
    /** Return item at given index
57
    * @param aIndex - index of item element */
58
    getItemAtIndex(aIndex)
59
60
    /** Return count of item elements */
61
    getRowCount()
62
63
    /** Return count of visible item elements */
64
    getNumberOfVisibleRows()
65
66
    /** Return index of first visible item element */
67
    getIndexOfFirstVisibleRow()
68
69
    /** Return true if item of given index is visible
70
     * @param aIndex - index of item element
71
     *
72
     * @note XXX: this method should be removed after bug 364612 is fixed
73
     */
74
    ensureIndexIsVisible(aIndex)
75
76
    /** Return true if item element is visible
77
     * @param aElement - given item element */
78
    ensureElementIsVisible(aElement)
79
80
    /** Scroll list control to make visible item of given index
81
     * @param aIndex - index of item element
82
     *
83
     * @note XXX: this method should be removed after bug 364612 is fixed
84
     */
85
    scrollToIndex(aIndex)
86
87
    /** Create item element and append it to the end of listbox
88
     * @param aLabel - label of new item element
89
     * @param aValue - value of new item element */
90
    appendItem(aLabel, aValue)
91
92
    /** Create item element and insert it to given position
93
     * @param aIndex - insertion position
94
     * @param aLabel - label of new item element
95
     * @param aValue - value of new item element */
96
    insertItemAt(aIndex, aLabel, aValue)
97
98
    /** Scroll up/down one page
99
     * @param aDirection - specifies scrolling direction, should be either -1
100
                           or 1 */
101
    scrollOnePage(aDirection)
102
103
    /** Fire "select" event */
104
    _fireOnSelect()
105
   -->
106
   <binding id="listbox-base"
107
            extends="chrome://global/content/bindings/general.xml#basecontrol">
45
108
46
      <!-- ///////////////// nsIAccessibleProvider ///////////////// -->
109
    <implementation implements="nsIDOMXULMultiSelectControlElement, nsIAccessibleProvider">
47
110
111
    <!-- nsIAccessibleProvider -->
48
      <property name="accessibleType" readonly="true">
112
      <property name="accessibleType" readonly="true">
49
        <getter>
113
        <getter>
50
          <![CDATA[
114
          return Components.interfaces.nsIAccessibleProvider.XULListbox;
51
            return Components.interfaces.nsIAccessibleProvider.XULListbox;
52
          ]]>
53
        </getter>
115
        </getter>
54
      </property>
116
      </property>
55
      
56
      <!-- ///////////////// public listbox members ///////////////// -->
57
58
      <property name="listBoxObject"
59
                onget="return this.boxObject.QueryInterface(Components.interfaces.nsIListBoxObject);"
60
                readonly="true"/>
61
62
      <property name="disableKeyNavigation"
63
                onget="return this.hasAttribute('disableKeyNavigation');"
64
                onset="if (val) this.setAttribute('disableKeyNavigation', 'true');
65
                       else this.removeAttribute('disableKeyNavigation'); return val;"/>
66
67
      <property name="_selectDelay" 
68
                onset="this.setAttribute('_selectDelay', val);"
69
                onget="return this.getAttribute('_selectDelay') || 50;"/>
70
117
71
      <method name="timedSelect">
118
    <!-- nsIDOMXULSelectControlElement -->
72
        <parameter name="item"/>
119
      <property name="selectedItem"
73
        <parameter name="timeout"/>
120
                onset="this.selectItem(val);">
74
        <body>
121
        <getter>
75
        <![CDATA[
122
        <![CDATA[
76
          var suppress = this._suppressOnSelect;
123
          return this.selectedItems.length > 0 ? this.selectedItems[0] : null;
77
          if (timeout != -1)
78
            this._suppressOnSelect = true;
79
          
80
          this.selectItem(item);
81
          
82
          this._suppressOnSelect = suppress;         
83
          
84
          if (timeout != -1) {
85
            if (this._selectTimeout)
86
              window.clearTimeout(this._selectTimeout);
87
              
88
            this._selectTimeout = window.setTimeout(this._selectTimeoutHandler, timeout, this); 
89
          }
90
        ]]>
124
        ]]>
91
        </body>
125
        </getter>
92
      </method>      
126
      </property>
93
94
      <!-- ///////////////// private listbox members ///////////////// -->
95
127
96
      <method name="_fireOnSelect">
128
      <property name="selectedIndex">
97
        <body>
129
        <getter>
98
        <![CDATA[
130
        <![CDATA[
99
          if (!this._suppressOnSelect && this.getAttribute("suppressonselect") != "true") {
131
          if (this.selectedItems.length > 0)
100
            var event = document.createEvent("Events");
132
            return this.getIndexOfItem(this.selectedItems[0]);
101
            event.initEvent("select", true, true);
133
          return -1;
102
            this.dispatchEvent(event);
103
          }
104
        ]]>
134
        ]]>
105
        </body>
135
        </getter>
106
      </method>
136
        <setter>
107
108
      <method name="_selectTimeoutHandler">
109
        <parameter name="me"/>
110
        <body>
111
        <![CDATA[
137
        <![CDATA[
112
          me._fireOnSelect();
113
          me._selectTimeout = null;
114
        ]]>
115
        </body>
116
      </method>      
117
      
118
      <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
119
120
      <property name="selType"
121
                onget="return this.getAttribute('seltype')"
122
                onset="this.setAttribute('seltype', val); return val;"/>
123
124
      <property name="selectedIndex">
125
        <getter><![CDATA[
126
          return this.selectedItems.length > 0 ? this.getIndexOfItem(this.selectedItems[0]) : -1;
127
        ]]></getter>
128
        <setter><![CDATA[
129
          if (val >= 0)
138
          if (val >= 0)
130
            this.selectItem(this.getItemAtIndex(val));
139
            this.selectItem(this.getItemAtIndex(val));
131
          else
140
          else
132
            this.clearSelection();
141
            this.clearSelection();
133
        ]]></setter>
142
        ]]>
134
      </property>
143
        </setter>
135
136
      <field name="selectedItems">[]</field>
137
      <property name="selectedItem">
138
        <getter><![CDATA[
139
          return this.selectedItems.length > 0 ? this.selectedItems[0] : null;
140
        ]]></getter>
141
        <setter><![CDATA[
142
          this.selectItem(val);
143
        ]]></setter>
144
      </property>
144
      </property>
145
145
146
      <property name="value">
146
      <property name="value">
147
        <getter>
147
        <getter>
148
          <![CDATA[
149
            if (this.selectedItems.length > 0)
150
              return this.selectedItem.value;
151
            else
152
              return null;
153
          ]]>
154
        </getter>
155
        <setter>
156
          <![CDATA[
157
            var kids = this.getElementsByAttribute("value", val);
158
            if (kids && kids.item(0))
159
              this.selectItem(kids[0]);
160
            return val;
161
          ]]>
162
        </setter>
163
      </property>
164
165
      <method name="appendItem">
166
        <parameter name="label"/>
167
        <parameter name="value"/>
168
        <body>
169
        <![CDATA[
148
        <![CDATA[
170
          var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
149
          if (this.selectedItems.length > 0)
171
          var item = document.createElementNS(XULNS, "listitem");
150
            return this.selectedItem.value;
172
          item.setAttribute("label", label);
151
          return null;
173
          item.setAttribute("value", value);
174
          this.appendChild(item);
175
          return item;
176
        ]]>
152
        ]]>
177
        </body>
153
        </getter>
178
      </method>
154
        <setter>
179
      
180
      <method name="insertItemAt">
181
        <parameter name="index"/>
182
        <parameter name="label"/>
183
        <parameter name="value"/>
184
        <body>
185
        <![CDATA[
155
        <![CDATA[
186
          var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
156
          var kids = this.getElementsByAttribute("value", val);
187
          var item = document.createElementNS(XULNS, "listitem");
157
          if (kids && kids.item(0))
188
          item.setAttribute("label", label);
158
            this.selectItem(kids[0]);
189
          item.setAttribute("value", value);
159
          return val;
190
          var before = this.getItemAtIndex(index);
191
          if (before)
192
            this.insertBefore(item, before);
193
          else
194
            this.appendChild(item);
195
          return item;
196
        ]]>
160
        ]]>
197
        </body>
161
        </setter>
198
      </method>
162
      </property>
199
163
200
      <method name="removeItemAt">
164
      <method name="removeItemAt">
201
        <parameter name="index"/>
165
        <parameter name="index"/>
202
        <body>
166
        <body>
203
        <![CDATA[
167
        <![CDATA[
204
          var remove = this.getItemAtIndex(index);
168
          var remove = this.getItemAtIndex(index);
205
          if (remove)
169
          if (remove)
206
            this.removeChild(remove);
170
            this.removeChild(remove);
207
          return remove;
171
          return remove;
208
        ]]>
172
        ]]>
209
        </body>
173
        </body>
210
      </method>
174
      </method>
211
      <!-- ///////////////// nsIDOMXULSelectMultipleControlElement ///////////////// -->
175
176
    <!-- nsIDOMXULMultiSelectControlElement -->
177
      <property name="selType"
178
                onget="return this.getAttribute('seltype');"
179
                onset="this.setAttribute('seltype', val); return val;"/>
212
180
213
      <property name="currentItem" onget="return this._currentItem;">
181
      <property name="currentItem" onget="return this._currentItem;">
214
        <setter>
182
        <setter>
215
        <![CDATA[
183
          if (this._currentItem == val)
184
            return val;
185
216
          if (this._currentItem)
186
          if (this._currentItem)
217
            this._currentItem.current = false;
187
            this._currentItem.current = false;
218
          this._currentItem = val;
188
          this._currentItem = val;
219
          if (val) {
189
220
            val.current = true;  
190
          if (val)
221
            var event = document.createEvent("Events");
191
            val.current = true;
222
            event.initEvent("DOMMenuItemActive", true, true);
192
223
            val.dispatchEvent(event);
224
          }
225
          return val;
193
          return val;
226
        ]]>
227
        </setter>
194
        </setter>
228
      </property>
195
      </property>
229
196
230
      <property name="currentIndex">
197
      <property name="currentIndex">
231
        <getter><![CDATA[
198
        <getter>
232
          return this.currentItem ? this.getIndexOfItem(this.currentItem) : -1;
199
          return this.currentItem ? this.getIndexOfItem(this.currentItem) : -1;
233
        ]]></getter>
200
        </getter>
234
        <setter><![CDATA[
201
        <setter>
202
        <![CDATA[
235
          if (val >= 0)
203
          if (val >= 0)
236
            this.currentItem = this.getItemAtIndex(val);
204
            this.currentItem = this.getItemAtIndex(val);
237
          else
205
          else
238
            this.currentItem = null;
206
            this.currentItem = null;
239
        ]]></setter>
207
        ]]>
208
        </setter>
240
      </property>
209
      </property>
241
210
242
      <property name="selectedCount" onget="return this.selectedItems.length;"/>
211
      <field name="selectedItems">[]</field>
243
      
212
244
      <method name="getSelectedItem">
245
        <parameter name="index"/>
246
        <body>
247
        <![CDATA[
248
          return index < this.selectedItems.length ? this.selectedItems[index] : null;
249
        ]]>
250
        </body>
251
      </method>      
252
      
253
      <method name="addItemToSelection">
213
      <method name="addItemToSelection">
254
        <parameter name="item"/>
214
        <parameter name="aItem"/>
255
        <body>
215
        <body>
256
        <![CDATA[
216
        <![CDATA[
257
          if (this.selType != "multiple" && this.selectedCount)
217
          if (this.selType != "multiple" && this.selectedCount)
258
            return;
218
            return;
259
219
260
          if (item.selected)
220
          if (aItem.selected)
261
            return;
221
            return;
262
            
222
263
          this.selectedItems.push(item);
223
          this.selectedItems.push(aItem);
264
          item.selected = true;
224
          aItem.selected = true;
265
          
225
266
          this._fireOnSelect();
226
          this._fireOnSelect();
267
        ]]>
227
        ]]>
268
        </body>
228
        </body>
269
      </method>      
229
      </method>
270
      
230
271
      <method name="removeItemFromSelection">
231
      <method name="removeItemFromSelection">
272
        <parameter name="item"/>
232
        <parameter name="aItem"/>
273
        <body>
233
        <body>
274
        <![CDATA[
234
        <![CDATA[
275
          if (!item.selected)
235
          if (!aItem.selected)
276
            return;
236
            return;
277
            
237
278
          for (var i = 0; i < this.selectedItems.length; ++i) {
238
          for (var i = 0; i < this.selectedItems.length; ++i) {
279
            if (this.selectedItems[i] == item) {
239
            if (this.selectedItems[i] == aItem) {
280
              this.selectedItems.splice(i, 1);
240
              this.selectedItems.splice(i, 1);
281
              item.selected = false;
241
              aItem.selected = false;
282
              break;
242
              break;
283
            }
243
            }
284
          }
244
          }
285
              
245
286
          this._fireOnSelect();
246
          this._fireOnSelect();
287
        ]]>
247
        ]]>
288
        </body>
248
        </body>
289
      </method>      
249
      </method>
290
      
250
291
      <method name="toggleItemSelection">
251
      <method name="toggleItemSelection">
292
        <parameter name="item"/>
252
        <parameter name="aItem"/>
293
        <body>
253
        <body>
294
        <![CDATA[
254
        <![CDATA[
295
          if (item.selected)
255
          if (aItem.selected)
296
            this.removeItemFromSelection(item);
256
            this.removeItemFromSelection(aItem);
297
          else
257
          else
298
            this.addItemToSelection(item);
258
            this.addItemToSelection(aItem);
299
        ]]>
259
        ]]>
300
        </body>
260
        </body>
301
      </method>      
261
      </method>
302
      
262
303
      <method name="selectItem">
263
      <method name="selectItem">
304
        <parameter name="item"/>
264
        <parameter name="aItem"/>
305
        <body>
265
        <body>
306
        <![CDATA[
266
        <![CDATA[
307
          if (!item)
267
          if (!aItem)
308
            return;
268
            return;
309
            
269
310
          if (this.selectedItems.length == 1 && this.selectedItems[0] == item)
270
          if (this.selectedItems.length == 1 && this.selectedItems[0] == aItem)
311
            return;
271
            return;
312
          
272
313
          this._selectionStart = null;
273
          this._selectionStart = null;
314
          
274
315
          var suppress = this._suppressOnSelect;
275
          var suppress = this._suppressOnSelect;
316
          this._suppressOnSelect = true;
276
          this._suppressOnSelect = true;
317
277
318
          this.clearSelection();
278
          this.clearSelection();
319
          this.addItemToSelection(item);
279
          this.addItemToSelection(aItem);
320
          this.currentItem = item;
280
          this.currentItem = aItem;
321
          
281
322
          this._suppressOnSelect = suppress;
282
          this._suppressOnSelect = suppress;
323
          this._fireOnSelect();
283
          this._fireOnSelect();
324
        ]]>
284
        ]]>
325
        </body>
285
        </body>
326
      </method>      
286
      </method>
327
      
287
328
      <method name="selectItemRange">
288
      <method name="selectItemRange">
329
        <parameter name="startItem"/>
289
        <parameter name="aStartItem"/>
330
        <parameter name="endItem"/>
290
        <parameter name="aEndItem"/>
331
        <body>
291
        <body>
332
        <![CDATA[
292
        <![CDATA[
333
          if (this.selType != "multiple")
293
          if (this.selType != "multiple")
334
            return;
294
            return;
335
295
336
          if (!startItem)
296
          if (!aStartItem)
337
            startItem = this._selectionStart ? this._selectionStart : this.currentItem;
297
            aStartItem = this._selectionStart ?
338
          if (!startItem)
298
              this._selectionStart : this.currentItem;
339
            startItem = endItem;
299
340
                      
300
          if (!aStartItem)
301
            aStartItem = aEndItem;
302
341
          var suppressSelect = this._suppressOnSelect;
303
          var suppressSelect = this._suppressOnSelect;
342
          this._suppressOnSelect = true;
304
          this._suppressOnSelect = true;
343
305
344
          this._selectionStart = startItem;
306
          this._selectionStart = aStartItem;
345
          
307
346
          var currentItem;
308
          var currentItem;
347
          var startIndex = this.getIndexOfItem(startItem);
309
          var startIndex = this.getIndexOfItem(aStartItem);
348
          var endIndex = this.getIndexOfItem(endItem);
310
          var endIndex = this.getIndexOfItem(aEndItem);
349
          if (endIndex < startIndex) {
311
          if (endIndex < startIndex) {
350
            currentItem = endItem;
312
            currentItem = aEndItem;
351
            endItem = startItem;
313
            aEndItem = aStartItem;
352
            startItem = currentItem;            
314
            aStartItem = currentItem;
353
          } else {
315
          } else {
354
            currentItem = startItem;
316
            currentItem = aStartItem;
355
          }
317
          }
356
318
357
          while (currentItem) {
319
          while (currentItem) {
358
            if (currentItem.localName == "listitem")
320
            this.addItemToSelection(currentItem);
359
              this.addItemToSelection(currentItem);
321
            if (currentItem == aEndItem) {
360
            if (currentItem == endItem) {
361
              currentItem = this.getNextItem(currentItem, 1);
322
              currentItem = this.getNextItem(currentItem, 1);
362
              break;
323
              break;
363
            }
324
            }
364
            currentItem = this.getNextItem(currentItem, 1);
325
            currentItem = this.getNextItem(currentItem, 1);
365
          }
326
          }
366
327
367
          // Clear around new selection
328
          // Clear around new selection
368
          // Don't use clearSelection() because it causes a lot of noise
329
          // Don't use clearSelection() because it causes a lot of noise
369
          // with respect to selection removed notifications used by the
330
          // with respect to selection removed notifications used by the
370
          // accessibility API support.
331
          // accessibility API support.
371
          for (; currentItem; currentItem = this.getNextItem(currentItem, 1))
332
          for (; currentItem; currentItem = this.getNextItem(currentItem, 1))
372
            this.removeItemFromSelection(currentItem);
333
            this.removeItemFromSelection(currentItem);
373
          for (currentItem = this.getItemAtIndex(0); currentItem != startItem;
334
335
          for (currentItem = this.getItemAtIndex(0); currentItem != aStartItem;
374
               currentItem = this.getNextItem(currentItem, 1))
336
               currentItem = this.getNextItem(currentItem, 1))
375
            this.removeItemFromSelection(currentItem);
337
            this.removeItemFromSelection(currentItem);
376
338
377
          this._suppressOnSelect = suppressSelect;
339
          this._suppressOnSelect = suppressSelect;
378
340
379
          this._fireOnSelect();
341
          this._fireOnSelect();
380
        ]]>
342
        ]]>
381
        </body>
343
        </body>
382
      </method>      
344
      </method>
383
      
345
384
      <method name="selectAll">
346
      <method name="selectAll">
385
        <body>
347
        <body>
386
        <![CDATA[
348
          this._selectionStart = null;
349
387
          var suppress = this._suppressOnSelect;
350
          var suppress = this._suppressOnSelect;
388
          this._suppressOnSelect = true;
351
          this._suppressOnSelect = true;
389
          
352
390
          var item = this.getItemAtIndex(0);
353
          var item = this.getItemAtIndex(0);
391
          while (item) {
354
          while (item) {
392
            this.addItemToSelection(item);
355
            this.addItemToSelection(item);
393
            item = this.getNextItem(item, 1);  
356
            item = this.getNextItem(item, 1);
394
          }
357
          }
395
          
358
396
          this._suppressOnSelect = suppress;
359
          this._suppressOnSelect = suppress;
397
          this._fireOnSelect();
360
          this._fireOnSelect();
398
        ]]>
399
        </body>
361
        </body>
400
      </method>      
362
      </method>
401
      
363
402
      <method name="invertSelection">
364
      <method name="invertSelection">
403
        <body>
365
        <body>
404
        <![CDATA[
366
          this._selectionStart = null;
367
405
          var suppress = this._suppressOnSelect;
368
          var suppress = this._suppressOnSelect;
406
          this._suppressOnSelect = true;
369
          this._suppressOnSelect = true;
407
          
370
408
          var item = this.getItemAtIndex(0);
371
          var item = this.getItemAtIndex(0);
409
          while (item) {
372
          while (item) {
410
            if (item.selected)
373
            if (item.selected)
411
              this.removeItemFromSelection(item);
374
              this.removeItemFromSelection(item);
412
            else
375
            else
413
              this.addItemToSelection(item);
376
              this.addItemToSelection(item);
414
            item = this.getNextItem(item, 1);  
377
            item = this.getNextItem(item, 1);
415
          }
378
          }
416
          
379
417
          this._suppressOnSelect = suppress;
380
          this._suppressOnSelect = suppress;
418
          this._fireOnSelect();
381
          this._fireOnSelect();
419
        ]]>
420
        </body>
382
        </body>
421
      </method>      
383
      </method>
422
      
384
423
      <method name="clearSelection">
385
      <method name="clearSelection">
424
        <body>
386
        <body>
425
        <![CDATA[
387
        <![CDATA[
426
          if (this.selectedItems)
388
          if (this.selectedItems) {
427
          {
389
            for (var i = this.selectedItems.length - 1; i >= 0; --i)
428
            for (var i = this.selectedItems.length-1; i >= 0; --i)
429
              this.selectedItems[i].selected = false;
390
              this.selectedItems[i].selected = false;
430
          
391
431
            this.selectedItems.splice(0, this.selectedItems.length);
392
            this.selectedItems.splice(0, this.selectedItems.length);
432
          }
393
          }
394
433
          this._selectionStart = null;
395
          this._selectionStart = null;
434
          this._fireOnSelect();
396
          this._fireOnSelect();
435
        ]]>
397
        ]]>
436
        </body>
398
        </body>
437
      </method>      
399
      </method>
438
      
400
439
      <!-- ///////////////// nsIListBoxObject ///////////////// -->
401
      <property name="selectedCount" readonly="true"
440
      
402
                onget="return this.selectedItems.length;"/>
403
404
      <method name="getSelectedItem">
405
        <parameter name="aIndex"/>
406
        <body>
407
        <![CDATA[
408
          return aIndex < this.selectedItems.length ?
409
            this.selectedItems[aIndex] : null;
410
        ]]>
411
        </body>
412
      </method>
413
414
    <!-- Other public members -->
415
      <property name="disableKeyNavigation"
416
                onget="return this.hasAttribute('disableKeyNavigation');">
417
        <setter>
418
          if (val)
419
            this.setAttribute("disableKeyNavigation", "true");
420
          else
421
            this.removeAttribute("disableKeyNavigation");
422
          return val;
423
        </setter>
424
      </property>
425
426
      <property name="suppressOnSelect"
427
                onget="return this.getAttribute('suppressonselect') == 'true';"
428
                onset="this.setAttribute('suppressonselect', val);"/>
429
430
      <property name="_selectDelay"
431
                onset="this.setAttribute('_selectDelay', val);"
432
                onget="return this.getAttribute('_selectDelay') || 50;"/>
433
434
      <method name="timedSelect">
435
        <parameter name="aItem"/>
436
        <parameter name="aTimeout"/>
437
        <body>
438
        <![CDATA[
439
          var suppress = this._suppressOnSelect;
440
          if (aTimeout != -1)
441
            this._suppressOnSelect = true;
442
443
          this.selectItem(aItem);
444
445
          this._suppressOnSelect = suppress;
446
447
          if (aTimeout != -1) {
448
            if (this._selectTimeout)
449
              window.clearTimeout(this._selectTimeout);
450
            this._selectTimeout =
451
              window.setTimeout(this._selectTimeoutHandler, aTimeout, this);
452
          }
453
        ]]>
454
        </body>
455
      </method>
456
457
      <method name="moveByOffset">
458
        <parameter name="aOffset"/>
459
        <parameter name="aIsSelecting"/>
460
        <parameter name="aIsSelectingRange"/>
461
        <body>
462
        <![CDATA[
463
          if ((aIsSelectingRange || !aIsSelecting) &&
464
              this.selType != "multiple")
465
            return;
466
467
          var newIndex = this.currentIndex + aOffset;
468
          if (newIndex < 0)
469
            newIndex = 0;
470
471
          var numItems = this.getRowCount();
472
          if (newIndex > numItems - 1)
473
            newIndex = numItems - 1;
474
475
          var newItem = this.getItemAtIndex(newIndex);
476
          if (newItem) {
477
            this.ensureIndexIsVisible(newIndex);
478
            if (aIsSelectingRange)
479
              this.selectItemRange(null, newItem);
480
            else if (aIsSelecting)
481
              this.selectItem(newItem);
482
483
            this.currentItem = newItem;
484
          }
485
        ]]>
486
        </body>
487
      </method>
488
489
    <!-- Private -->
441
      <method name="getNextItem">
490
      <method name="getNextItem">
442
        <parameter name="startItem"/>
491
        <parameter name="aStartItem"/>
443
        <parameter name="delta"/>
492
        <parameter name="aDelta"/>
444
        <body><![CDATA[
493
        <body>
445
          while (startItem) {
494
        <![CDATA[
446
            startItem = startItem.nextSibling;
495
          while (aStartItem) {
447
            if (startItem && startItem.localName == "listitem") {
496
            aStartItem = aStartItem.nextSibling;
448
              --delta;
497
            if (aStartItem && aStartItem instanceof
449
              if (delta == 0)
498
                Components.interfaces.nsIDOMXULSelectControlItemElement) {
450
                return startItem;
499
              --aDelta;
500
              if (aDelta == 0)
501
                return aStartItem;
451
            }
502
            }
452
          }
503
          }
453
          return null;
504
          return null;
454
        ]]></body>
505
        ]]></body>
455
      </method>
506
      </method>
507
456
      <method name="getPreviousItem">
508
      <method name="getPreviousItem">
457
        <parameter name="startItem"/>
509
        <parameter name="aStartItem"/>
458
        <parameter name="delta"/>
510
        <parameter name="aDelta"/>
459
        <body><![CDATA[
511
        <body>
460
          while (startItem) {
512
        <![CDATA[
461
            startItem = startItem.previousSibling;
513
          while (aStartItem) {
462
            if (startItem && startItem.localName == "listitem") {
514
            aStartItem = aStartItem.previousSibling;
463
              --delta;
515
            if (aStartItem && aStartItem instanceof
464
              if (delta == 0)
516
                Components.interfaces.nsIDOMXULSelectControlItemElement) {
465
                return startItem;
517
              --aDelta;
518
              if (aDelta == 0)
519
                return aStartItem;
466
            }
520
            }
467
          }
521
          }
468
          return null;
522
          return null;
469
        ]]></body>
523
        ]]>
524
        </body>
525
      </method>
526
527
      <method name="_selectTimeoutHandler">
528
        <parameter name="aMe"/>
529
        <body>
530
          aMe._fireOnSelect();
531
          aMe._selectTimeout = null;
532
        </body>
533
      </method>
534
535
      <constructor>
536
      <![CDATA[
537
        var els = this.getElementsByAttribute("selected", "true");
538
        for (var i = 0; i < els.length; ++i)
539
          this.selectedItems.push(els[i]);
540
      ]]>
541
      </constructor>
542
543
      <field name="_suppressOnSelect">false</field>
544
      <field name="_selectTimeout">null</field>
545
      <field name="_currentItem">null</field>
546
      <field name="_selectionStart">null</field>
547
    </implementation>
548
  </binding>
549
550
551
  <!-- Binding for xul:listbox element.
552
  -->
553
  <binding id="listbox"
554
           extends="#listbox-base">
555
556
    <resources>
557
      <stylesheet src="chrome://global/skin/listbox.css"/>
558
    </resources>
559
560
    <content>
561
      <children includes="listcols">
562
        <xul:listcols>
563
          <xul:listcol flex="1"/>
564
        </xul:listcols>
565
      </children>
566
      <xul:listrows>
567
        <children includes="listhead"/>
568
        <xul:listboxbody xbl:inherits="rows,size,minheight">
569
          <children includes="listitem"/>
570
        </xul:listboxbody> 
571
      </xul:listrows>
572
    </content>
573
574
    <implementation>
575
      <field name="_lastKeyTime">0</field>
576
      <field name="_incrementalString">""</field>
577
578
      <!-- ///////////////// public listbox members ///////////////// -->
579
580
      <property name="listBoxObject"
581
                onget="return this.boxObject.QueryInterface(Components.interfaces.nsIListBoxObject);"
582
                readonly="true"/>
583
584
      <!-- ///////////////// private listbox members ///////////////// -->
585
586
      <method name="_fireOnSelect">
587
        <body>
588
        <![CDATA[
589
          if (!this._suppressOnSelect && !this.suppressOnSelect) {
590
            var event = document.createEvent("Events");
591
            event.initEvent("select", true, true);
592
            this.dispatchEvent(event);
593
          }
594
        ]]>
595
        </body>
596
      </method>
597
598
      <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
599
600
      <method name="appendItem">
601
        <parameter name="aLabel"/>
602
        <parameter name="aValue"/>
603
        <body>
604
          const XULNS =
605
            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
606
607
          var item = this.ownerDocument.createElementNS(XULNS, "listitem");
608
          item.setAttribute("label", aLabel);
609
          item.setAttribute("value", aValue);
610
          this.appendChild(item);
611
          return item;
612
        </body>
613
      </method>
614
615
      <method name="insertItemAt">
616
        <parameter name="aIndex"/>
617
        <parameter name="aLabel"/>
618
        <parameter name="aValue"/>
619
        <body>
620
          const XULNS =
621
            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
622
623
          var item = this.ownerDocument.createElementNS(XULNS, "listitem");
624
          item.setAttribute("label", aLabel);
625
          item.setAttribute("value", aValue);
626
          var before = this.getItemAtIndex(aIndex);
627
          if (before)
628
            this.insertBefore(item, before);
629
          else
630
            this.appendChild(item);
631
          return item;
632
        </body>
470
      </method>
633
      </method>
634
635
      <!-- ///////////////// nsIListBoxObject ///////////////// -->
471
      <method name="getIndexOfItem">
636
      <method name="getIndexOfItem">
472
        <parameter name="item"/>
637
        <parameter name="item"/>
473
        <body>
638
        <body>
474
          return this.listBoxObject.getIndexOfItem(item);
639
          return this.listBoxObject.getIndexOfItem(item);
475
        </body>
640
        </body>
476
      </method>
641
      </method>
642
477
      <method name="getItemAtIndex">
643
      <method name="getItemAtIndex">
478
        <parameter name="index"/>
644
        <parameter name="index"/>
479
        <body>
645
        <body>
480
          return this.listBoxObject.getItemAtIndex(index);
646
          return this.listBoxObject.getItemAtIndex(index);
481
        </body>
647
        </body>
482
      </method>
648
      </method>
483
      <method name="ensureIndexIsVisible">
649
      <method name="ensureIndexIsVisible">
484
        <parameter name="index"/>
650
        <parameter name="index"/>
 Lines 508-551    Link Here 
508
          return this.listBoxObject.getIndexOfFirstVisibleRow();
674
          return this.listBoxObject.getIndexOfFirstVisibleRow();
509
        </body>
675
        </body>
510
      </method>
676
      </method>
511
      <method name="getRowCount">
677
      <method name="getRowCount">
512
        <body>
678
        <body>
513
          return this.listBoxObject.getRowCount();
679
          return this.listBoxObject.getRowCount();
514
        </body>
680
        </body>
515
      </method>
681
      </method>
516
      <method name="moveByOffset">
682
517
        <parameter name="offset"/>
518
        <parameter name="isSelecting"/>
519
        <parameter name="isSelectingRange"/>
520
        <body>
521
          <![CDATA[
522
            if ((isSelectingRange || !isSelecting) && this.selType != "multiple")
523
              return;
524
            var newIndex = this.currentIndex + offset;
525
            if (newIndex < 0)
526
              newIndex = 0;
527
            var numItems = this.getRowCount();
528
            if (newIndex > numItems - 1)
529
              newIndex = numItems - 1;
530
            var newItem = this.getItemAtIndex(newIndex);
531
            if (newItem) {
532
              this.ensureIndexIsVisible(newIndex);
533
              if (isSelectingRange) {
534
                this.selectItemRange(null, newItem);
535
              }
536
              else if (isSelecting) {
537
                this.selectItem(newItem);
538
              }
539
              this.currentItem = newItem;
540
            }
541
          ]]>
542
        </body>
543
      </method>
544
      <method name="scrollOnePage">
683
      <method name="scrollOnePage">
545
        <parameter name="direction"/>  <!-- Must be -1 or 1 -->
684
        <parameter name="direction"/>  <!-- Must be -1 or 1 -->
546
        <body>      
685
        <body>      
547
          <![CDATA[
686
          <![CDATA[
548
            var pageOffset = this.getNumberOfVisibleRows() * direction;
687
            var pageOffset = this.getNumberOfVisibleRows() * direction;
549
            var newTop = this.getIndexOfFirstVisibleRow() + pageOffset;
688
            var newTop = this.getIndexOfFirstVisibleRow() + pageOffset;
550
            if (direction == 1) {
689
            if (direction == 1) {
551
              var maxTop = this.getRowCount() - pageOffset;
690
              var maxTop = this.getRowCount() - pageOffset;
 Lines 585-600    Link Here 
585
          }
724
          }
586
        ]]>
725
        ]]>
587
      </handler>
726
      </handler>
588
      <handler event="focus">
727
      <handler event="focus">
589
        <![CDATA[
728
        <![CDATA[
590
          if (this.currentIndex == -1 && this.getRowCount() > 0) {
729
          if (this.currentIndex == -1 && this.getRowCount() > 0) {
591
            this.currentIndex = this.getIndexOfFirstVisibleRow();
730
            this.currentIndex = this.getIndexOfFirstVisibleRow();
592
          }
731
          }
732
          this._lastKeyTime = 0;
593
        ]]>
733
        ]]>
594
      </handler>
734
      </handler>
595
      <handler event="keypress" key=" " modifiers="control" phase="target">
735
      <handler event="keypress" key=" " modifiers="control" phase="target">
596
        <![CDATA[
736
        <![CDATA[
597
          if (this.currentItem && this.selType == "multiple")
737
          if (this.currentItem && this.selType == "multiple")
598
            this.toggleItemSelection(this.currentItem);
738
            this.toggleItemSelection(this.currentItem);
599
        ]]>
739
        ]]>
600
      </handler>
740
      </handler>
 Lines 646-663    Link Here 
646
             }
786
             }
647
           }
787
           }
648
         }
788
         }
649
       ]]>
789
       ]]>
650
       </handler>
790
       </handler>
651
    </handlers>    
791
    </handlers>    
652
  </binding>
792
  </binding>
653
793
654
  <binding id="listrows"
794
  <binding id="listrows">
655
           extends="chrome://global/content/bindings/listbox.xml#listbox-base">
795
796
    <resources>
797
      <stylesheet src="chrome://global/skin/listbox.css"/>
798
    </resources>
799
656
    <handlers>
800
    <handlers>
657
      <handler event="DOMMouseScroll" phase="capturing">
801
      <handler event="DOMMouseScroll" phase="capturing">
658
      <![CDATA[
802
      <![CDATA[
659
        var listBox = this.parentNode.listBoxObject;
803
        var listBox = this.parentNode.listBoxObject;
660
        var rows = event.detail;
804
        var rows = event.detail;
661
        if (rows == NSUIEvent.SCROLL_PAGE_UP)
805
        if (rows == NSUIEvent.SCROLL_PAGE_UP)
662
          rows = -listBox.getNumberOfVisibleRows();
806
          rows = -listBox.getNumberOfVisibleRows();
663
        else if (rows == NSUIEvent.SCROLL_PAGE_DOWN)
807
        else if (rows == NSUIEvent.SCROLL_PAGE_DOWN)
 Lines 684-699    Link Here 
684
828
685
    <implementation implements="nsIDOMXULSelectControlItemElement, nsIAccessibleProvider">
829
    <implementation implements="nsIDOMXULSelectControlItemElement, nsIAccessibleProvider">
686
      <property name="current" onget="return this.getAttribute('current') == 'true';">
830
      <property name="current" onget="return this.getAttribute('current') == 'true';">
687
        <setter><![CDATA[
831
        <setter><![CDATA[
688
          if (val)
832
          if (val)
689
            this.setAttribute("current", "true");
833
            this.setAttribute("current", "true");
690
          else
834
          else
691
            this.removeAttribute("current");
835
            this.removeAttribute("current");
836
837
          this._fireEvent(val ? "DOMMenuItemActive" : "DOMMenuItemInactive");
838
692
          return val;
839
          return val;
693
        ]]></setter>
840
        ]]></setter>
694
      </property>
841
      </property>
695
842
696
      <!-- ///////////////// nsIAccessibleProvider ///////////////// -->
843
      <!-- ///////////////// nsIAccessibleProvider ///////////////// -->
697
844
698
      <property name="accessibleType" readonly="true">
845
      <property name="accessibleType" readonly="true">
699
        <getter>
846
        <getter>
 Lines 712-802    Link Here 
712
      
859
      
713
      <property name="selected" onget="return this.getAttribute('selected') == 'true';">
860
      <property name="selected" onget="return this.getAttribute('selected') == 'true';">
714
        <setter><![CDATA[
861
        <setter><![CDATA[
715
          if (val)
862
          if (val)
716
            this.setAttribute("selected", "true");
863
            this.setAttribute("selected", "true");
717
          else
864
          else
718
            this.removeAttribute("selected");
865
            this.removeAttribute("selected");
719
866
720
          var event = document.createEvent("Events");
867
          if (!this.control || this.control.selType == "multiple")
721
          event.initEvent("DOMMenuItemActive", true, true);
868
            this._fireEvent(val ? "DOMItemSelected" : "DOMItemUnselected");
722
          this.dispatchEvent(event);
723
869
724
          return val;
870
          return val;
725
        ]]></setter>
871
        ]]></setter>
726
      </property>
872
      </property>
727
873
728
      <property name="control">
874
      <property name="control">
729
        <getter><![CDATA[
875
        <getter><![CDATA[
730
          var parent = this.parentNode;
876
          var parent = this.parentNode;
731
          while (parent) {
877
          while (parent) {
732
            if (parent.localName == "listbox")
878
            if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
733
              return parent;
879
              return parent;
734
            parent = parent.parentNode;
880
            parent = parent.parentNode;
735
          }
881
          }
736
          return null;
882
          return null;
737
        ]]></getter>
883
        ]]></getter>
738
      </property>
884
      </property>
885
886
      <method name="_fireEvent">
887
        <parameter name="name"/>
888
        <body>
889
        <![CDATA[
890
          var event = document.createEvent("Events");
891
          event.initEvent(name, true, true);
892
          this.dispatchEvent(event);
893
        ]]>
894
        </body>
895
      </method>
739
    </implementation>
896
    </implementation>
740
    <handlers>
897
    <handlers>
741
      <!-- If there is no modifier key, we select on mousedown, not
898
      <!-- If there is no modifier key, we select on mousedown, not
742
           click, so that drags work correctly. -->
899
           click, so that drags work correctly. -->
743
      <handler event="mousedown">
900
      <handler event="mousedown">
744
      <![CDATA[
901
      <![CDATA[
745
        if (this.parentNode.disabled) return;
902
        var control = this.control;
903
        if (!control || control.disabled)
904
          return;
746
        if (!event.ctrlKey && !event.shiftKey && !event.metaKey) {
905
        if (!event.ctrlKey && !event.shiftKey && !event.metaKey) {
747
          if (!this.selected) {
906
          if (!this.selected) {
748
            parentNode.selectItem(this);
907
            control.selectItem(this);
749
          }
908
          }
750
          parentNode.currentItem = this;
909
          control.currentItem = this;
751
        }
910
        }
752
      ]]>
911
      ]]>
753
      </handler>
912
      </handler>
754
913
755
      <!-- On a click (up+down on the same item), deselect everything
914
      <!-- On a click (up+down on the same item), deselect everything
756
           except this item. -->
915
           except this item. -->
757
      <handler event="click" button="0">
916
      <handler event="click" button="0">
758
      <![CDATA[
917
      <![CDATA[
759
        if (this.parentNode.disabled) return;
918
        var control = this.control;
760
        if (parentNode.selType != "multiple") {
919
        if (!control || control.disabled)
761
          parentNode.selectItem(this);
920
          return;
921
        if (control.selType != "multiple") {
922
          control.selectItem(this);
762
        }
923
        }
763
        else if (event.ctrlKey || event.metaKey) {
924
        else if (event.ctrlKey || event.metaKey) {
764
          parentNode.toggleItemSelection(this);
925
          control.toggleItemSelection(this);
765
          parentNode.currentItem = this;
926
          control.currentItem = this;
766
        }
927
        }
767
        else if (event.shiftKey) {
928
        else if (event.shiftKey) {
768
          parentNode.selectItemRange(null, this);
929
          control.selectItemRange(null, this);
769
          parentNode.currentItem = this;
930
          control.currentItem = this;
770
        }
931
        }
771
        else {
932
        else {
772
          /* We want to deselect all the selected items except what was
933
          /* We want to deselect all the selected items except what was
773
            clicked, UNLESS it was a right-click.  We have to do this
934
            clicked, UNLESS it was a right-click.  We have to do this
774
            in click rather than mousedown so that you can drag a
935
            in click rather than mousedown so that you can drag a
775
            selected group of items */
936
            selected group of items */
776
937
777
          var selectedItems = parentNode.selectedItems;
938
          // use selectItemRange instead of selectItem, because this
778
          var didSuppressSelect = false;
939
          // doesn't de- and reselect this item if it is selected
779
          var i = 0;
940
          control.selectItemRange(this, this);
780
          while (i < selectedItems.length) {
781
            if (selectedItems[i] != this) {
782
              if (!didSuppressSelect) {
783
                parentNode._suppressOnSelect = true;
784
                didSuppressSelect = true;
785
              }
786
              parentNode.removeItemFromSelection(selectedItems[i]);
787
            }
788
            else
789
              i++;
790
          }
791
          if (didSuppressSelect) {
792
            parentNode._suppressOnSelect = false;
793
            parentNode._fireOnSelect();
794
          }
795
        }
941
        }
796
      ]]>
942
      ]]>
797
      </handler>
943
      </handler>
798
    </handlers>
944
    </handlers>
799
  </binding>
945
  </binding>
800
946
801
  <binding id="listitem-iconic"
947
  <binding id="listitem-iconic"
802
           extends="chrome://global/content/bindings/listbox.xml#listitem">
948
           extends="chrome://global/content/bindings/listbox.xml#listitem">
 Lines 829-862    Link Here 
829
          return val;
975
          return val;
830
        ]]></setter>
976
        ]]></setter>
831
      </property>
977
      </property>
832
    </implementation>
978
    </implementation>
833
979
834
    <handlers> 
980
    <handlers> 
835
      <handler event="mousedown" button="0">
981
      <handler event="mousedown" button="0">
836
      <![CDATA[
982
      <![CDATA[
837
        if (!this.disabled && !this.parentNode.disabled)
983
        if (!this.disabled && !this.control.disabled)
838
          this.checked = !this.checked;
984
          this.checked = !this.checked;
839
      ]]>
985
      ]]>
840
      </handler>
986
      </handler>
841
    </handlers>
987
    </handlers>
842
  </binding>
988
  </binding>
843
  
989
  
844
  <binding id="listitem-checkbox-iconic"
990
  <binding id="listitem-checkbox-iconic"
845
           extends="chrome://global/content/bindings/listbox.xml#listitem-checkbox">
991
           extends="chrome://global/content/bindings/listbox.xml#listitem-checkbox">
846
    <content>
992
    <content>
847
      <children>
993
      <children>
848
        <xul:listcell type="checkbox" class="listcell-iconic" xbl:inherits="label,image,crop,checked,disabled,flexlabel"/>
994
        <xul:listcell type="checkbox" class="listcell-iconic" xbl:inherits="label,image,crop,checked,disabled,flexlabel"/>
849
      </children>
995
      </children>
850
    </content>
996
    </content>
851
  </binding>
997
  </binding>
852
  
998
  
853
  <binding id="listcell"
999
  <binding id="listcell"
854
           extends="chrome://global/content/bindings/listbox.xml#listbox-base">
1000
           extends="chrome://global/content/bindings/general.xml#basecontrol">
1001
1002
    <resources>
1003
      <stylesheet src="chrome://global/skin/listbox.css"/>
1004
    </resources>
1005
855
    <content>
1006
    <content>
856
      <children>
1007
      <children>
857
        <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
1008
        <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
858
      </children>
1009
      </children>
859
    </content>
1010
    </content>
860
  </binding>
1011
  </binding>
861
1012
862
  <binding id="listcell-iconic"
1013
  <binding id="listcell-iconic"
 Lines 885-911    Link Here 
885
      <children>
1036
      <children>
886
        <xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
1037
        <xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
887
        <xul:image class="listcell-icon" xbl:inherits="src=image"/>
1038
        <xul:image class="listcell-icon" xbl:inherits="src=image"/>
888
        <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabelcrop,disabled" flex="1" crop="right"/>
1039
        <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabelcrop,disabled" flex="1" crop="right"/>
889
      </children>
1040
      </children>
890
    </content>
1041
    </content>
891
  </binding>
1042
  </binding>
892
1043
893
  <binding id="listhead"
1044
  <binding id="listhead">
894
           extends="chrome://global/content/bindings/listbox.xml#listbox-base">
1045
1046
    <resources>
1047
      <stylesheet src="chrome://global/skin/listbox.css"/>
1048
    </resources>
1049
895
    <content>
1050
    <content>
896
      <xul:listheaditem>
1051
      <xul:listheaditem>
897
        <children includes="listheader"/>
1052
        <children includes="listheader"/>
898
      </xul:listheaditem>
1053
      </xul:listheaditem>
899
    </content>
1054
    </content>
900
  </binding>
1055
  </binding>
901
  
1056
  
902
  <binding id="listheader" display="xul:button"
1057
  <binding id="listheader" display="xul:button">
903
           extends="chrome://global/content/bindings/listbox.xml#listbox-base">
1058
1059
    <resources>
1060
      <stylesheet src="chrome://global/skin/listbox.css"/>
1061
    </resources>
1062
904
    <content>
1063
    <content>
905
      <xul:image class="listheader-icon"/>
1064
      <xul:image class="listheader-icon"/>
906
      <xul:label class="listheader-label" xbl:inherits="value=label,crop" flex="1" crop="right"/>
1065
      <xul:label class="listheader-label" xbl:inherits="value=label,crop" flex="1" crop="right"/>
907
      <xul:image class="listheader-sortdirection" xbl:inherits="sortDirection"/>
1066
      <xul:image class="listheader-sortdirection" xbl:inherits="sortDirection"/>
908
    </content>
1067
    </content>
909
  </binding>
1068
  </binding>
910
1069
911
</bindings>
1070
</bindings>
(-)toolkit/content/widgets/richlistbox.xml (-338 / +319 lines)
Line     Link Here 
 Lines 17-32    Link Here 
17
   -
17
   -
18
   - The Initial Developer of the Original Code is
18
   - The Initial Developer of the Original Code is
19
   - IBM Corporation.
19
   - IBM Corporation.
20
   - Portions created by the Initial Developer are Copyright (C) 2005
20
   - Portions created by the Initial Developer are Copyright (C) 2005
21
   - IBM Corporation. All Rights Reserved.
21
   - IBM Corporation. All Rights Reserved.
22
   -
22
   -
23
   - Contributor(s):
23
   - Contributor(s):
24
   -   Doron Rosenberg <doronr@us.ibm.com> (Original Author)
24
   -   Doron Rosenberg <doronr@us.ibm.com> (Original Author)
25
   -   Simon Bünzli <zeniko@gmail.com>
25
   -
26
   -
26
   - Alternatively, the contents of this file may be used under the terms of
27
   - Alternatively, the contents of this file may be used under the terms of
27
   - either the GNU General Public License Version 2 or later (the "GPL"), or
28
   - either the GNU General Public License Version 2 or later (the "GPL"), or
28
   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
   - in which case the provisions of the GPL or the LGPL are applicable instead
30
   - in which case the provisions of the GPL or the LGPL are applicable instead
30
   - of those above. If you wish to allow use of your version of this file only
31
   - of those above. If you wish to allow use of your version of this file only
31
   - under the terms of either the GPL or the LGPL, and not to allow others to
32
   - under the terms of either the GPL or the LGPL, and not to allow others to
32
   - use your version of this file under the terms of the MPL, indicate your
33
   - use your version of this file under the terms of the MPL, indicate your
 Lines 37-65    Link Here 
37
   -
38
   -
38
   - ***** END LICENSE BLOCK ***** -->
39
   - ***** END LICENSE BLOCK ***** -->
39
40
40
<bindings id="richlistboxBindings"
41
<bindings id="richlistboxBindings"
41
          xmlns="http://www.mozilla.org/xbl"
42
          xmlns="http://www.mozilla.org/xbl"
42
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
43
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
43
          xmlns:xbl="http://www.mozilla.org/xbl">
44
          xmlns:xbl="http://www.mozilla.org/xbl">
44
45
45
  <binding id="richlistbox">
46
  <binding id="richlistbox"
47
           extends="chrome://global/content/bindings/listbox.xml#listbox-base">
48
    <resources>
49
      <stylesheet src="chrome://global/skin/richlistbox.css"/>
50
    </resources>
51
46
    <content>
52
    <content>
47
      <xul:scrollbox allowevents="true" orient="vertical" anonid="main-box"
53
      <xul:scrollbox allowevents="true" orient="vertical" anonid="main-box"
48
                     flex="1" style="overflow: auto;">
54
                     flex="1" style="overflow: auto;">
49
        <children />
55
        <children />
50
      </xul:scrollbox>
56
      </xul:scrollbox>
51
    </content>
57
    </content>
52
58
53
    <resources>
59
    <implementation>
54
      <stylesheet src="chrome://global/skin/richlistbox.css"/>
55
    </resources>
56
57
    <implementation implements="nsIAccessibleProvider, nsIDOMXULSelectControlElement">
58
      <field name="scrollBoxObject">null</field>
60
      <field name="scrollBoxObject">null</field>
59
      <constructor>
61
      <constructor>
60
        <![CDATA[
62
        <![CDATA[
61
          var x = document.getAnonymousElementByAttribute(this, "anonid", "main-box");
63
          var x = document.getAnonymousElementByAttribute(this, "anonid", "main-box");
62
          this.scrollBoxObject = x.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
64
          this.scrollBoxObject = x.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
63
65
64
          // add a template build listener
66
          // add a template build listener
65
          if (this.builder)
67
          if (this.builder)
 Lines 69-589    Link Here 
69
        ]]>
71
        ]]>
70
      </constructor>
72
      </constructor>
71
73
72
      <destructor>
74
      <destructor>
73
        <![CDATA[
75
        <![CDATA[
74
          // remove the template build listener
76
          // remove the template build listener
75
          if (this.builder)
77
          if (this.builder)
76
            this.builder.removeListener(this._builderListener);
78
            this.builder.removeListener(this._builderListener);
77
78
          this._selectedItem = null;
79
        ]]>
79
        ]]>
80
      </destructor>
80
      </destructor>
81
81
82
      <property name="accessibleType" readonly="true">
82
    <!-- Overriding baselistbox -->
83
        <getter>
83
      <method name="_fireOnSelect">
84
          <![CDATA[
85
            return Components.interfaces.nsIAccessibleProvider.XULListbox;
86
          ]]>
87
        </getter>
88
      </property>
89
90
      <property name="children">
91
        <getter>
92
          <![CDATA[
93
            var childNodes = [];
94
            for (var i = 0; i < this.childNodes.length; ++i) {
95
              if (this.childNodes[i] instanceof Components.interfaces.nsIDOMXULSelectControlItemElement)
96
                childNodes.push(this.childNodes[i]);
97
            }
98
            return childNodes;
99
          ]]>
100
        </getter>
101
      </property>
102
103
      <field name="_builderListener">
104
        <![CDATA[
105
          ({
106
            mOuter: this,
107
            item: null,
108
            willRebuild : function(builder) {},
109
            didRebuild : function(builder) {
110
              this.mOuter._refreshSelection();
111
            }
112
          });
113
        ]]>
114
      </field>
115
116
      <method name="_refreshSelection">
117
        <body>
84
        <body>
118
          <![CDATA[
85
          <![CDATA[
119
            // when this method is called, we know that either the selectedItem
86
            // make sure not to modify last-selected when suppressing select events
120
            // we have is null (ctor) or a reference to an element no longer in
87
            // (otherwise we'll lose the selection when a template gets rebuilt)
121
            // the DOM (template).
88
            if (this._suppressOnSelect || this.suppressOnSelect)
122
89
              return;
123
            // fist look for a last-selected attribute
90
124
            var lastSelected = this.getAttribute("last-selected");
91
            // remember the current item and all selected items with IDs
125
            if (lastSelected != "") {
92
            var state = this.currentItem ? this.currentItem.id : "";
126
              var element = document.getElementById(lastSelected);
93
            if (this.selType == "multiple" && this.selectedCount) {
127
94
              function getId(aItem) { return aItem.id; }
128
              if (element) {
95
              state += " " + this.selectedItems.filter(getId).map(getId).join(" ");
129
                this.selectedItem = element;
130
                if (!this._isItemVisible(this.selectedItem))
131
                  this.scrollBoxObject.scrollToElement(this.selectedItem);
132
                return;
133
              }
134
            }
96
            }
97
            if (state)
98
              this.setAttribute("last-selected", state);
99
            else
100
              this.removeAttribute("last-selected");
135
101
136
            // cache the selected index
102
            // preserve the index just in case no IDs are available
137
            var selectedIndex = this._selectedIndex;
103
            if (this.currentIndex > -1)
104
              this._currentIndex = this.currentIndex + 1;
138
105
139
            // refreshes selection.  Called for example when a template rebuild
106
            var event = document.createEvent("Events");
140
            // happens.
107
            event.initEvent("select", true, true);
141
            if (this.selectedItem) {
108
            this.dispatchEvent(event);
142
              if (this.selectedItem.hasAttribute("id")) {
143
                var id = this.selectedItem.getAttribute("id");
144
                var item = document.getElementById(id);
145
146
                // if we find no item, clear selection so that the code at the bottom
147
                // takes over
148
                if (item) {
149
                  this.selectedItem = item;
150
                } else {
151
                  this.clearSelection();
152
                }
153
              } else {
154
                 // if no id, we clear selection so that the below code will select
155
                 // based on the current index
156
                 this.clearSelection();
157
              }
158
            }
159
109
160
            // if we have no previously selected item or the above if check fails to
110
            // always call this (allows a commandupdater without controller)
161
            // find the previous node (which causes it to clear selection)
111
            document.commandDispatcher.updateCommands("richlistbox-select");
162
            if (!this.selectedItem) {
163
              // if the selectedIndex is larger than the row count, select the last
164
              // item.
165
              if (selectedIndex >= this.getRowCount())
166
                this.selectedIndex = this.getRowCount() - 1;
167
              else
168
                this.selectedIndex = selectedIndex;
169
170
              // XXX: downloadmanager needs the following line, else we scroll to
171
              // the middle on inital load.
172
              this.ensureSelectedElementIsVisible();
173
            }
174
          ]]>
112
          ]]>
175
        </body>
113
        </body>
176
      </method>
114
      </method>
177
115
178
      <method name="fireActiveItemEvent">
116
      <method name="appendItem">
117
        <parameter name="aLabel"/>
118
        <parameter name="aValue"/>
179
        <body>
119
        <body>
180
          <![CDATA[
120
          return this.insertItemAt(-1, aLabel, aValue);
181
            if (this.selectedItem) {
182
              var event = document.createEvent("Events");
183
              event.initEvent("DOMMenuItemActive", true, true);
184
              this.selectedItem.dispatchEvent(event);
185
            }
186
            return false;
187
          ]]>
188
        </body>
121
        </body>
189
      </method>
122
      </method>
190
123
191
      <field name="_selectedIndex">0</field>
124
      <method name="insertItemAt">
192
      <property name="selectedIndex">
125
        <parameter name="aIndex"/>
193
        <getter>
126
        <parameter name="aLabel"/>
194
          <![CDATA[
127
        <parameter name="aValue"/>
195
            return this.getIndexOf(this.selectedItem);
128
        <body>
196
          ]]>
129
          const XULNS =
197
        </getter>
130
            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
198
        <setter>
199
          <![CDATA[
200
            if (val == -1) {
201
              // clear selection
202
              this.clearSelection();
203
            } else if (val >= 0) {
204
              // only set if we get an item returned
205
              var item = this.getItemAtIndex(val);
206
              if (item)
207
                this.selectedItem = item;
208
            }
209
          ]]>
210
        </setter>
211
      </property>
212
213
      <field name="_selectedItem">null</field>
214
      <property name="selectedItem">
215
        <getter>
216
          return this._selectedItem;
217
        </getter>
218
        <setter>
219
          <![CDATA[
220
            if (this._selectedItem == val)
221
              return;
222
223
            this._setItemSelection(val);
224
131
225
            if (val)
132
          var item =
226
              this.fireActiveItemEvent();
133
            this.ownerDocument.createElementNS(XULNS, "richlistitem");
134
          item.setAttribute("value", aValue);
135
136
          var label = this.ownerDocument.createElementNS(XULNS, "label");
137
          label.setAttribute("value", aLabel);
138
          label.setAttribute("flex", "1");
139
          label.setAttribute("crop", "end");
140
          item.appendChild(label);
141
142
          var before = this.getItemAtIndex(aIndex);
143
          if (!before)
144
            this.appendChild(item);
145
          else
146
            this.insertBefore(item, before);
227
147
228
            this._fireOnSelect();
148
          return item;
229
          ]]>
149
        </body>
230
        </setter>
150
      </method>
231
      </property>
232
151
233
      <!-- sets selection but doesn't cause any events -->
152
      <method name="getIndexOfItem">
234
      <method name="_setItemSelection">
235
        <parameter name="aItem"/>
153
        <parameter name="aItem"/>
236
        <body>
154
        <body>
237
          <![CDATA[
155
          <![CDATA[
238
            // unselect current item
156
            // don't search the children, if we're looking for none of them
239
            if (this._selectedItem)
157
            if (aItem == null)
240
              this._selectedItem.selected = false
158
              return -1;
241
159
242
            this._selectedItem = aItem;
160
            return this.children.indexOf(aItem);
243
            this._selectedIndex = this.getIndexOf(aItem);
244
            this.ensureSelectedElementIsVisible();
245
246
            if (aItem) {
247
              aItem.selected = true;
248
              aItem.focus();
249
            }
250
          ]]>
161
          ]]>
251
        </body>
162
        </body>
252
      </method>
163
      </method>
253
164
254
      <method name="clearSelection">
165
      <method name="getItemAtIndex">
166
        <parameter name="aIndex"/>
167
        <body>
168
          return this.children[aIndex] || null;
169
        </body>
170
      </method>
171
172
      <method name="ensureIndexIsVisible">
173
        <parameter name="aIndex"/>
255
        <body>
174
        <body>
256
          <![CDATA[
175
          <![CDATA[
257
            this.selectedItem = null;
176
            // work around missing implementation in scrollBoxObject
177
            return this.ensureElementIsVisible(this.getItemAtIndex(aIndex));
258
          ]]>
178
          ]]>
259
        </body>
179
        </body>
260
      </method>
180
      </method>
261
181
262
      <method name="getRowCount">
182
      <method name="ensureElementIsVisible">
183
        <parameter name="aElement"/>
263
        <body>
184
        <body>
264
          <![CDATA[
185
          <![CDATA[
265
            return this.children.length;
186
            if (aElement)
187
              this.scrollBoxObject.ensureElementIsVisible(aElement);
266
          ]]>
188
          ]]>
267
        </body>
189
        </body>
268
      </method>
190
      </method>
269
191
270
      <method name="goUp">
192
      <method name="scrollToIndex">
193
        <parameter name="aIndex"/>
271
        <body>
194
        <body>
272
          <![CDATA[
195
          <![CDATA[
273
            // if nothing selected, we go from the bottom
196
            var item = this.getItemAtIndex(aIndex);
274
            for (var i = this.selectedItem ? this.selectedItem.previousSibling : this.lastChild; i; i = i.previousSibling) {
197
            if (item)
275
              // could have a template element, which would be a sibling
198
              this.scrollBoxObject.scrollToElement(item);
276
              if (i instanceof Components.interfaces.nsIDOMXULSelectControlItemElement) {
277
                this.selectedItem = i;
278
                return true;
279
              }
280
            }
281
            return false;
282
          ]]>
199
          ]]>
283
        </body>
200
        </body>
284
      </method>
201
      </method>
285
202
286
      <method name="goDown">
203
      <method name="getNumberOfVisibleRows">
204
        <!-- returns the number of currently visible rows                -->
205
        <!-- don't rely on this function, if the items' height can vary! -->
287
        <body>
206
        <body>
288
          <![CDATA[
207
          <![CDATA[
289
            // if nothing selected, we go from the top
208
            var children = this.children;
290
            for (var i = this.selectedItem ? this.selectedItem.nextSibling : this.firstChild; i; i = i.nextSibling) {
209
291
              // could have a template element, which would be a sibling
210
            for (var top = 0; top < children.length && !this._isItemVisible(children[top]); top++);
292
              if (i instanceof Components.interfaces.nsIDOMXULSelectControlItemElement) {
211
            for (var ix = top; ix < children.length && this._isItemVisible(children[ix]); ix++);
293
                this.selectedItem = i;
212
294
                return true;
213
            return ix - top;
295
              }
296
            }
297
            return false;
298
          ]]>
214
          ]]>
299
        </body>
215
        </body>
300
      </method>
216
      </method>
301
217
302
      <method name="_isItemVisible">
218
      <method name="getIndexOfFirstVisibleRow">
303
        <parameter name="aItem"/>
304
        <body>
219
        <body>
305
          <![CDATA[
220
          <![CDATA[
306
            if (!aItem)
221
            var children = this.children;
307
              return false;
308
222
309
            var y = {};
223
            for (var ix = 0; ix < children.length; ix++)
310
            this.scrollBoxObject.getPosition({}, y);
224
              if (this._isItemVisible(children[ix]))
311
            y.value += this.scrollBoxObject.y;
225
                return ix;
312
226
313
            // Partially visible items are also considered visible
227
            return -1;
314
            return (aItem.boxObject.y + aItem.boxObject.height > y.value) &&
228
          ]]>
315
                   (aItem.boxObject.y < y.value + this.scrollBoxObject.height);
229
        </body>
230
      </method>
231
232
      <method name="getRowCount">
233
        <body>
234
          <![CDATA[
235
            return this.children.length;
316
          ]]>
236
          ]]>
317
        </body>
237
        </body>
318
      </method>
238
      </method>
319
239
320
      <method name="scrollOnePage">
240
      <method name="scrollOnePage">
321
        <parameter name="aDirection"/> <!-- Must be -1 or 1 -->
241
        <parameter name="aDirection"/> <!-- Must be -1 or 1 -->
322
        <body>
242
        <body>
323
          <![CDATA[
243
          <![CDATA[
324
            var children = this.children;
244
            var children = this.children;
325
245
326
            if (children.length == 0)
246
            if (children.length == 0)
327
              return false;
247
              return 0;
328
329
            var index = children.indexOf(this.selectedItem);
330
248
331
            // If nothing is selected, we just select the first element
249
            // If nothing is selected, we just select the first element
332
            // at the extreme we're moving away from
250
            // at the extreme we're moving away from
333
            if (index == -1) {
251
            if (!this.currentItem)
334
              index = aDirection == -1 ? children.length - 1 : 0;
252
              return aDirection == -1 ? children.length : 0;
335
              this.selectedItem = children[index];
336
              return true;
337
            }
338
253
339
            // If the selected item is visible, we scroll by one page so that
254
            // If the current item is visible, scroll by one page so that
340
            // the newly selected item is at approximately the same position as
255
            // the new current item is at approximately the same position as
341
            // the currently selected one
256
            // the existing current item.
342
            var currentItem = children[index];
257
            if (this._isItemVisible(this.currentItem))
343
            if (this._isItemVisible(currentItem))
344
              this.scrollBoxObject.scrollBy(0, this.scrollBoxObject.height * aDirection);
258
              this.scrollBoxObject.scrollBy(0, this.scrollBoxObject.height * aDirection);
345
259
346
            // Figure out, how many items fully fit into the view port
260
            // Figure out, how many items fully fit into the view port
347
            // (including the currently selected one), and determine
261
            // (including the currently selected one), and determine
348
            // the index of the first one lying (partially) outside
262
            // the index of the first one lying (partially) outside
349
            var height = this.scrollBoxObject.height;
263
            var height = this.scrollBoxObject.height;
350
            var border = currentItem.boxObject.y;
264
            var border = this.currentItem.boxObject.y;
351
            if (aDirection == -1)
265
            if (aDirection == -1)
352
              border += currentItem.boxObject.height;
266
              border += this.currentItem.boxObject.height;
353
            while (index >= 0 && index < children.length) {
267
            var index = this.currentIndex;
268
            while (0 <= index && index < children.length) {
354
              var border2 = children[index].boxObject.y;
269
              var border2 = children[index].boxObject.y;
355
              if (aDirection == -1)
270
              if (aDirection == -1)
356
                border2 += children[index].boxObject.height;
271
                border2 += children[index].boxObject.height;
357
              if ((border2 - border) * aDirection > height)
272
              if ((border2 - border) * aDirection > height)
358
                break;
273
                break;
359
              index += aDirection;
274
              index += aDirection;
360
            }
275
            }
361
            index -= aDirection;
276
            index -= aDirection;
362
277
363
            if (this.selectedItem != children[index]) {
278
            return index != this.currentIndex ? index - this.currentIndex : aDirection;
364
              this.selectedItem = children[index];
365
              return true;
366
            }
367
368
            // Move by at least one item if the view port is too small
369
            if (aDirection == -1)
370
              return this.goUp();
371
372
            return this.goDown();
373
          ]]>
279
          ]]>
374
        </body>
280
        </body>
375
      </method>
281
      </method>
376
282
377
      <method name="getItemAtIndex">
283
    <!-- richlistbox specific -->
378
        <parameter name="aIndex"/>
284
      <property name="children" readonly="true">
285
        <getter>
286
          <![CDATA[
287
            var childNodes = [];
288
            for (var child = this.firstChild; child; child = child.nextSibling) {
289
              if (child instanceof Components.interfaces.nsIDOMXULSelectControlItemElement)
290
                childNodes.push(child);
291
            }
292
            return childNodes;
293
          ]]>
294
        </getter>
295
      </property>
296
297
      <field name="_builderListener" readonly="true">
298
        <![CDATA[
299
          ({
300
            mOuter: this,
301
            item: null,
302
            willRebuild: function(builder) { },
303
            didRebuild: function(builder) {
304
              this.mOuter._refreshSelection();
305
            }
306
          });
307
        ]]>
308
      </field>
309
310
      <method name="_refreshSelection">
379
        <body>
311
        <body>
380
          <![CDATA[
312
          <![CDATA[
381
            return this.children[aIndex];
313
            // when this method is called, we know that either the currentItem
314
            // and selectedItems we have are null (ctor) or a reference to an
315
            // element no longer in the DOM (template).
316
317
            // first look for the last-selected attribute
318
            var state = this.getAttribute("last-selected");
319
            if (state) {
320
              var ids = state.split(" ");
321
322
              var suppressSelect = this._suppressOnSelect;
323
              this._suppressOnSelect = true;
324
              this.clearSelection();
325
              for (var i = 1; i < ids.length; i++) {
326
                var selectedItem = document.getElementById(ids[i]);
327
                if (selectedItem)
328
                  this.addItemToSelection(selectedItem);
329
              }
330
331
              var currentItem = document.getElementById(ids[0]);
332
              if (!currentItem && this._currentIndex)
333
                currentItem = this.getItemAtIndex(Math.min(
334
                  this._currentIndex - 1, this.getRowCount()));
335
              if (currentItem) {
336
                this.currentItem = currentItem;
337
                if (this.selType != "multiple" && this.selectedCount == 0)
338
                  this.selectedItem = currentItem;
339
340
                if (this.scrollBoxObject.height)
341
                  this.ensureElementIsVisible(currentItem);
342
                else // XXX hack around a bug in ensureElementIsVisible
343
                  this.ensureElementIsVisible(currentItem.previousSibling);
344
              }
345
              this._suppressOnSelect = suppressSelect;
346
              // XXX actually it's just a refresh, but at least
347
              // the Extensions manager expects this:
348
              this._fireOnSelect();
349
              return;
350
            }
351
352
            // try to restore the selected items according to their IDs
353
            // (applies after a template rebuild, if last-selected was not set)
354
            if (this.selectedItems) {
355
              for (i = this.selectedCount - 1; i >= 0; i--) {
356
                if (this.selectedItems[i] && this.selectedItems[i].id)
357
                  this.selectedItems[i] = document.getElementById(this.selectedItems[i].id);
358
                else
359
                  this.selectedItems[i] = null;
360
                if (!this.selectedItems[i])
361
                  this.selectedItems.splice(i, 1);
362
              }
363
            }
364
            if (this.currentItem && this.currentItem.id)
365
              this.currentItem = document.getElementById(this.currentItem.id);
366
            else
367
              this.currentItem = null;
368
369
            // if we have no previously current item or if the above check fails to
370
            // find the previous nodes (which causes it to clear selection)
371
            if (!this.currentItem && this.selectedCount == 0) {
372
              this.currentIndex = this._currentIndex ? this._currentIndex - 1 : 0;
373
374
              // cf. listbox constructor:
375
              // select items according to their attributes
376
              var els = this.getElementsByAttribute("selected", "true");
377
              for (i = 0; i < els.length; i++)
378
                this.selectedItems.push(els[i]);
379
            }
380
381
            if (this.selType != "multiple" && this.selectedCount == 0)
382
              this.selectedItem = this.currentItem;
383
384
            // XXX hack for the Downloads manager (better to focus the list than
385
            // the individual items - these usually aren't tabbable anyway, and
386
            // we need the keyboard focus for navigation), see bug 363271:
387
            this.focus();
382
          ]]>
388
          ]]>
383
        </body>
389
        </body>
384
      </method>
390
      </method>
385
391
386
      <method name="getIndexOf">
392
      <method name="_isItemVisible">
387
        <parameter name="aElement"/>
393
        <parameter name="aItem"/>
388
        <body>
394
        <body>
389
          <![CDATA[
395
          <![CDATA[
390
            // don't search the children, if we're looking for none of them
396
            if (!aItem)
391
            if (aElement == null)
397
              return false;
392
              return -1;
398
399
            var y = {};
400
            this.scrollBoxObject.getPosition({}, y);
401
            y.value += this.scrollBoxObject.y;
393
402
394
            return this.children.indexOf(aElement);
403
            // Partially visible items are also considered visible
404
            return (aItem.boxObject.y + aItem.boxObject.height > y.value) &&
405
                   (aItem.boxObject.y < y.value + this.scrollBoxObject.height);
395
          ]]>
406
          ]]>
396
        </body>
407
        </body>
397
      </method>
408
      </method>
398
409
399
      <method name="ensureElementIsVisible">
410
      <field name="_currentIndex">null</field>
411
412
      <!-- For backwards-compatibility and for convenience.
413
        Use getIndexOfItem instead. -->
414
      <method name="getIndexOf">
400
        <parameter name="aElement"/>
415
        <parameter name="aElement"/>
401
        <body>
416
        <body>
402
          <![CDATA[
417
          <![CDATA[
403
            if (aElement)
418
            return this.getIndexOfItem(aElement);
404
              this.scrollBoxObject.ensureElementIsVisible(aElement);
405
          ]]>
419
          ]]>
406
        </body>
420
        </body>
407
      </method>
421
      </method>
408
422
423
      <!-- For backwards-compatibility and for convenience.
424
        Use ensureElementIsVisible instead -->
409
      <method name="ensureSelectedElementIsVisible">
425
      <method name="ensureSelectedElementIsVisible">
410
        <body>
426
        <body>
411
          <![CDATA[
427
          <![CDATA[
412
	    if (this.selectedItem) {
428
            return this.ensureElementIsVisible(this.selectedItem);
413
              this.ensureElementIsVisible(this.selectedItem);
414
	    }
415
          ]]>
429
          ]]>
416
        </body>
430
        </body>
417
      </method>
431
      </method>
418
432
419
      <property name="suppressOnSelect">
433
      <!-- For backwards-compatibility and for convenience.
420
        <getter>
434
        Use moveByOffset instead. -->
435
      <method name="goUp">
436
        <body>
421
          <![CDATA[
437
          <![CDATA[
422
            return this.getAttribute("suppressonselect") == "true";
438
            var index = this.currentIndex;
439
            this.moveByOffset(-1, true, false);
440
            return index != this.currentIndex;
423
          ]]>
441
          ]]>
424
        </getter>
442
        </body>
425
      </property>
443
      </method>
426
444
      <method name="goDown">
427
      <method name="_fireOnSelect">
428
        <body>
445
        <body>
429
          <![CDATA[
446
          <![CDATA[
430
            if (this.selectedItem)
447
            var index = this.currentIndex;
431
              this.setAttribute("last-selected", this.selectedItem.getAttribute("id"));
448
            this.moveByOffset(1, true, false);
432
            else
449
            return index != this.currentIndex;
433
              this.removeAttribute("last-selected");
434
435
            if (!this.suppressOnSelect) {
436
              var event = document.createEvent("Events");
437
              event.initEvent("select", true, true);
438
              this.dispatchEvent(event);
439
440
              // if we have controllers, notify the command dispatcher
441
              if (this.controllers.getControllerCount() > 0)
442
                document.commandDispatcher.updateCommands("richlistbox-select");
443
            }
444
          ]]>
450
          ]]>
445
        </body>
451
        </body>
446
      </method>
452
      </method>
453
454
      <!-- deprecated (is implied by currentItem and selectItem) -->
455
      <method name="fireActiveItemEvent"><body/></method>
447
    </implementation>
456
    </implementation>
448
457
449
    <handlers>
458
    <handlers>
450
      <handler event="keypress" keycode="VK_UP" action="goUp(); event.preventDefault();"/>
459
      <!-- handle keyboard navigation also when a child element has got the focus -->
451
      <handler event="keypress" keycode="VK_DOWN" action="goDown(); event.preventDefault();"/>
460
      <handler event="keypress" keycode="VK_UP"
452
      <handler event="keypress" keycode="VK_PAGE_UP" action="scrollOnePage(-1); event.preventDefault();"/>
461
               modifiers="control shift any"
453
      <handler event="keypress" keycode="VK_PAGE_DOWN" action="scrollOnePage(1); event.preventDefault();"/>
462
               action="this.moveByOffset(-1, !event.ctrlKey, event.shiftKey);"
454
      <handler event="keypress" keycode="VK_HOME" action="clearSelection(); goDown(); event.preventDefault();"/>
463
               preventdefault="true"/>
455
      <handler event="keypress" keycode="VK_END" action="clearSelection(); goUp(); event.preventDefault();"/>
464
      <handler event="keypress" keycode="VK_DOWN"
465
               modifiers="control shift any"
466
               action="this.moveByOffset(1, !event.ctrlKey, event.shiftKey);"
467
               preventdefault="true"/>
468
      <handler event="keypress" keycode="VK_HOME"
469
               modifiers="control shift any"
470
               action="this.moveByOffset(-this.currentIndex, !event.ctrlKey, event.shiftKey);"
471
               preventdefault="true"/>
472
      <handler event="keypress" keycode="VK_END"
473
               modifiers="control shift any"
474
               action="this.moveByOffset(this.getRowCount(), !event.ctrlKey, event.shiftKey);"
475
               preventdefault="true"/>
476
      <handler event="keypress" keycode="VK_PAGE_UP"
477
               modifiers="control shift any"
478
               action="this.moveByOffset(this.scrollOnePage(-1), !event.ctrlKey, event.shiftKey);"
479
               preventdefault="true"/>
480
      <handler event="keypress" keycode="VK_PAGE_DOWN"
481
               modifiers="control shift any"
482
               action="this.moveByOffset(this.scrollOnePage(1), !event.ctrlKey, event.shiftKey);"
483
               preventdefault="true"/>
456
484
457
      <handler event="click">
485
      <handler event="click">
458
        <![CDATA[
486
        <![CDATA[
459
          // clicking into nothing should unselect
487
          // clicking into nothing should unselect
460
          if (event.originalTarget.getAttribute("anonid") == "main-box")
488
          if (event.originalTarget.getAttribute("anonid") == "main-box") {
461
            this.clearSelection();
489
            this.clearSelection();
490
            this.currentItem = null;
491
          }
462
        ]]>
492
        ]]>
463
      </handler>
493
      </handler>
464
      <handler event="contextmenu">
494
      <handler event="contextmenu">
465
        <![CDATA[
495
        <![CDATA[
466
          // if the context menu was opened via the keyboard, display it in the
496
          // if the context menu was opened via the keyboard, display it in the
467
          // right location.
497
          // right location.
468
          if (event.button != 2) {
498
          if (event.button != 2) {
469
            var popup = document.getElementById(this.getAttribute("context"));
499
            var popup = document.getElementById(this.getAttribute("context"));
470
            if (popup)
500
            if (popup)
471
              popup.showPopup(this.selectedItem, -1, -1, "context", "bottomleft", "topleft");
501
              popup.showPopup(this.currentItem, -1, -1, "context", "bottomleft", "topleft");
472
          }
502
          }
473
        ]]>
503
        ]]>
474
      </handler>
504
      </handler>
475
      <handler event="focus">
476
        <![CDATA[
477
          if (event.target == this)
478
            this.fireActiveItemEvent();
479
        ]]>
480
      </handler>
481
    </handlers>
505
    </handlers>
482
  </binding>
506
  </binding>
483
507
484
  <binding id="richlistitem"
508
  <binding id="richlistitem"
485
           extends="chrome://global/content/bindings/general.xml#basecontrol">
509
           extends="chrome://global/content/bindings/listbox.xml#listitem">
486
    <content>
510
    <content>
487
      <children />
511
      <children/>
488
    </content>
512
    </content>
489
513
490
    <resources>
514
    <resources>
491
      <stylesheet src="chrome://global/skin/richlistbox.css"/>
515
      <stylesheet src="chrome://global/skin/richlistbox.css"/>
492
    </resources>
516
    </resources>
493
517
494
    <implementation implements="nsIAccessibleProvider, nsIDOMXULSelectControlItemElement">
518
    <implementation>
495
      <destructor>
519
      <destructor>
496
        <![CDATA[
520
        <![CDATA[
497
          // When we are destructed and we are selected, unselect ourselves so
521
          var control = this.control;
498
          // that richlistbox's selection doesn't point to something not in the DOM.
522
          if (!control)
499
          // We don't want to reset last-selected, so we don't call clearSelection().
523
            return;
524
          // When we are destructed and we are current or selected, unselect ourselves
525
          // so that richlistbox's selection doesn't point to something not in the DOM.
526
          // We don't want to reset last-selected, so we set _suppressOnSelect.
500
          if (this.selected) {
527
          if (this.selected) {
501
            this.control._setItemSelection(null);
528
            var suppressSelect = control._suppressOnSelect;
529
            control._suppressOnSelect = true;
530
            control.removeItemFromSelection(this);
531
            control._suppressOnSelect = suppressSelect;
502
          }
532
          }
533
          if (this.current)
534
            control.currentItem = null;
503
        ]]>
535
        ]]>
504
      </destructor>
536
      </destructor>
505
537
506
      <!-- ///////////////// nsIAccessibleProvider ///////////////// -->
538
      <property name="label" readonly="true">
507
      <property name="accessibleType" readonly="true">
508
        <getter>
509
          <![CDATA[
510
            return Components.interfaces.nsIAccessibleProvider.XULListitem;
511
          ]]>
512
        </getter>
513
      </property>
514
      <!-- ///////////////// nsIDOMXULSelectControlItemElement ///////////////// -->
515
516
      <property name="value" onget="return this.getAttribute('value');"
517
                             onset="this.setAttribute('value', val); return val;"/>
518
519
      <property name="label">
520
        <!-- Setter purposely not implemented; the getter returns a
539
        <!-- Setter purposely not implemented; the getter returns a
521
             concatentation of label text to expose via accessibility APIs-->
540
             concatentation of label text to expose via accessibility APIs -->
522
        <getter>
541
        <getter>
523
          <![CDATA[
542
          <![CDATA[
543
            const XULNS =
544
              "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
545
524
            var labelText = "";
546
            var labelText = "";
525
            var startEl = document.getAnonymousNodes(this)[0];
547
            var startEl = document.getAnonymousNodes(this);
526
            if (startEl) {
548
            if (!startEl.length)
527
              var labels = 
549
              startEl = [this];
528
                startEl.getElementsByTagNameNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul',
550
            var labels = startEl[0].getElementsByTagNameNS(XULNS, "label");
529
                                              'label');
551
            for (var count = 0; count < labels.length; count++)
530
              var numLabels = labels.length;
552
              labelText += labels[count].value + " ";
531
              for (count = 0; count < numLabels; count ++) {
532
                var label = labels[count];
533
                if (!label.collapsed && !label.hidden &&
534
                    label.className != 'text-link') {
535
                  labelText += label.value + ' ';
536
                }
537
              }
538
            }
539
            return labelText;
553
            return labelText;
540
          ]]>
554
          ]]>
541
        </getter>
555
        </getter>
542
      </property>
556
      </property>
543
544
      <property name="selected"
545
                onget="return this.getAttribute('selected') == 'true';"
546
                onset="return this.setAttribute('selected',val);"/>
547
548
      <property name="control">
549
        <getter>
550
          <![CDATA[
551
            var parent = this.parentNode;
552
            while (parent) {
553
              if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
554
                return parent;
555
              parent = parent.parentNode;
556
            }
557
            return null;
558
          ]]>
559
        </getter>
560
      </property>
561
    </implementation>
557
    </implementation>
562
558
563
    <handlers>
559
    <handlers>
564
      <handler event="click">
565
        <![CDATA[
566
          var listbox = this.control;
567
          if ((event.target == this) && event.ctrlKey && (listbox.selectedItem == this)) {
568
            listbox.clearSelection();
569
          } else {
570
            listbox.selectedItem = this;
571
          }
572
        ]]>
573
      </handler>
574
      <handler event="contextmenu" phase="capturing">
560
      <handler event="contextmenu" phase="capturing">
575
        <![CDATA[
561
        <![CDATA[
576
          // This needed to be called before the contextmenu gets shown to handle
562
          // handle someone right-clicking on an item other than the current one
577
          // someone rightclicking on an unselected item
563
          if (event.target == this && this.control)
578
          if (event.target == this) {
564
            this.control.currentItem = this;
579
            var listbox = this.control;
580
            if (listbox) {
581
              listbox.selectedItem = this;
582
            }
583
          }
584
        ]]>
565
        ]]>
585
      </handler>
566
      </handler>
586
    </handlers>
567
    </handlers>
587
  </binding>
568
  </binding>
588
</bindings>
569
</bindings>
589
570
(-)toolkit/themes/pinstripe/global/richlistbox.css (+16 lines)
Line     Link Here 
 Lines 15-30    Link Here 
15
 *
15
 *
16
 * The Initial Developer of the Original Code is
16
 * The Initial Developer of the Original Code is
17
 * IBM Corporation.
17
 * IBM Corporation.
18
 * Portions created by the Initial Developer are Copyright (C) 2005
18
 * Portions created by the Initial Developer are Copyright (C) 2005
19
 * the Initial Developer. All Rights Reserved.
19
 * the Initial Developer. All Rights Reserved.
20
 *
20
 *
21
 * Contributor(s):
21
 * Contributor(s):
22
 *   Doron Rosenberg <doronr@us.ibm.com> (original author)
22
 *   Doron Rosenberg <doronr@us.ibm.com> (original author)
23
 *   Simon Bünzli <zeniko@gmail.com>
23
 *
24
 *
24
 * Alternatively, the contents of this file may be used under the terms of
25
 * Alternatively, the contents of this file may be used under the terms of
25
 * either the GNU General Public License Version 2 or later (the "GPL"), or
26
 * either the GNU General Public License Version 2 or later (the "GPL"), or
26
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
 * in which case the provisions of the GPL or the LGPL are applicable instead
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
28
 * of those above. If you wish to allow use of your version of this file only
29
 * of those above. If you wish to allow use of your version of this file only
29
 * under the terms of either the GPL or the LGPL, and not to allow others to
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
30
 * use your version of this file under the terms of the MPL, indicate your
31
 * use your version of this file under the terms of the MPL, indicate your
 Lines 33-53    Link Here 
33
 * the provisions above, a recipient may use your version of this file under
34
 * the provisions above, a recipient may use your version of this file under
34
 * the terms of any one of the MPL, the GPL or the LGPL.
35
 * the terms of any one of the MPL, the GPL or the LGPL.
35
 *
36
 *
36
 * ***** END LICENSE BLOCK ***** */
37
 * ***** END LICENSE BLOCK ***** */
37
38
38
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
39
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
39
40
40
richlistbox {
41
richlistbox {
42
  margin: 2px 4px;
41
  background-color: -moz-Field;
43
  background-color: -moz-Field;
42
  color: -moz-FieldText;
44
  color: -moz-FieldText;
43
}
45
}
44
46
45
richlistbox[disabled="true"] {
47
richlistbox[disabled="true"] {
46
  color: GrayText;
48
  color: GrayText;
47
}
49
}
48
50
49
richlistitem[selected="true"] {
51
richlistitem[selected="true"] {
52
  background-color: -moz-Dialog;
53
  color: -moz-DialogText;
54
}
55
56
richlistbox:focus > richlistitem[selected="true"] {
50
  background-color: Highlight;
57
  background-color: Highlight;
51
  color: HighlightText;
58
  color: HighlightText;
52
}
59
}
53
60
61
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"] {
62
  outline: 1px dotted Highlight;
63
  -moz-outline-offset: -1px;
64
}
65
66
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"][selected="true"] {
67
  outline: 1px dotted #F3D982; /* TODO: find a suitable system color */
68
}
69
(-)toolkit/themes/winstripe/global/richlistbox.css (+16 lines)
Line     Link Here 
 Lines 15-30    Link Here 
15
 *
15
 *
16
 * The Initial Developer of the Original Code is
16
 * The Initial Developer of the Original Code is
17
 * IBM Corporation.
17
 * IBM Corporation.
18
 * Portions created by the Initial Developer are Copyright (C) 2005
18
 * Portions created by the Initial Developer are Copyright (C) 2005
19
 * the Initial Developer. All Rights Reserved.
19
 * the Initial Developer. All Rights Reserved.
20
 *
20
 *
21
 * Contributor(s):
21
 * Contributor(s):
22
 *   Doron Rosenberg <doronr@us.ibm.com> (original author)
22
 *   Doron Rosenberg <doronr@us.ibm.com> (original author)
23
 *   Simon Bünzli <zeniko@gmail.com>
23
 *
24
 *
24
 * Alternatively, the contents of this file may be used under the terms of
25
 * Alternatively, the contents of this file may be used under the terms of
25
 * either the GNU General Public License Version 2 or later (the "GPL"), or
26
 * either the GNU General Public License Version 2 or later (the "GPL"), or
26
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27
 * in which case the provisions of the GPL or the LGPL are applicable instead
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
28
 * of those above. If you wish to allow use of your version of this file only
29
 * of those above. If you wish to allow use of your version of this file only
29
 * under the terms of either the GPL or the LGPL, and not to allow others to
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
30
 * use your version of this file under the terms of the MPL, indicate your
31
 * use your version of this file under the terms of the MPL, indicate your
 Lines 33-53    Link Here 
33
 * the provisions above, a recipient may use your version of this file under
34
 * the provisions above, a recipient may use your version of this file under
34
 * the terms of any one of the MPL, the GPL or the LGPL.
35
 * the terms of any one of the MPL, the GPL or the LGPL.
35
 *
36
 *
36
 * ***** END LICENSE BLOCK ***** */
37
 * ***** END LICENSE BLOCK ***** */
37
38
38
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
39
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
39
40
40
richlistbox {
41
richlistbox {
42
  margin: 2px 4px;
41
  background-color: -moz-Field;
43
  background-color: -moz-Field;
42
  color: -moz-FieldText;
44
  color: -moz-FieldText;
43
}
45
}
44
46
45
richlistbox[disabled="true"] {
47
richlistbox[disabled="true"] {
46
  color: GrayText;
48
  color: GrayText;
47
}
49
}
48
50
49
richlistitem[selected="true"] {
51
richlistitem[selected="true"] {
52
  background-color: -moz-Dialog;
53
  color: -moz-DialogText;
54
}
55
56
richlistbox:focus > richlistitem[selected="true"] {
50
  background-color: Highlight;
57
  background-color: Highlight;
51
  color: HighlightText;
58
  color: HighlightText;
52
}
59
}
53
60
61
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"] {
62
  outline: 1px dotted Highlight;
63
  -moz-outline-offset: -1px;
64
}
65
66
richlistbox[seltype="multiple"]:focus > richlistitem[current="true"][selected="true"] {
67
  outline: 1px dotted #F3D982; /* TODO: find a suitable system color */
68
}
69

Return to bug 298371