3
- CookieKeeper - a Mozilla add-on
4
- (c) 2013-2016 Yvon TANGUY
6
- ==================================================================
7
- This Source Code Form is subject to the terms of the Mozilla Public
8
- License, v. 2.0. If a copy of the MPL was not distributed with this
9
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
12
This code is based on mozilla radio.xml code.
14
- Always have a image (icon)
15
- Like some Blender radio buttons (icon for each choice), packed.
16
- Can be focus, menu can be triggered with the keyboard
20
<bindings id="iradioBindings"
21
xmlns="http://www.mozilla.org/xbl"
22
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
23
xmlns:xbl="http://www.mozilla.org/xbl">
25
<binding id="iradiogroup" role="xul:radiogroup"
26
extends="chrome://global/content/bindings/general.xml#basecontrol">
28
<stylesheet src="chrome://cookiekeeper/skin/iradio.css"/>
31
<implementation implements="nsIDOMXULSelectControlElement">
34
if (this.getAttribute("disabled") == "true")
37
var children = this._getRadioChildren();
38
var length = children.length;
39
for (var i = 0; i < length; i++) {
40
if (children[i].getAttribute("selected") == "true") {
41
this.selectedIndex = i;
46
var value = this.value;
50
this.selectedIndex = 0;
54
<property name="value" onget="return this.getAttribute('value');">
57
this.setAttribute("value", val);
58
var children = this._getRadioChildren();
59
for (var i = 0; i < children.length; i++) {
60
if (String(children[i].value) == String(val)) {
61
this.selectedItem = children[i];
69
<property name="disabled">
72
if (this.getAttribute('disabled') == 'true')
74
var children = this._getRadioChildren();
75
for (var i = 0; i < children.length; ++i) {
76
if (!children[i].hidden && !children[i].collapsed && !children[i].disabled)
85
this.setAttribute('disabled', 'true');
87
this.removeAttribute('disabled');
88
var children = this._getRadioChildren();
89
for (var i = 0; i < children.length; ++i) {
90
children[i].disabled = val;
97
<property name="itemCount" readonly="true"
98
onget="return this._getRadioChildren().length"/>
100
<property name="selectedIndex">
103
var children = this._getRadioChildren();
104
for (var i = 0; i < children.length; ++i) {
105
if (children[i].selected)
113
this.selectedItem = this._getRadioChildren()[val];
119
<property name="selectedItem">
122
var children = this._getRadioChildren();
123
for (var i = 0; i < children.length; ++i) {
124
if (children[i].selected)
132
var focused = this.getAttribute("focused") == "true";
133
var alreadySelected = false;
136
alreadySelected = val.getAttribute("selected") == "true";
137
val.setAttribute("focused", focused);
138
val.setAttribute("selected", "true");
139
this.setAttribute("value", val.value);
142
this.removeAttribute("value");
145
// uncheck all other group nodes
146
var children = this._getRadioChildren();
147
var previousItem = null;
148
for (var i = 0; i < children.length; ++i) {
149
if (children[i] != val) {
150
if (children[i].getAttribute("selected") == "true")
151
previousItem = children[i];
153
children[i].removeAttribute("selected");
154
children[i].removeAttribute("focused");
158
var event = document.createEvent("Events");
159
event.initEvent("select", false, true);
160
this.dispatchEvent(event);
162
if (!alreadySelected && focused) {
163
// Only report if actual change
166
myEvent = document.createEvent("Events");
167
myEvent.initEvent("RadioStateChange", true, true);
168
val.dispatchEvent(myEvent);
172
myEvent = document.createEvent("Events");
173
myEvent.initEvent("RadioStateChange", true, true);
174
previousItem.dispatchEvent(myEvent);
183
<property name="focusedItem">
186
var children = this._getRadioChildren();
187
for (var i = 0; i < children.length; ++i) {
188
if (children[i].getAttribute("focused") == "true")
196
if (val) val.setAttribute("focused", "true");
198
// unfocus all other group nodes
199
var children = this._getRadioChildren();
200
for (var i = 0; i < children.length; ++i) {
201
if (children[i] != val)
202
children[i].removeAttribute("focused");
209
<method name="checkAdjacentElement">
210
<parameter name="aNextFlag"/>
213
var currentElement = this.focusedItem || this.selectedItem;
215
var children = this._getRadioChildren();
216
for (i = 0; i < children.length; ++i ) {
217
if (children[i] == currentElement)
224
if (++i == children.length)
229
while (children[i].hidden || children[i].collapsed || children[i].disabled);
230
// XXX check for display/visibility props too
232
this.selectedItem = children[i];
233
children[i].doCommand();
242
while (children[i].hidden || children[i].collapsed || children[i].disabled);
243
// XXX check for display/visibility props too
245
this.selectedItem = children[i];
246
children[i].doCommand();
251
<field name="_radioChildren">null</field>
252
<method name="_getRadioChildren">
255
if (this._radioChildren)
256
return this._radioChildren;
258
var radioChildren = [];
259
var doc = this.ownerDocument;
261
if (this.hasChildNodes()) {
262
// Don't store the collected child nodes immediately,
263
// collecting the child nodes could trigger constructors
264
// which would blow away our list.
266
const nsIDOMNodeFilter = Components.interfaces.nsIDOMNodeFilter;
267
var iterator = doc.createTreeWalker(this,
268
nsIDOMNodeFilter.SHOW_ELEMENT,
269
this._filterRadioGroup);
270
while (iterator.nextNode())
271
radioChildren.push(iterator.currentNode);
272
return this._radioChildren = radioChildren;
275
// We don't have child nodes.
276
const XUL_NS = "http://www.mozilla.org/keymaster/"
277
+ "gatekeeper/there.is.only.xul";
278
var elems = doc.getElementsByAttribute("group", this.id);
279
for (var i = 0; i < elems.length; i++) {
280
if ((elems[i].namespaceURI == XUL_NS) &&
281
(elems[i].localName == "radio")) {
282
radioChildren.push(elems[i]);
285
return this._radioChildren = radioChildren;
289
<method name="_filterRadioGroup">
290
<parameter name="node"/>
293
switch (node.localName) {
294
case "iradio": return NodeFilter.FILTER_ACCEPT;
295
default: return NodeFilter.FILTER_SKIP;
301
<method name="getIndexOfItem">
302
<parameter name="item"/>
304
return this._getRadioChildren().indexOf(item);
308
<method name="getItemAtIndex">
309
<parameter name="index"/>
312
var children = this._getRadioChildren();
313
return (index >= 0 && index < children.length) ? children[index] : null;
321
<handler event="mousedown">
323
event.preventDefault();
326
<!-- keyboard navigation -->
327
<!-- Here's how keyboard navigation works in radio groups on Windows:
328
The group takes 'focus'
329
The user is then free to navigate around inside the group
330
using the arrow keys. Accessing previous or following radio buttons
331
is done solely through the arrow keys and not the tab button. Tab
332
takes you to the next widget in the tab order -->
333
<handler event="keypress" key=" " phase="target">
334
this.selectedItem = this.focusedItem;
335
this.selectedItem.doCommand();
337
<handler event="keypress" keycode="VK_UP" phase="target">
338
this.checkAdjacentElement(false);
339
event.stopPropagation();
341
<handler event="keypress" keycode="VK_LEFT" phase="target">
342
// left arrow goes back when we are ltr, forward when we are rtl
343
this.checkAdjacentElement(document.defaultView.getComputedStyle(
344
this, "").direction == "rtl");
345
event.stopPropagation();
347
<handler event="keypress" keycode="VK_DOWN" phase="target">
348
this.checkAdjacentElement(true);
349
event.stopPropagation();
351
<handler event="keypress" keycode="VK_RIGHT" phase="target">
352
// right arrow goes forward when we are ltr, back when we are rtl
353
this.checkAdjacentElement(document.defaultView.getComputedStyle(
354
this, "").direction == "ltr");
355
event.stopPropagation();
358
<!-- set a focused attribute on the selected item when the group
359
receives focus so that we can style it as if it were focused even though
360
it is not (Windows platform behaviour is for the group to receive focus,
362
<handler event="focus" phase="target">
364
this.setAttribute("focused", "true");
365
if (this.focusedItem)
368
var val = this.selectedItem;
369
if (!val || val.disabled || val.hidden || val.collapsed) {
370
var children = this._getRadioChildren();
371
for (var i = 0; i < children.length; ++i) {
372
if (!children[i].hidden && !children[i].collapsed && !children[i].disabled) {
378
this.focusedItem = val;
381
<handler event="blur" phase="target">
382
this.removeAttribute("focused");
383
this.focusedItem = null;
388
<binding id="iradio" role="xul:radiobutton"
389
extends="chrome://global/content/bindings/general.xml#control-item">
391
<stylesheet src="chrome://cookiekeeper/skin/iradio.css"/>
395
<xul:box class="iradio-box" orient="vectical" align="center">
396
<xul:image class="iradio-icon" xbl:inherits="disabled,selected,src"/>
400
<implementation implements="nsIDOMXULSelectControlItemElement">
403
// Just clear out the parent's cached list of radio children
404
var control = this.control;
406
control._radioChildren = null;
411
if (!this.radioGroup)
414
var radioList = this.radioGroup.mRadioChildren;
417
for (var i = 0; i < radioList.length; ++i) {
418
if (radioList[i] == this) {
419
radioList.splice(i, 1);
425
<property name="selected" readonly="true">
428
return this.hasAttribute('selected');
432
<property name="radioGroup" readonly="true" onget="return this.control"/>
433
<property name="control" readonly="true">
436
const XUL_NS = "http://www.mozilla.org/keymaster/"
437
+ "gatekeeper/there.is.only.xul";
438
var parent = this.parentNode;
440
if ((parent.namespaceURI == XUL_NS) &&
441
(parent.localName == "iradiogroup")) {
444
parent = parent.parentNode;
447
var group = this.getAttribute("group");
452
parent = this.ownerDocument.getElementById(group);
454
(parent.namespaceURI != XUL_NS) ||
455
(parent.localName != "iradiogroup")) {
464
<handler event="click" button="0">
466
if (!this.disabled) {
467
this.control.selectedItem = this;
468
this.setAttribute("focused", true);
474
<handler event="mousedown" button="0">
476
if (!this.disabled) {
477
this.control.focusedItem = this;
482
<handler event="keypress" key=" " phase="target">
484
this.control.selectedItem = this;
488
<handler event="blur" phase="target">
490
this.removeAttribute("focused");
494
<handler event="focus" phase="target">
496
this.setAttribute("focused", true);
499
<handler event="keypress" keycode="VK_UP" phase="target">
501
this.control.checkAdjacentElement(false);
502
event.stopPropagation();
505
<handler event="keypress" keycode="VK_LEFT" phase="target">
507
// left arrow goes back when we are ltr, forward when we are rtl
508
this.control.checkAdjacentElement(document.defaultView.getComputedStyle(
509
this.control, "").direction == "rtl");
510
event.stopPropagation();
513
<handler event="keypress" keycode="VK_DOWN" phase="target">
515
this.control.checkAdjacentElement(true);
516
event.stopPropagation();
519
<handler event="keypress" keycode="VK_RIGHT" phase="target">
521
// right arrow goes forward when we are ltr, back when we are rtl
522
this.control.checkAdjacentElement(document.defaultView.getComputedStyle(
523
this.control, "").direction == "ltr");
524
event.stopPropagation();