|
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="buttonBindings" |
|
8 xmlns="http://www.mozilla.org/xbl" |
|
9 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
|
10 xmlns:xbl="http://www.mozilla.org/xbl"> |
|
11 |
|
12 <binding id="button-base" extends="chrome://global/content/bindings/general.xml#basetext" role="xul:button"> |
|
13 <implementation implements="nsIDOMXULButtonElement"> |
|
14 <property name="type" |
|
15 onget="return this.getAttribute('type');" |
|
16 onset="this.setAttribute('type', val); return val;"/> |
|
17 |
|
18 <property name="dlgType" |
|
19 onget="return this.getAttribute('dlgtype');" |
|
20 onset="this.setAttribute('dlgtype', val); return val;"/> |
|
21 |
|
22 <property name="group" |
|
23 onget="return this.getAttribute('group');" |
|
24 onset="this.setAttribute('group', val); return val;"/> |
|
25 |
|
26 <property name="open" onget="return this.hasAttribute('open');"> |
|
27 <setter><![CDATA[ |
|
28 if (this.boxObject instanceof |
|
29 Components.interfaces.nsIMenuBoxObject) { |
|
30 this.boxObject.openMenu(val); |
|
31 } else { |
|
32 // Fall back to just setting the attribute |
|
33 if (val) { |
|
34 this.setAttribute('open', 'true'); |
|
35 } else { |
|
36 this.removeAttribute('open'); |
|
37 } |
|
38 } |
|
39 return val; |
|
40 ]]></setter> |
|
41 </property> |
|
42 |
|
43 <property name="checked" onget="return this.hasAttribute('checked');"> |
|
44 <setter><![CDATA[ |
|
45 if (this.type == "checkbox") { |
|
46 this.checkState = val ? 1 : 0; |
|
47 } else if (this.type == "radio" && val) { |
|
48 var sibs = this.parentNode.getElementsByAttribute("group", this.group); |
|
49 for (var i = 0; i < sibs.length; ++i) |
|
50 sibs[i].removeAttribute("checked"); |
|
51 } |
|
52 |
|
53 if (val) |
|
54 this.setAttribute("checked", "true"); |
|
55 else |
|
56 this.removeAttribute("checked"); |
|
57 |
|
58 return val; |
|
59 ]]></setter> |
|
60 </property> |
|
61 |
|
62 <property name="checkState"> |
|
63 <getter><![CDATA[ |
|
64 var state = this.getAttribute("checkState"); |
|
65 if (state == "") |
|
66 return this.checked ? 1 : 0; |
|
67 else |
|
68 return state == "0" ? 0 : (state == "2" ? 2 : 1); |
|
69 ]]></getter> |
|
70 <setter><![CDATA[ |
|
71 this.setAttribute("checkState", val); |
|
72 return val; |
|
73 ]]></setter> |
|
74 </property> |
|
75 |
|
76 <property name="autoCheck" |
|
77 onget="return this.getAttribute('autoCheck') == 'true';" |
|
78 onset="this.setAttribute('autoCheck', val); return val;"/> |
|
79 |
|
80 <method name ="filterButtons"> |
|
81 <parameter name="node"/> |
|
82 <body> |
|
83 <![CDATA[ |
|
84 // if the node isn't visible, don't descend into it. |
|
85 var cs = node.ownerDocument.defaultView.getComputedStyle(node, null); |
|
86 if (cs.visibility != "visible" || cs.display == "none") { |
|
87 return NodeFilter.FILTER_REJECT; |
|
88 } |
|
89 // but it may be a popup element, in which case we look at "state"... |
|
90 if (cs.display == "-moz-popup" && node.state != "open") { |
|
91 return NodeFilter.FILTER_REJECT; |
|
92 } |
|
93 // OK - the node seems visible, so it is a candidate. |
|
94 if (node.localName == "button" && node.accessKey && !node.disabled) |
|
95 return NodeFilter.FILTER_ACCEPT; |
|
96 return NodeFilter.FILTER_SKIP; |
|
97 ]]> |
|
98 </body> |
|
99 </method> |
|
100 |
|
101 <method name="fireAccessKeyButton"> |
|
102 <parameter name="aSubtree"/> |
|
103 <parameter name="aAccessKeyLower"/> |
|
104 <body> |
|
105 <![CDATA[ |
|
106 var iterator = aSubtree.ownerDocument.createTreeWalker(aSubtree, |
|
107 NodeFilter.SHOW_ELEMENT, |
|
108 this.filterButtons); |
|
109 while (iterator.nextNode()) { |
|
110 var test = iterator.currentNode; |
|
111 if (test.accessKey.toLowerCase() == aAccessKeyLower && |
|
112 !test.disabled && !test.collapsed && !test.hidden) { |
|
113 test.focus(); |
|
114 test.click(); |
|
115 return true; |
|
116 } |
|
117 } |
|
118 return false; |
|
119 ]]> |
|
120 </body> |
|
121 </method> |
|
122 |
|
123 <method name="_handleClick"> |
|
124 <body> |
|
125 <![CDATA[ |
|
126 if (!this.disabled && |
|
127 (this.autoCheck || !this.hasAttribute("autoCheck"))) { |
|
128 |
|
129 if (this.type == "checkbox") { |
|
130 this.checked = !this.checked; |
|
131 } else if (this.type == "radio") { |
|
132 this.checked = true; |
|
133 } |
|
134 } |
|
135 ]]> |
|
136 </body> |
|
137 </method> |
|
138 </implementation> |
|
139 |
|
140 <handlers> |
|
141 <!-- While it would seem we could do this by handling oncommand, we can't |
|
142 because any external oncommand handlers might get called before ours, |
|
143 and then they would see the incorrect value of checked. Additionally |
|
144 a command attribute would redirect the command events anyway.--> |
|
145 <handler event="click" button="0" action="this._handleClick();"/> |
|
146 <handler event="keypress" key=" " action="this._handleClick();"/> |
|
147 |
|
148 <handler event="keypress"> |
|
149 <![CDATA[ |
|
150 if (this.boxObject instanceof Components.interfaces.nsIMenuBoxObject) { |
|
151 if (this.open) |
|
152 return; |
|
153 } else { |
|
154 if (event.keyCode == KeyEvent.DOM_VK_UP || |
|
155 (event.keyCode == KeyEvent.DOM_VK_LEFT && |
|
156 document.defaultView.getComputedStyle(this.parentNode, "") |
|
157 .direction == "ltr") || |
|
158 (event.keyCode == KeyEvent.DOM_VK_RIGHT && |
|
159 document.defaultView.getComputedStyle(this.parentNode, "") |
|
160 .direction == "rtl")) { |
|
161 event.preventDefault(); |
|
162 window.document.commandDispatcher.rewindFocus(); |
|
163 return; |
|
164 } |
|
165 |
|
166 if (event.keyCode == KeyEvent.DOM_VK_DOWN || |
|
167 (event.keyCode == KeyEvent.DOM_VK_RIGHT && |
|
168 document.defaultView.getComputedStyle(this.parentNode, "") |
|
169 .direction == "ltr") || |
|
170 (event.keyCode == KeyEvent.DOM_VK_LEFT && |
|
171 document.defaultView.getComputedStyle(this.parentNode, "") |
|
172 .direction == "rtl")) { |
|
173 event.preventDefault(); |
|
174 window.document.commandDispatcher.advanceFocus(); |
|
175 return; |
|
176 } |
|
177 } |
|
178 |
|
179 if (event.keyCode || event.charCode <= 32 || event.altKey || |
|
180 event.ctrlKey || event.metaKey) |
|
181 return; // No printable char pressed, not a potential accesskey |
|
182 |
|
183 // Possible accesskey pressed |
|
184 var charPressedLower = String.fromCharCode(event.charCode).toLowerCase(); |
|
185 |
|
186 // If the accesskey of the current button is pressed, just activate it |
|
187 if (this.accessKey.toLowerCase() == charPressedLower) { |
|
188 this.click(); |
|
189 return; |
|
190 } |
|
191 |
|
192 // Search for accesskey in the list of buttons for this doc and each subdoc |
|
193 // Get the buttons for the main document and all sub-frames |
|
194 for (var frameCount = -1; frameCount < window.top.frames.length; frameCount++) { |
|
195 var doc = (frameCount == -1)? window.top.document: |
|
196 window.top.frames[frameCount].document |
|
197 if (this.fireAccessKeyButton(doc.documentElement, charPressedLower)) |
|
198 return; |
|
199 } |
|
200 |
|
201 // Test anonymous buttons |
|
202 var dlg = window.top.document; |
|
203 var buttonBox = dlg.getAnonymousElementByAttribute(dlg.documentElement, |
|
204 "anonid", "buttons"); |
|
205 if (buttonBox) |
|
206 this.fireAccessKeyButton(buttonBox, charPressedLower); |
|
207 ]]> |
|
208 </handler> |
|
209 </handlers> |
|
210 </binding> |
|
211 |
|
212 <binding id="button" display="xul:button" |
|
213 extends="chrome://global/content/bindings/button.xml#button-base"> |
|
214 <resources> |
|
215 <stylesheet src="chrome://global/skin/button.css"/> |
|
216 </resources> |
|
217 |
|
218 <content> |
|
219 <children includes="observes|template|menupopup|panel|tooltip"/> |
|
220 <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient" |
|
221 align="center" pack="center" flex="1" anonid="button-box"> |
|
222 <children> |
|
223 <xul:image class="button-icon" xbl:inherits="src=image"/> |
|
224 <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop"/> |
|
225 </children> |
|
226 </xul:hbox> |
|
227 </content> |
|
228 </binding> |
|
229 |
|
230 <binding id="menu" display="xul:menu" |
|
231 extends="chrome://global/content/bindings/button.xml#button"> |
|
232 <content> |
|
233 <children includes="observes|template|menupopup|panel|tooltip"/> |
|
234 <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient" |
|
235 align="center" pack="center" flex="1"> |
|
236 <children> |
|
237 <xul:hbox class="box-inherit" xbl:inherits="align,dir,pack,orient" |
|
238 align="center" pack="center" flex="1"> |
|
239 <xul:image class="button-icon" xbl:inherits="src=image"/> |
|
240 <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop"/> |
|
241 </xul:hbox> |
|
242 <xul:dropmarker class="button-menu-dropmarker" xbl:inherits="open,disabled,label"/> |
|
243 </children> |
|
244 </xul:hbox> |
|
245 </content> |
|
246 |
|
247 <handlers> |
|
248 <handler event="keypress" keycode="VK_RETURN" action="this.open = true;"/> |
|
249 <handler event="keypress" key=" " action="this.open = true;"/> |
|
250 </handlers> |
|
251 </binding> |
|
252 |
|
253 <binding id="menu-button-base" |
|
254 extends="chrome://global/content/bindings/button.xml#button-base"> |
|
255 <implementation implements="nsIDOMEventListener"> |
|
256 <constructor> |
|
257 this.init(); |
|
258 </constructor> |
|
259 |
|
260 <method name="init"> |
|
261 <body> |
|
262 <![CDATA[ |
|
263 var btn = document.getAnonymousElementByAttribute(this, "anonid", "button"); |
|
264 if (!btn) |
|
265 throw "XBL binding for <button type=\"menu-button\"/> binding must contain an element with anonid=\"button\""; |
|
266 |
|
267 var menubuttonParent = this; |
|
268 btn.addEventListener("mouseover", function() { |
|
269 if (!this.disabled) |
|
270 menubuttonParent.buttonover = true; |
|
271 }, true); |
|
272 btn.addEventListener("mouseout", function() { |
|
273 menubuttonParent.buttonover = false; |
|
274 }, true); |
|
275 btn.addEventListener("mousedown", function() { |
|
276 if (!this.disabled) { |
|
277 menubuttonParent.buttondown = true; |
|
278 document.addEventListener("mouseup", menubuttonParent, true); |
|
279 } |
|
280 }, true); |
|
281 ]]> |
|
282 </body> |
|
283 </method> |
|
284 |
|
285 <property name="buttonover" onget="return this.getAttribute('buttonover');"> |
|
286 <setter> |
|
287 <![CDATA[ |
|
288 var v = val || val == "true"; |
|
289 if (!v && this.buttondown) { |
|
290 this.buttondown = false; |
|
291 this._pendingActive = true; |
|
292 } |
|
293 else { |
|
294 if (this._pendingActive) { |
|
295 this.buttondown = true; |
|
296 this._pendingActive = false; |
|
297 } |
|
298 } |
|
299 |
|
300 if (v) |
|
301 this.setAttribute("buttonover", "true"); |
|
302 else |
|
303 this.removeAttribute("buttonover"); |
|
304 return val; |
|
305 ]]> |
|
306 </setter> |
|
307 </property> |
|
308 |
|
309 <property name="buttondown" onget="return this.getAttribute('buttondown') == 'true';"> |
|
310 <setter> |
|
311 <![CDATA[ |
|
312 if (val || val == "true") |
|
313 this.setAttribute("buttondown", "true"); |
|
314 else |
|
315 this.removeAttribute("buttondown"); |
|
316 return val; |
|
317 ]]> |
|
318 </setter> |
|
319 </property> |
|
320 |
|
321 <field name="_pendingActive">false</field> |
|
322 |
|
323 <method name="handleEvent"> |
|
324 <parameter name="aEvent"/> |
|
325 <body> |
|
326 <![CDATA[ |
|
327 this._pendingActive = false; |
|
328 this.buttondown = false; |
|
329 document.removeEventListener("mouseup", this, true); |
|
330 ]]> |
|
331 </body> |
|
332 </method> |
|
333 |
|
334 </implementation> |
|
335 |
|
336 <handlers> |
|
337 <handler event="keypress" keycode="VK_RETURN"> |
|
338 if (event.originalTarget == this) |
|
339 this.open = true; |
|
340 </handler> |
|
341 <handler event="keypress" key=" "> |
|
342 if (event.originalTarget == this) |
|
343 this.open = true; |
|
344 </handler> |
|
345 </handlers> |
|
346 </binding> |
|
347 |
|
348 <binding id="menu-button" display="xul:menu" |
|
349 extends="chrome://global/content/bindings/button.xml#menu-button-base"> |
|
350 <resources> |
|
351 <stylesheet src="chrome://global/skin/button.css"/> |
|
352 </resources> |
|
353 |
|
354 <content> |
|
355 <children includes="observes|template|menupopup|panel|tooltip"/> |
|
356 <xul:button class="box-inherit button-menubutton-button" |
|
357 anonid="button" flex="1" allowevents="true" |
|
358 xbl:inherits="disabled,crop,image,label,accesskey,command, |
|
359 buttonover,buttondown,align,dir,pack,orient"> |
|
360 <children/> |
|
361 </xul:button> |
|
362 <xul:dropmarker class="button-menubutton-dropmarker" xbl:inherits="open,disabled,label"/> |
|
363 </content> |
|
364 </binding> |
|
365 |
|
366 <binding id="button-image" display="xul:button" |
|
367 extends="chrome://global/content/bindings/button.xml#button"> |
|
368 <content> |
|
369 <xul:image class="button-image-icon" xbl:inherits="src=image"/> |
|
370 </content> |
|
371 </binding> |
|
372 |
|
373 <binding id="button-repeat" display="xul:autorepeatbutton" |
|
374 extends="chrome://global/content/bindings/button.xml#button"/> |
|
375 |
|
376 </bindings> |