toolkit/content/widgets/menulist.xml

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:e9236839fe11
1 <?xml version="1.0"?>
2 <!-- This Source Code Form is subject to the terms of the Mozilla Public
3 - License, v. 2.0. If a copy of the MPL was not distributed with this
4 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
5
6
7 <bindings id="menulistBindings"
8 xmlns="http://www.mozilla.org/xbl"
9 xmlns:html="http://www.w3.org/1999/xhtml"
10 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
11 xmlns:xbl="http://www.mozilla.org/xbl">
12
13 <binding id="menulist-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
14 <resources>
15 <stylesheet src="chrome://global/content/menulist.css"/>
16 <stylesheet src="chrome://global/skin/menulist.css"/>
17 </resources>
18 </binding>
19
20 <binding id="menulist" display="xul:menu" role="xul:menulist"
21 extends="chrome://global/content/bindings/menulist.xml#menulist-base">
22 <content sizetopopup="pref">
23 <xul:hbox class="menulist-label-box" flex="1">
24 <xul:image class="menulist-icon" xbl:inherits="src=image,src"/>
25 <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
26 </xul:hbox>
27 <xul:dropmarker class="menulist-dropmarker" type="menu" xbl:inherits="disabled,open"/>
28 <children includes="menupopup"/>
29 </content>
30
31 <handlers>
32 <handler event="command" phase="capturing"
33 action="if (event.target.parentNode.parentNode == this) this.selectedItem = event.target;"/>
34
35 <handler event="popupshowing">
36 <![CDATA[
37 if (event.target.parentNode == this) {
38 this.menuBoxObject.activeChild = null;
39 if (this.selectedItem)
40 // Not ready for auto-setting the active child in hierarchies yet.
41 // For now, only do this when the outermost menupopup opens.
42 this.menuBoxObject.activeChild = this.mSelectedInternal;
43 }
44 ]]>
45 </handler>
46
47 <handler event="keypress" modifiers="shift any" group="system">
48 <![CDATA[
49 if (!event.defaultPrevented &&
50 (event.keyCode == KeyEvent.DOM_VK_UP ||
51 event.keyCode == KeyEvent.DOM_VK_DOWN ||
52 event.keyCode == KeyEvent.DOM_VK_PAGE_UP ||
53 event.keyCode == KeyEvent.DOM_VK_PAGE_DOWN ||
54 event.keyCode == KeyEvent.DOM_VK_HOME ||
55 event.keyCode == KeyEvent.DOM_VK_END ||
56 event.keyCode == KeyEvent.DOM_VK_BACK_SPACE ||
57 event.charCode > 0)) {
58 // Moving relative to an item: start from the currently selected item
59 this.menuBoxObject.activeChild = this.mSelectedInternal;
60 if (this.menuBoxObject.handleKeyPress(event)) {
61 this.menuBoxObject.activeChild.doCommand();
62 event.preventDefault();
63 }
64 }
65 ]]>
66 </handler>
67 </handlers>
68
69 <implementation implements="nsIDOMXULMenuListElement, nsIDOMEventListener">
70 <constructor>
71 this.mInputField = null;
72 this.mSelectedInternal = null;
73 this.menuBoxObject = this.boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject);
74 this.setInitialSelection();
75 </constructor>
76
77 <method name="setInitialSelection">
78 <body>
79 <![CDATA[
80 var popup = this.menupopup;
81 if (popup) {
82 var arr = popup.getElementsByAttribute('selected', 'true');
83
84 var editable = this.editable;
85 var value = this.value;
86 if (!arr.item(0) && value)
87 arr = popup.getElementsByAttribute(editable ? 'label' : 'value', value);
88
89 if (arr.item(0))
90 this.selectedItem = arr[0];
91 else if (!editable)
92 this.selectedIndex = 0;
93 }
94 ]]>
95 </body>
96 </method>
97
98 <property name="value" onget="return this.getAttribute('value');">
99 <setter>
100 <![CDATA[
101 // if the new value is null, we still need to remove the old value
102 if (val == null)
103 return this.selectedItem = val;
104
105 var arr = null;
106 var popup = this.menupopup;
107 if (popup)
108 arr = popup.getElementsByAttribute('value', val);
109
110 if (arr && arr.item(0))
111 this.selectedItem = arr[0];
112 else {
113 this.selectedItem = null;
114 this.setAttribute('value', val);
115 }
116
117 return val;
118 ]]>
119 </setter>
120 </property>
121
122 <property name="inputField" readonly="true" onget="return null;"/>
123
124 <property name="crop" onset="this.setAttribute('crop',val); return val;"
125 onget="return this.getAttribute('crop');"/>
126 <property name="image" onset="this.setAttribute('image',val); return val;"
127 onget="return this.getAttribute('image');"/>
128 <property name="label" readonly="true" onget="return this.getAttribute('label');"/>
129 <property name="description" onset="this.setAttribute('description',val); return val;"
130 onget="return this.getAttribute('description');"/>
131 <property name="editable" onset="this.setAttribute('editable',val); return val;"
132 onget="return this.getAttribute('editable') == 'true';"/>
133
134 <property name="open" onset="this.menuBoxObject.openMenu(val);
135 return val;"
136 onget="return this.hasAttribute('open');"/>
137
138 <property name="itemCount" readonly="true"
139 onget="return this.menupopup ? this.menupopup.childNodes.length : 0"/>
140
141 <property name="menupopup" readonly="true">
142 <getter>
143 <![CDATA[
144 var popup = this.firstChild;
145 while (popup && popup.localName != "menupopup")
146 popup = popup.nextSibling;
147 return popup;
148 ]]>
149 </getter>
150 </property>
151
152 <method name="contains">
153 <parameter name="item"/>
154 <body>
155 <![CDATA[
156 if (!item)
157 return false;
158
159 var parent = item.parentNode;
160 return (parent && parent.parentNode == this);
161 ]]>
162 </body>
163 </method>
164
165 <property name="selectedIndex">
166 <getter>
167 <![CDATA[
168 // Quick and dirty. We won't deal with hierarchical menulists yet.
169 if (!this.selectedItem ||
170 !this.mSelectedInternal.parentNode ||
171 this.mSelectedInternal.parentNode.parentNode != this)
172 return -1;
173
174 var children = this.mSelectedInternal.parentNode.childNodes;
175 var i = children.length;
176 while (i--)
177 if (children[i] == this.mSelectedInternal)
178 break;
179
180 return i;
181 ]]>
182 </getter>
183 <setter>
184 <![CDATA[
185 var popup = this.menupopup;
186 if (popup && 0 <= val) {
187 if (val < popup.childNodes.length)
188 this.selectedItem = popup.childNodes[val];
189 }
190 else
191 this.selectedItem = null;
192 return val;
193 ]]>
194 </setter>
195 </property>
196
197 <property name="selectedItem">
198 <getter>
199 <![CDATA[
200 return this.mSelectedInternal;
201 ]]>
202 </getter>
203 <setter>
204 <![CDATA[
205 var oldval = this.mSelectedInternal;
206 if (oldval == val)
207 return val;
208
209 if (val && !this.contains(val))
210 return val;
211
212 if (oldval) {
213 oldval.removeAttribute('selected');
214 if (document instanceof Components.interfaces.nsIDOMXULDocument) {
215 document.removeBroadcastListenerFor(oldval, this, "value");
216 document.removeBroadcastListenerFor(oldval, this, "label");
217 document.removeBroadcastListenerFor(oldval, this, "image");
218 document.removeBroadcastListenerFor(oldval, this, "description");
219 }
220 else
221 oldval.removeEventListener("DOMAttrModified", this, false);
222 }
223
224 this.mSelectedInternal = val;
225 if (val) {
226 val.setAttribute('selected', 'true');
227 this.setAttribute('value', val.getAttribute('value'));
228 this.setAttribute('image', val.getAttribute('image'));
229 this.setAttribute('label', val.getAttribute('label'));
230 this.setAttribute('description', val.getAttribute('description'));
231 // DOMAttrModified listeners slow down setAttribute calls within
232 // the document, see bug 395496
233 if (document instanceof Components.interfaces.nsIDOMXULDocument) {
234 document.addBroadcastListenerFor(val, this, "value");
235 document.addBroadcastListenerFor(val, this, "label");
236 document.addBroadcastListenerFor(val, this, "image");
237 document.addBroadcastListenerFor(val, this, "description");
238 }
239 else
240 val.addEventListener("DOMAttrModified", this, false);
241 }
242 else {
243 this.removeAttribute('value');
244 this.removeAttribute('image');
245 this.removeAttribute('label');
246 this.removeAttribute('description');
247 }
248
249 var event = document.createEvent("Events");
250 event.initEvent("select", true, true);
251 this.dispatchEvent(event);
252
253 var event = document.createEvent("Events");
254 event.initEvent("ValueChange", true, true);
255 this.dispatchEvent(event);
256
257 return val;
258 ]]>
259 </setter>
260 </property>
261
262 <method name="handleEvent">
263 <parameter name="aEvent"/>
264 <body>
265 <![CDATA[
266 if (aEvent.type == "DOMAttrModified" &&
267 aEvent.target == this.mSelectedInternal) {
268 var attrName = aEvent.attrName;
269 switch (attrName) {
270 case "value":
271 case "label":
272 case "image":
273 case "description":
274 this.setAttribute(attrName, aEvent.newValue);
275 }
276 }
277 ]]>
278 </body>
279 </method>
280
281 <method name="getIndexOfItem">
282 <parameter name="item"/>
283 <body>
284 <![CDATA[
285 var popup = this.menupopup;
286 if (popup) {
287 var children = popup.childNodes;
288 var i = children.length;
289 while (i--)
290 if (children[i] == item)
291 return i;
292 }
293 return -1;
294 ]]>
295 </body>
296 </method>
297
298 <method name="getItemAtIndex">
299 <parameter name="index"/>
300 <body>
301 <![CDATA[
302 var popup = this.menupopup;
303 if (popup) {
304 var children = popup.childNodes;
305 if (index >= 0 && index < children.length)
306 return children[index];
307 }
308 return null;
309 ]]>
310 </body>
311 </method>
312
313 <method name="appendItem">
314 <parameter name="label"/>
315 <parameter name="value"/>
316 <parameter name="description"/>
317 <body>
318 <![CDATA[
319 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
320 var popup = this.menupopup ||
321 this.appendChild(document.createElementNS(XULNS, "menupopup"));
322 var item = document.createElementNS(XULNS, "menuitem");
323 item.setAttribute("label", label);
324 item.setAttribute("value", value);
325 if (description)
326 item.setAttribute("description", description);
327
328 popup.appendChild(item);
329 return item;
330 ]]>
331 </body>
332 </method>
333
334 <method name="insertItemAt">
335 <parameter name="index"/>
336 <parameter name="label"/>
337 <parameter name="value"/>
338 <parameter name="description"/>
339 <body>
340 <![CDATA[
341 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
342 var popup = this.menupopup ||
343 this.appendChild(document.createElementNS(XULNS, "menupopup"));
344 var item = document.createElementNS(XULNS, "menuitem");
345 item.setAttribute("label", label);
346 item.setAttribute("value", value);
347 if (description)
348 item.setAttribute("description", description);
349
350 if (index >= 0 && index < popup.childNodes.length)
351 popup.insertBefore(item, popup.childNodes[index]);
352 else
353 popup.appendChild(item);
354 return item;
355 ]]>
356 </body>
357 </method>
358
359 <method name="removeItemAt">
360 <parameter name="index"/>
361 <body>
362 <![CDATA[
363 var popup = this.menupopup;
364 if (popup && 0 <= index && index < popup.childNodes.length) {
365 var remove = popup.childNodes[index];
366 popup.removeChild(remove);
367 return remove;
368 }
369 return null;
370 ]]>
371 </body>
372 </method>
373
374 <method name="removeAllItems">
375 <body>
376 <![CDATA[
377 this.selectedItem = null;
378 var popup = this.menupopup;
379 if (popup)
380 this.removeChild(popup);
381 ]]>
382 </body>
383 </method>
384
385 <destructor>
386 <![CDATA[
387 if (this.mSelectedInternal) {
388 if (document instanceof Components.interfaces.nsIDOMXULDocument) {
389 document.removeBroadcastListenerFor(this.mSelectedInternal, this, "value");
390 document.removeBroadcastListenerFor(this.mSelectedInternal, this, "label");
391 document.removeBroadcastListenerFor(this.mSelectedInternal, this, "image");
392 document.removeBroadcastListenerFor(this.mSelectedInternal, this, "description");
393 }
394 else
395 this.mSelectedInternal.removeEventListener("DOMAttrModified", this, false);
396 }
397 ]]>
398 </destructor>
399 </implementation>
400 </binding>
401
402 <binding id="menulist-editable" extends="chrome://global/content/bindings/menulist.xml#menulist">
403 <content sizetopopup="pref">
404 <xul:hbox class="menulist-editable-box textbox-input-box" xbl:inherits="context,disabled,readonly,focused" flex="1">
405 <html:input class="menulist-editable-input" anonid="input" allowevents="true"
406 xbl:inherits="value=label,value,disabled,tabindex,readonly,placeholder"/>
407 </xul:hbox>
408 <xul:dropmarker class="menulist-dropmarker" type="menu"
409 xbl:inherits="open,disabled,parentfocused=focused"/>
410 <children includes="menupopup"/>
411 </content>
412
413 <implementation>
414 <method name="_selectInputFieldValueInList">
415 <body>
416 <![CDATA[
417 if (this.hasAttribute("disableautoselect"))
418 return;
419
420 // Find and select the menuitem that matches inputField's "value"
421 var arr = null;
422 var popup = this.menupopup;
423
424 if (popup)
425 arr = popup.getElementsByAttribute('label', this.inputField.value);
426
427 this.setSelectionInternal(arr ? arr.item(0) : null);
428 ]]>
429 </body>
430 </method>
431
432 <method name="setSelectionInternal">
433 <parameter name="val"/>
434 <body>
435 <![CDATA[
436 // This is called internally to set selected item
437 // without triggering infinite loop
438 // when using selectedItem's setter
439 if (this.mSelectedInternal == val)
440 return val;
441
442 if (this.mSelectedInternal)
443 this.mSelectedInternal.removeAttribute('selected');
444
445 this.mSelectedInternal = val;
446
447 if (val)
448 val.setAttribute('selected', 'true');
449
450 //Do NOT change the "value", which is owned by inputField
451 return val;
452 ]]>
453 </body>
454 </method>
455
456 <property name="inputField" readonly="true">
457 <getter><![CDATA[
458 if (!this.mInputField)
459 this.mInputField = document.getAnonymousElementByAttribute(this, "anonid", "input");
460 return this.mInputField;
461 ]]></getter>
462 </property>
463
464 <property name="label" onset="this.inputField.value = val; return val;"
465 onget="return this.inputField.value;"/>
466
467 <property name="value" onget="return this.inputField.value;">
468 <setter>
469 <![CDATA[
470 // Override menulist's value setter to refer to the inputField's value
471 // (Allows using "menulist.value" instead of "menulist.inputField.value")
472 this.inputField.value = val;
473 this.setAttribute('value', val);
474 this.setAttribute('label', val);
475 this._selectInputFieldValueInList();
476 return val;
477 ]]>
478 </setter>
479 </property>
480
481 <property name="selectedItem">
482 <getter>
483 <![CDATA[
484 // Make sure internally-selected item
485 // is in sync with inputField.value
486 this._selectInputFieldValueInList();
487 return this.mSelectedInternal;
488 ]]>
489 </getter>
490 <setter>
491 <![CDATA[
492 var oldval = this.mSelectedInternal;
493 if (oldval == val)
494 return val;
495
496 if (val && !this.contains(val))
497 return val;
498
499 // This doesn't touch inputField.value or "value" and "label" attributes
500 this.setSelectionInternal(val);
501 if (val) {
502 // Editable menulist uses "label" as its "value"
503 var label = val.getAttribute('label');
504 this.inputField.value = label;
505 this.setAttribute('value', label);
506 this.setAttribute('label', label);
507 }
508 else {
509 this.inputField.value = "";
510 this.removeAttribute('value');
511 this.removeAttribute('label');
512 }
513
514 var event = document.createEvent("Events");
515 event.initEvent("select", true, true);
516 this.dispatchEvent(event);
517
518 var event = document.createEvent("Events");
519 event.initEvent("ValueChange", true, true);
520 this.dispatchEvent(event);
521
522 return val;
523 ]]>
524 </setter>
525 </property>
526 <property name="disableautoselect"
527 onset="if (val) this.setAttribute('disableautoselect','true');
528 else this.removeAttribute('disableautoselect'); return val;"
529 onget="return this.hasAttribute('disableautoselect');"/>
530
531 <property name="editor" readonly="true">
532 <getter><![CDATA[
533 const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement;
534 return this.inputField.QueryInterface(nsIDOMNSEditableElement).editor;
535 ]]></getter>
536 </property>
537
538 <property name="readOnly" onset="this.inputField.readOnly = val;
539 if (val) this.setAttribute('readonly', 'true');
540 else this.removeAttribute('readonly'); return val;"
541 onget="return this.inputField.readOnly;"/>
542
543 <method name="select">
544 <body>
545 this.inputField.select();
546 </body>
547 </method>
548 </implementation>
549
550 <handlers>
551 <handler event="focus" phase="capturing">
552 <![CDATA[
553 this.setAttribute('focused','true');
554 ]]>
555 </handler>
556
557 <handler event="blur" phase="capturing">
558 <![CDATA[
559 this.removeAttribute('focused');
560 ]]>
561 </handler>
562
563 <handler event="popupshowing">
564 <![CDATA[
565 // editable menulists elements aren't in the focus order,
566 // so when the popup opens we need to force the focus to the inputField
567 if (event.target.parentNode == this) {
568 if (document.commandDispatcher.focusedElement != this.inputField)
569 this.inputField.focus();
570
571 this.menuBoxObject.activeChild = null;
572 if (this.selectedItem)
573 // Not ready for auto-setting the active child in hierarchies yet.
574 // For now, only do this when the outermost menupopup opens.
575 this.menuBoxObject.activeChild = this.mSelectedInternal;
576 }
577 ]]>
578 </handler>
579
580 <handler event="keypress">
581 <![CDATA[
582 // open popup if key is up arrow, down arrow, or F4
583 if (!event.ctrlKey && !event.shiftKey) {
584 if (event.keyCode == KeyEvent.DOM_VK_UP ||
585 event.keyCode == KeyEvent.DOM_VK_DOWN ||
586 (event.keyCode == KeyEvent.DOM_VK_F4 && !event.altKey)) {
587 event.preventDefault();
588 this.open = true;
589 }
590 }
591 ]]>
592 </handler>
593 </handlers>
594 </binding>
595
596 <binding id="menulist-compact" display="xul:menu"
597 extends="chrome://global/content/bindings/menulist.xml#menulist">
598 <content sizetopopup="false">
599 <xul:dropmarker class="menulist-dropmarker" type="menu" xbl:inherits="disabled,open"/>
600 <xul:image class="menulist-icon" xbl:inherits="src=image,src"/>
601 <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
602 <children includes="menupopup"/>
603 </content>
604 </binding>
605
606 <binding id="menulist-description" display="xul:menu"
607 extends="chrome://global/content/bindings/menulist.xml#menulist">
608 <content sizetopopup="pref">
609 <xul:hbox class="menulist-label-box" flex="1">
610 <xul:image class="menulist-icon" xbl:inherits="src=image,src"/>
611 <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
612 <xul:label class="menulist-label menulist-description" xbl:inherits="value=description" crop="right" flex="10000"/>
613 </xul:hbox>
614 <xul:dropmarker class="menulist-dropmarker" type="menu" xbl:inherits="disabled,open"/>
615 <children includes="menupopup"/>
616 </content>
617 </binding>
618 </bindings>

mercurial