1 <?xml version="1.0"?>
2
3 <!DOCTYPE bindings SYSTEM "chrome://global/locale/preferences.dtd">
4
5 <bindings id="preferencesBindings"
6 xmlns="http://www.mozilla.org/xbl"
7 xmlns:xbl="http://www.mozilla.org/xbl"
8 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
9
10 #
11 # = Preferences Window Framework
12 #
13 # The syntax for use looks something like:
14 #
15 # <prefwindow>
16 # <prefpane id="prefPaneA">
17 # <preferences>
18 # <preference id="preference1" name="app.preference1" type="bool" onchange="foo();"/>
19 # <preference id="preference2" name="app.preference2" type="bool" useDefault="true"/>
20 # </preferences>
21 # <checkbox label="Preference" preference="preference1"/>
22 # </prefpane>
23 # </prefwindow>
24 #
25
26 <binding id="preferences">
27 <implementation implements="nsIObserver">
28 <method name="observe">
29 <parameter name="aSubject"/>
30 <parameter name="aTopic"/>
31 <parameter name="aData"/>
32 <body>
33 <![CDATA[
34 for (var i = 0; i < this.childNodes.length; ++i) {
35 var preference = this.childNodes[i];
36 if (preference.name == aData) {
37 preference.value = preference.valueFromPreferences;
38 }
39 }
40 ]]>
41 </body>
42 </method>
43
44 <method name="fireChangedEvent">
45 <parameter name="aPreference"/>
46 <body>
47 <![CDATA[
48 // Value changed, synthesize an event
49 try {
50 var event = document.createEvent("Events");
51 event.initEvent("change", true, true);
52 aPreference.dispatchEvent(event);
53 }
54 catch (e) {
55 Components.utils.reportError(e);
56 }
57 ]]>
58 </body>
59 </method>
60
61 <field name="service">
62 Components.classes["@mozilla.org/preferences-service;1"]
63 .getService(Components.interfaces.nsIPrefService);
64 </field>
65 <field name="rootBranch">
66 Components.classes["@mozilla.org/preferences-service;1"]
67 .getService(Components.interfaces.nsIPrefBranch);
68 </field>
69 <field name="defaultBranch">
70 this.service.getDefaultBranch("");
71 </field>
72 <field name="rootBranchInternal">
73 Components.classes["@mozilla.org/preferences-service;1"]
74 .getService(Components.interfaces.nsIPrefBranchInternal);
75 </field>
76 </implementation>
77 </binding>
78
79 <binding id="preference">
80 <implementation>
81 <constructor>
82 <![CDATA[
83 // if the element has been inserted without the name attribute set,
84 // we have nothing to do here
85 if (!this.name)
86 return;
87
88 this.preferences.rootBranchInternal
89 .addObserver(this.name, this.preferences, false);
90 // In non-instant apply mode, we must try and use the last saved state
91 // from any previous opens of a child dialog instead of the value from
92 // preferences, to pick up any edits a user may have made.
93 if (document.documentElement.type == "child" &&
94 !this.instantApply && window.opener) {
95 var pdoc = window.opener.document;
96
97 // Try to find a preference element for the same preference.
98 var preference = null;
99 var parentPreferences = pdoc.getElementsByTagName("preferences");
100 for (var k = 0; (k < parentPreferences.length && !preference); ++k) {
101 var parentPrefs = parentPreferences[k]
102 .getElementsByAttribute("name", this.name);
103 for (var l = 0; (l < parentPrefs.length && !preference); ++l) {
104 if (parentPrefs[l].localName == "preference")
105 preference = parentPrefs[l];
106 }
107 }
108 this._setValue(preference ? preference.value
109 : this.valueFromPreferences, false);
110 }
111 else
112 this._setValue(this.valueFromPreferences, false);
113 ]]>
114 </constructor>
115 <destructor>
116 this.preferences.rootBranchInternal
117 .removeObserver(this.name, this.preferences);
118 </destructor>
119
120 <property name="instantApply">
121 <getter>
122 return this.getAttribute("instantApply") == "true" || document.documentElement.instantApply;
123 </getter>
124 </property>
125
126 <property name="preferences" onget="return this.parentNode"/>
127 <property name="name" onget="return this.getAttribute('name');">
128 <setter>
129 if (val == this.name)
130 return val;
131
132 this.preferences.rootBranchInternal
133 .removeObserver(this.name, this.preferences);
134 this.setAttribute('name', val);
135 this.preferences.rootBranchInternal
136 .addObserver(val, this.preferences, false);
137
138 return val;
139 </setter>
140 </property>
141 <property name="type" onget="return this.getAttribute('type');"
142 onset="this.setAttribute('type', val); return val;"/>
143 <property name="inverted" onget="return this.getAttribute('inverted') == 'true';"
144 onset="this.setAttribute('inverted', val); return val;"/>
145 <property name="readonly" onget="return this.getAttribute('readonly') == 'true';"
146 onset="this.setAttribute('readonly', val); return val;"/>
147
148 <field name="_value">null</field>
149 <method name="_setValue">
150 <parameter name="aValue"/>
151 <parameter name="aUpdate"/>
152 <body>
153 <![CDATA[
154 if (aUpdate && this.value != aValue) {
155 this._value = aValue;
156 if (this.instantApply)
157 this.valueFromPreferences = aValue;
158 this.preferences.fireChangedEvent(this);
159 }
160 else if (!aUpdate) {
161 this._value = aValue;
162 this.updateElements();
163 }
164 return aValue;
165 ]]>
166 </body>
167 </method>
168 <property name="value" onget="return this._value" onset="return this._setValue(val, true);"/>
169
170 <property name="locked">
171 <getter>
172 return this.preferences.rootBranch.prefIsLocked(this.name);
173 </getter>
174 </property>
175
176 <property name="disabled">
177 <getter>
178 return this.getAttribute("disabled") == "true";
179 </getter>
180 <setter>
181 <![CDATA[
182 if (val)
183 this.setAttribute("disabled", "true");
184 else
185 this.removeAttribute("disabled");
186
187 if (!this.id)
188 return val;
189
190 var elements = document.getElementsByAttribute("preference", this.id);
191 for (var i = 0; i < elements.length; ++i) {
192 elements[i].disabled = val;
193
194 var labels = document.getElementsByAttribute("control", elements[i].id);
195 for (var j = 0; j < labels.length; ++j)
196 labels[j].disabled = val;
197 }
198
199 return val;
200 ]]>
201 </setter>
202 </property>
203
204 <property name="tabIndex">
205 <getter>
206 return parseInt(this.getAttribute("tabindex"));
207 </getter>
208 <setter>
209 <![CDATA[
210 if (val)
211 this.setAttribute("tabindex", val);
212 else
213 this.removeAttribute("tabindex");
214
215 if (!this.id)
216 return val;
217
218 var elements = document.getElementsByAttribute("preference", this.id);
219 for (var i = 0; i < elements.length; ++i) {
220 elements[i].tabIndex = val;
221
222 var labels = document.getElementsByAttribute("control", elements[i].id);
223 for (var j = 0; j < labels.length; ++j)
224 labels[j].tabIndex = val;
225 }
226
227 return val;
228 ]]>
229 </setter>
230 </property>
231
232 <property name="hasUserValue">
233 <getter>
234 return this.preferences.rootBranch.prefHasUserValue(this.name);
235 </getter>
236 </property>
237
238 <method name="reset">
239 <body>
240 this.preferences.rootBranch.clearUserPref(this.name);
241 </body>
242 </method>
243
244 <field name="_useDefault">false</field>
245 <property name="defaultValue">
246 <getter>
247 <![CDATA[
248 this._useDefault = true;
249 var val = this.valueFromPreferences;
250 this._useDefault = false;
251 return val;
252 ]]>
253 </getter>
254 </property>
255
256 <property name="_branch">
257 <getter>
258 return this._useDefault ? this.preferences.defaultBranch : this.preferences.rootBranch;
259 </getter>
260 </property>
261
262 <field name="batching">false</field>
263
264 <method name="_reportUnknownType">
265 <body>
266 <![CDATA[
267 var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
268 .getService(Components.interfaces.nsIConsoleService);
269 var msg = "<preference> with id='" + this.id + "' and name='" +
270 this.name + "' has unknown type '" + this.type + "'.";
271 consoleService.logStringMessage(msg);
272 ]]>
273 </body>
274 </method>
275
276 <property name="valueFromPreferences">
277 <getter>
278 <![CDATA[
279 try {
280 // Force a resync of value with preferences.
281 switch (this.type) {
282 case "int":
283 return this._branch.getIntPref(this.name);
284 case "bool":
285 var val = this._branch.getBoolPref(this.name);
286 return this.inverted ? !val : val;
287 case "wstring":
288 return this._branch
289 .getComplexValue(this.name, Components.interfaces.nsIPrefLocalizedString)
290 .data;
291 case "string":
292 case "unichar":
293 return this._branch
294 .getComplexValue(this.name, Components.interfaces.nsISupportsString)
295 .data;
296 case "fontname":
297 var family = this._branch
298 .getComplexValue(this.name, Components.interfaces.nsISupportsString)
299 .data;
300 var fontEnumerator = Components.classes["@mozilla.org/gfx/fontenumerator;1"]
301 .createInstance(Components.interfaces.nsIFontEnumerator);
302 return fontEnumerator.getStandardFamilyName(family);
303 case "file":
304 var f = this._branch
305 .getComplexValue(this.name, Components.interfaces.nsILocalFile);
306 return f;
307 default:
308 this._reportUnknownType();
309 }
310 }
311 catch (e) { }
312 return null;
313 ]]>
314 </getter>
315 <setter>
316 <![CDATA[
317 if (this.readonly || this.valueFromPreferences == val)
318 return val;
319
320 // Force a resync of preferences with value.
321 switch (this.type) {
322 case "int":
323 this.preferences.rootBranch.setIntPref(this.name, val);
324 break;
325 case "bool":
326 this.preferences.rootBranch.setBoolPref(this.name, this.inverted ? !val : val);
327 break;
328 case "wstring":
329 var pls = Components.classes["@mozilla.org/pref-localizedstring;1"]
330 .createInstance(Components.interfaces.nsIPrefLocalizedString);
331 pls.data = val;
332 this.preferences.rootBranch
333 .setComplexValue(this.name, Components.interfaces.nsIPrefLocalizedString, pls);
334 break;
335 case "string":
336 case "unichar":
337 case "fontname":
338 var iss = Components.classes["@mozilla.org/supports-string;1"]
339 .createInstance(Components.interfaces.nsISupportsString);
340 iss.data = val;
341 this.preferences.rootBranch
342 .setComplexValue(this.name, Components.interfaces.nsISupportsString, iss);
343 break;
344 case "file":
345 var lf;
346 if (typeof(val) == "string") {
347 lf = Components.classes["@mozilla.org/file/local;1"]
348 .createInstance(Components.interfaces.nsILocalFile);
349 lf.persistentDescriptor = val;
350 if (!lf.exists())
351 lf.initWithPath(val);
352 }
353 else
354 lf = val.QueryInterface(Components.interfaces.nsILocalFile);
355 this.preferences.rootBranch
356 .setComplexValue(this.name, Components.interfaces.nsILocalFile, lf);
357 break;
358 default:
359 this._reportUnknownType();
360 }
361 if (!this.batching)
362 this.preferences.service.savePrefFile(null);
363 return val;
364 ]]>
365 </setter>
366 </property>
367
368 <method name="setElementValue">
369 <parameter name="aElement"/>
370 <body>
371 <![CDATA[
372 if (this.locked)
373 aElement.disabled = true;
374
375 if (!this.isElementEditable(aElement))
376 return;
377
378 var rv = undefined;
379 if (aElement.hasAttribute("onsyncfrompreference")) {
380 // Value changed, synthesize an event
381 try {
382 var event = document.createEvent("Events");
383 event.initEvent("syncfrompreference", true, true);
384 var f = new Function ("event",
385 aElement.getAttribute("onsyncfrompreference"));
386 rv = f.call(aElement, event);
387 }
388 catch (e) {
389 Components.utils.reportError(e);
390 }
391 }
392 var val = rv !== undefined ? rv : (this.instantApply ? this.valueFromPreferences : this.value);
393
394 /**
395 * Initialize a UI element property with a value. Handles the case
396 * where an element has not yet had a XBL binding attached for it and
397 * the property setter does not yet exist by setting the same attribute
398 * on the XUL element using DOM apis and assuming the element's
399 * constructor or property getters appropriately handle this state.
400 */
401 function setValue(element, attribute, value) {
402 if (attribute in element)
403 element[attribute] = value;
404 else
405 element.setAttribute(attribute, value);
406 }
407 if (aElement.localName == "checkbox" ||
408 aElement.localName == "listitem")
409 setValue(aElement, "checked", val);
410 else if (aElement.localName == "colorpicker")
411 setValue(aElement, "color", val);
412 else if (aElement.localName == "textbox") {
413 // XXXmano Bug 303998: Avoid a caret placement issue if either the
414 // preference observer or its setter calls updateElements as a result
415 // of the input event handler.
416 if (aElement.value !== val)
417 setValue(aElement, "value", val);
418 }
419 else
420 setValue(aElement, "value", val);
421 ]]>
422 </body>
423 </method>
424
425 <method name="getElementValue">
426 <parameter name="aElement"/>
427 <body>
428 <![CDATA[
429 if (aElement.hasAttribute("onsynctopreference")) {
430 // Value changed, synthesize an event
431 try {
432 var event = document.createEvent("Events");
433 event.initEvent("synctopreference", true, true);
434 var f = new Function ("event",
435 aElement.getAttribute("onsynctopreference"));
436 var rv = f.call(aElement, event);
437 if (rv !== undefined)
438 return rv;
439 }
440 catch (e) {
441 Components.utils.reportError(e);
442 }
443 }
444
445 /**
446 * Read the value of an attribute from an element, assuming the
447 * attribute is a property on the element's node API. If the property
448 * is not present in the API, then assume its value is contained in
449 * an attribute, as is the case before a binding has been attached.
450 */
451 function getValue(element, attribute) {
452 if (attribute in element)
453 return element[attribute];
454 return element.getAttribute(attribute);
455 }
456 if (aElement.localName == "checkbox" ||
457 aElement.localName == "listitem")
458 var value = getValue(aElement, "checked");
459 else if (aElement.localName == "colorpicker")
460 value = getValue(aElement, "color");
461 else
462 value = getValue(aElement, "value");
463
464 switch (this.type) {
465 case "int":
466 var intValue = parseInt(value, 10) + '';
467 return intValue != "NaN" ? intValue : 0;
468 case "bool":
469 return typeof(value) == "boolean" ? value : value == "true";
470 }
471 return value;
472 ]]>
473 </body>
474 </method>
475
476 <method name="isElementEditable">
477 <parameter name="aElement"/>
478 <body>
479 <![CDATA[
480 switch (aElement.localName) {
481 case "checkbox":
482 case "colorpicker":
483 case "radiogroup":
484 case "textbox":
485 case "listitem":
486 case "listbox":
487 case "menulist":
488 return true;
489 }
490 return aElement.getAttribute("preference-editable") == "true";
491 ]]>
492 </body>
493 </method>
494
495 <method name="updateElements">
496 <body>
497 <![CDATA[
498 if (!this.id)
499 return;
500
501 // This "change" event handler tracks changes made to preferences by
502 // sources other than the user in this window.
503 var elements = document.getElementsByAttribute("preference", this.id);
504 for (var i = 0; i < elements.length; ++i)
505 this.setElementValue(elements[i]);
506 ]]>
507 </body>
508 </method>
509 </implementation>
510
511 <handlers>
512 <handler event="change">
513 this.updateElements();
514 </handler>
515 </handlers>
516 </binding>
517
518 <binding id="prefwindow"
519 extends="chrome://global/content/bindings/dialog.xml#dialog">
520 <resources>
521 <stylesheet src="chrome://global/skin/preferences.css"/>
522 </resources>
523 <content dlgbuttons="accept,cancel" persist="lastSelected screenX screenY"
524 closebuttonlabel="&preferencesCloseButton.label;"
525 closebuttonaccesskey="&preferencesCloseButton.accesskey;"
526 role="dialog"
527 #ifdef XP_WIN
528 title="&preferencesDefaultTitleWin.title;">
529 #else
530 title="&preferencesDefaultTitleMac.title;">
531 #endif
532 <xul:radiogroup anonid="selector" orient="horizontal" class="paneSelector chromeclass-toolbar"
533 role="listbox"/> <!-- Expose to accessibility APIs as a listbox -->
534 <xul:hbox flex="1" class="paneDeckContainer">
535 <xul:deck anonid="paneDeck" flex="1">
536 <children includes="prefpane"/>
537 </xul:deck>
538 </xul:hbox>
539 <xul:hbox anonid="dlg-buttons" class="prefWindow-dlgbuttons"
540 #ifdef XP_UNIX
541 >
542 <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
543 <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/>
544 <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
545 <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
546 <xul:spacer anonid="spacer" flex="1"/>
547 <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/>
548 <xul:button dlgtype="accept" class="dialog-button" icon="accept"/>
549 #else
550 pack="end">
551 <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
552 <xul:spacer anonid="spacer" flex="1"/>
553 <xul:button dlgtype="accept" class="dialog-button" icon="accept"/>
554 <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
555 <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/>
556 <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/>
557 <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
558 #endif
559 </xul:hbox>
560 <xul:hbox>
561 <children/>
562 </xul:hbox>
563 </content>
564 <implementation implements="nsITimerCallback">
565 <constructor>
566 <![CDATA[
567 if (this.type != "child") {
568 var psvc = Components.classes["@mozilla.org/preferences-service;1"]
569 .getService(Components.interfaces.nsIPrefBranch);
570 this.instantApply = psvc.getBoolPref("browser.preferences.instantApply");
571 if (this.instantApply) {
572 var docElt = document.documentElement;
573 var acceptButton = docElt.getButton("accept");
574 acceptButton.hidden = true;
575 var cancelButton = docElt.getButton("cancel");
576 #ifdef XP_MACOSX
577 // no buttons on Mac except Help
578 cancelButton.hidden = true;
579 // Also, don't fire onDialogAccept on enter
580 acceptButton.disabled = true;
581 #else
582 // morph the Cancel button into the Close button
583 cancelButton.setAttribute ("icon", "close");
584 cancelButton.label = docElt.getAttribute("closebuttonlabel");
585 cancelButton.accesskey = docElt.getAttribute("closebuttonaccesskey");
586 #endif
587 }
588 }
589 this.setAttribute("animated", this._shouldAnimate ? "true" : "false");
590 var panes = this.preferencePanes;
591
592 var lastPane = null;
593 if (this.lastSelected) {
594 lastPane = document.getElementById(this.lastSelected);
595 if (!lastPane) {
596 this.lastSelected = null;
597 }
598 }
599
600 var paneToLoad;
601 if ("arguments" in window && window.arguments[0] && document.getElementById(window.arguments[0]) && document.getElementById(window.arguments[0]).nodeName == "prefpane") {
602 paneToLoad = document.getElementById(window.arguments[0]);
603 this.lastSelected = paneToLoad.id;
604 }
605 else if (lastPane)
606 paneToLoad = lastPane;
607 else
608 paneToLoad = panes[0];
609
610 for (var i = 0; i < panes.length; ++i) {
611 this._makePaneButton(panes[i]);
612 if (panes[i].loaded) {
613 // Inline pane content, fire load event to force initialization.
614 this._fireEvent("paneload", panes[i]);
615 }
616 }
617 this.showPane(paneToLoad);
618
619 if (panes.length == 1)
620 this._selector.setAttribute("collapsed", "true");
621 ]]>
622 </constructor>
623
624 <destructor>
625 <![CDATA[
626 // Release timers to avoid reference cycles.
627 if (this._animateTimer) {
628 this._animateTimer.cancel();
629 this._animateTimer = null;
630 }
631 if (this._fadeTimer) {
632 this._fadeTimer.cancel();
633 this._fadeTimer = null;
634 }
635 ]]>
636 </destructor>
637
638 <field name="instantApply">false</field>
639
640 <property name="preferencePanes"
641 onget="return this.getElementsByTagName('prefpane');"/>
642
643 <property name="type" onget="return this.getAttribute('type');"/>
644 <property name="_paneDeck"
645 onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'paneDeck');"/>
646 <property name="_paneDeckContainer"
647 onget="return document.getAnonymousElementByAttribute(this, 'class', 'paneDeckContainer');"/>
648 <property name="_selector"
649 onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'selector');"/>
650 <property name="lastSelected"
651 onget="return this.getAttribute('lastSelected');">
652 <setter>
653 this.setAttribute("lastSelected", val);
654 document.persist(this.id, "lastSelected");
655 return val;
656 </setter>
657 </property>
658 <property name="currentPane"
659 onset="return this._currentPane = val;">
660 <getter>
661 if (!this._currentPane)
662 this._currentPane = this.preferencePanes[0];
663
664 return this._currentPane;
665 </getter>
666 </property>
667 <field name="_currentPane">null</field>
668
669
670 <method name="_makePaneButton">
671 <parameter name="aPaneElement"/>
672 <body>
673 <![CDATA[
674 var radio = document.createElement("radio");
675 radio.setAttribute("pane", aPaneElement.id);
676 radio.setAttribute("label", aPaneElement.label);
677 // Expose preference group choice to accessibility APIs as an unchecked list item
678 // The parent group is exposed to accessibility APIs as a list
679 if (aPaneElement.image)
680 radio.setAttribute("src", aPaneElement.image);
681 radio.style.listStyleImage = aPaneElement.style.listStyleImage;
682 this._selector.appendChild(radio);
683 return radio;
684 ]]>
685 </body>
686 </method>
687
688 <method name="showPane">
689 <parameter name="aPaneElement"/>
690 <body>
691 <![CDATA[
692 if (!aPaneElement)
693 return;
694
695 this._selector.selectedItem = document.getAnonymousElementByAttribute(this, "pane", aPaneElement.id);
696 if (!aPaneElement.loaded) {
697 function OverlayLoadObserver(aPane)
698 {
699 this._pane = aPane;
700 }
701 OverlayLoadObserver.prototype = {
702 _outer: this,
703 observe: function (aSubject, aTopic, aData)
704 {
705 this._pane.loaded = true;
706 this._outer._fireEvent("paneload", this._pane);
707 this._outer._selectPane(this._pane);
708 }
709 };
710
711 var obs = new OverlayLoadObserver(aPaneElement);
712 document.loadOverlay(aPaneElement.src, obs);
713 }
714 else
715 this._selectPane(aPaneElement);
716 ]]>
717 </body>
718 </method>
719
720 <method name="_fireEvent">
721 <parameter name="aEventName"/>
722 <parameter name="aTarget"/>
723 <body>
724 <![CDATA[
725 // Panel loaded, synthesize a load event.
726 try {
727 var event = document.createEvent("Events");
728 event.initEvent(aEventName, true, true);
729 var cancel = !aTarget.dispatchEvent(event);
730 if (aTarget.hasAttribute("on" + aEventName)) {
731 var fn = new Function ("event", aTarget.getAttribute("on" + aEventName));
732 var rv = fn.call(aTarget, event);
733 if (rv == false)
734 cancel = true;
735 }
736 return !cancel;
737 }
738 catch (e) {
739 Components.utils.reportError(e);
740 }
741 return false;
742 ]]>
743 </body>
744 </method>
745
746 <field name="_initialized">false</field>
747 <method name="_selectPane">
748 <parameter name="aPaneElement"/>
749 <body>
750 <![CDATA[
751 #ifdef XP_MACOSX
752 var paneTitle = aPaneElement.label;
753 if (paneTitle != "")
754 document.title = paneTitle;
755 #endif
756 var helpButton = document.documentElement.getButton("help");
757 if (aPaneElement.helpTopic)
758 helpButton.hidden = false;
759 else
760 helpButton.hidden = true;
761
762 // Find this pane's index in the deck and set the deck's
763 // selectedIndex to that value to switch to it.
764 var prefpanes = this.preferencePanes;
765 for (var i = 0; i < prefpanes.length; ++i) {
766 if (prefpanes[i] == aPaneElement) {
767 this._paneDeck.selectedIndex = i;
768
769 if (this.type != "child") {
770 if (aPaneElement.hasAttribute("flex") && this._shouldAnimate &&
771 prefpanes.length > 1)
772 aPaneElement.removeAttribute("flex");
773 // Calling sizeToContent after the first prefpane is loaded
774 // will size the windows contents so style information is
775 // available to calculate correct sizing.
776 if (!this._initialized && prefpanes.length > 1) {
777 if (this._shouldAnimate)
778 this.style.minHeight = 0;
779 window.sizeToContent();
780 }
781
782 var oldPane = this.lastSelected ? document.getElementById(this.lastSelected) : this.preferencePanes[0];
783 oldPane.selected = !(aPaneElement.selected = true);
784 this.lastSelected = aPaneElement.id;
785 this.currentPane = aPaneElement;
786 this._initialized = true;
787
788 // Only animate if we've switched between prefpanes
789 if (this._shouldAnimate && oldPane.id != aPaneElement.id) {
790 aPaneElement.style.opacity = 0.0;
791 this.animate(oldPane, aPaneElement);
792 }
793 else if (!this._shouldAnimate && prefpanes.length > 1) {
794 var targetHeight = parseInt(window.getComputedStyle(this._paneDeckContainer, "").height);
795 var verticalPadding = parseInt(window.getComputedStyle(aPaneElement, "").paddingTop);
796 verticalPadding += parseInt(window.getComputedStyle(aPaneElement, "").paddingBottom);
797 if (aPaneElement.contentHeight > targetHeight - verticalPadding) {
798 // To workaround the bottom border of a groupbox from being
799 // cutoff an hbox with a class of bottomBox may enclose it.
800 // This needs to include its padding to resize properly.
801 // See bug 394433
802 var bottomPadding = 0;
803 var bottomBox = aPaneElement.getElementsByAttribute("class", "bottomBox")[0];
804 if (bottomBox)
805 bottomPadding = parseInt(window.getComputedStyle(bottomBox, "").paddingBottom);
806 window.innerHeight += bottomPadding + verticalPadding + aPaneElement.contentHeight - targetHeight;
807 }
808
809 // XXX rstrong - extend the contents of the prefpane to
810 // prevent elements from being cutoff (see bug 349098).
811 if (aPaneElement.contentHeight + verticalPadding < targetHeight)
812 aPaneElement._content.style.height = targetHeight - verticalPadding + "px";
813 }
814 }
815 break;
816 }
817 }
818 ]]>
819 </body>
820 </method>
821
822 <property name="_shouldAnimate">
823 <getter>
824 <![CDATA[
825 var psvc = Components.classes["@mozilla.org/preferences-service;1"]
826 .getService(Components.interfaces.nsIPrefBranch);
827 #ifdef XP_MACOSX
828 var animate = true;
829 #else
830 var animate = false;
831 #endif
832 try {
833 animate = psvc.getBoolPref("browser.preferences.animateFadeIn");
834 }
835 catch (e) { }
836 return animate;
837 ]]>
838 </getter>
839 </property>
840
841 <method name="animate">
842 <parameter name="aOldPane"/>
843 <parameter name="aNewPane"/>
844 <body>
845 <![CDATA[
846 // if we are already resizing, use currentHeight
847 var oldHeight = this._currentHeight ? this._currentHeight : aOldPane.contentHeight;
848
849 this._multiplier = aNewPane.contentHeight > oldHeight ? 1 : -1;
850 var sizeDelta = Math.abs(oldHeight - aNewPane.contentHeight);
851 this._animateRemainder = sizeDelta % this._animateIncrement;
852
853 this._setUpAnimationTimer(oldHeight);
854 ]]>
855 </body>
856 </method>
857
858 <property name="_sizeIncrement">
859 <getter>
860 <![CDATA[
861 var lastSelectedPane = document.getElementById(this.lastSelected);
862 var increment = this._animateIncrement * this._multiplier;
863 var newHeight = this._currentHeight + increment;
864 if ((this._multiplier > 0 && this._currentHeight >= lastSelectedPane.contentHeight) ||
865 (this._multiplier < 0 && this._currentHeight <= lastSelectedPane.contentHeight))
866 return 0;
867
868 if ((this._multiplier > 0 && newHeight > lastSelectedPane.contentHeight) ||
869 (this._multiplier < 0 && newHeight < lastSelectedPane.contentHeight))
870 increment = this._animateRemainder * this._multiplier;
871 return increment;
872 ]]>
873 </getter>
874 </property>
875
876 <method name="notify">
877 <parameter name="aTimer"/>
878 <body>
879 <![CDATA[
880 if (!document)
881 aTimer.cancel();
882
883 if (aTimer == this._animateTimer) {
884 var increment = this._sizeIncrement;
885 if (increment != 0) {
886 window.innerHeight += increment;
887 this._currentHeight += increment;
888 }
889 else {
890 aTimer.cancel();
891 this._setUpFadeTimer();
892 }
893 } else if (aTimer == this._fadeTimer) {
894 var elt = document.getElementById(this.lastSelected);
895 var newOpacity = parseFloat(window.getComputedStyle(elt, "").opacity) + this._fadeIncrement;
896 if (newOpacity < 1.0)
897 elt.style.opacity = newOpacity;
898 else {
899 aTimer.cancel();
900 elt.style.opacity = 1.0;
901 }
902 }
903 ]]>
904 </body>
905 </method>
906
907 <method name="_setUpAnimationTimer">
908 <parameter name="aStartHeight"/>
909 <body>
910 <![CDATA[
911 if (!this._animateTimer)
912 this._animateTimer = Components.classes["@mozilla.org/timer;1"]
913 .createInstance(Components.interfaces.nsITimer);
914 else
915 this._animateTimer.cancel();
916 this._currentHeight = aStartHeight;
917
918 this._animateTimer.initWithCallback(this, this._animateDelay,
919 Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
920 ]]>
921 </body>
922 </method>
923
924 <method name="_setUpFadeTimer">
925 <body>
926 <![CDATA[
927 if (!this._fadeTimer)
928 this._fadeTimer = Components.classes["@mozilla.org/timer;1"]
929 .createInstance(Components.interfaces.nsITimer);
930 else
931 this._fadeTimer.cancel();
932
933 this._fadeTimer.initWithCallback(this, this._fadeDelay,
934 Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
935 ]]>
936 </body>
937 </method>
938
939 <field name="_animateTimer">null</field>
940 <field name="_fadeTimer">null</field>
941 <field name="_animateDelay">15</field>
942 <field name="_animateIncrement">40</field>
943 <field name="_fadeDelay">5</field>
944 <field name="_fadeIncrement">0.40</field>
945 <field name="_animateRemainder">0</field>
946 <field name="_currentHeight">0</field>
947 <field name="_multiplier">0</field>
948
949 <method name="addPane">
950 <parameter name="aPaneElement"/>
951 <body>
952 <![CDATA[
953 this.appendChild(aPaneElement);
954
955 // Set up pane button
956 this._makePaneButton(aPaneElement);
957 ]]>
958 </body>
959 </method>
960
961 <method name="openSubDialog">
962 <parameter name="aURL"/>
963 <parameter name="aFeatures"/>
964 <parameter name="aParams"/>
965 <body>
966 return openDialog(aURL, "", "modal,centerscreen,resizable=no" + (aFeatures != "" ? ("," + aFeatures) : ""), aParams);
967 </body>
968 </method>
969
970 <method name="openWindow">
971 <parameter name="aWindowType"/>
972 <parameter name="aURL"/>
973 <parameter name="aFeatures"/>
974 <parameter name="aParams"/>
975 <body>
976 <![CDATA[
977 var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
978 .getService(Components.interfaces.nsIWindowMediator);
979 var win = aWindowType ? wm.getMostRecentWindow(aWindowType) : null;
980 if (win) {
981 if ("initWithParams" in win)
982 win.initWithParams(aParams);
983 win.focus();
984 }
985 else {
986 var features = "resizable,dialog=no,centerscreen" + (aFeatures != "" ? ("," + aFeatures) : "");
987 var parentWindow = (this.instantApply || !window.opener || window.opener.closed) ? window : window.opener;
988 win = parentWindow.openDialog(aURL, "_blank", features, aParams);
989 }
990 return win;
991 ]]>
992 </body>
993 </method>
994 </implementation>
995 <handlers>
996 <handler event="dialogaccept">
997 <![CDATA[
998 if (!this._fireEvent("beforeaccept", this))
999 return;
1000
1001 if (this.type == "child" && window.opener) {
1002 var psvc = Components.classes["@mozilla.org/preferences-service;1"]
1003 .getService(Components.interfaces.nsIPrefBranch);
1004 var instantApply = psvc.getBoolPref("browser.preferences.instantApply");
1005 if (instantApply) {
1006 var panes = this.preferencePanes;
1007 for (var i = 0; i < panes.length; ++i)
1008 panes[i].writePreferences(true);
1009 }
1010 else {
1011 // Clone all the preferences elements from the child document and
1012 // insert them into the pane collection of the parent.
1013 var pdoc = window.opener.document;
1014 if (pdoc.documentElement.localName == "prefwindow") {
1015 var currentPane = pdoc.documentElement.currentPane;
1016 var id = window.location.href + "#childprefs";
1017 var childPrefs = pdoc.getElementById(id);
1018 if (!childPrefs) {
1019 var childPrefs = pdoc.createElement("preferences");
1020 currentPane.appendChild(childPrefs);
1021 childPrefs.id = id;
1022 }
1023 var panes = this.preferencePanes;
1024 for (var i = 0; i < panes.length; ++i) {
1025 var preferences = panes[i].preferences;
1026 for (var j = 0; j < preferences.length; ++j) {
1027 // Try to find a preference element for the same preference.
1028 var preference = null;
1029 var parentPreferences = pdoc.getElementsByTagName("preferences");
1030 for (var k = 0; (k < parentPreferences.length && !preference); ++k) {
1031 var parentPrefs = parentPreferences[k]
1032 .getElementsByAttribute("name", preferences[j].name);
1033 for (var l = 0; (l < parentPrefs.length && !preference); ++l) {
1034 if (parentPrefs[l].localName == "preference")
1035 preference = parentPrefs[l];
1036 }
1037 }
1038 if (!preference) {
1039 // No matching preference in the parent window.
1040 preference = pdoc.createElement("preference");
1041 childPrefs.appendChild(preference);
1042 preference.name = preferences[j].name;
1043 preference.type = preferences[j].type;
1044 preference.inverted = preferences[j].inverted;
1045 preference.readonly = preferences[j].readonly;
1046 preference.disabled = preferences[j].disabled;
1047 }
1048 preference.value = preferences[j].value;
1049 }
1050 }
1051 }
1052 }
1053 }
1054 else {
1055 var panes = this.preferencePanes;
1056 for (var i = 0; i < panes.length; ++i)
1057 panes[i].writePreferences(false);
1058
1059 var psvc = Components.classes["@mozilla.org/preferences-service;1"]
1060 .getService(Components.interfaces.nsIPrefService);
1061 psvc.savePrefFile(null);
1062 }
1063 ]]>
1064 </handler>
1065 <handler event="command">
1066 if (event.originalTarget.hasAttribute("pane")) {
1067 var pane = document.getElementById(event.originalTarget.getAttribute("pane"));
1068 event.originalTarget.parentNode.parentNode.showPane(pane);
1069 }
1070 </handler>
1071
1072 <handler event="keypress" key="&windowClose.key;" modifiers="accel" phase="capturing">
1073 if (this.instantApply)
1074 window.close();
1075 </handler>
1076 </handlers>
1077 </binding>
1078
1079 <binding id="prefpane">
1080 <resources>
1081 <stylesheet src="chrome://global/skin/preferences.css"/>
1082 </resources>
1083 <content>
1084 <xul:vbox class="content-box" xbl:inherits="flex">
1085 <children/>
1086 </xul:vbox>
1087 </content>
1088 <implementation>
1089 <method name="writePreferences">
1090 <parameter name="aFlushToDisk"/>
1091 <body>
1092 <![CDATA[
1093 // Write all values to preferences.
1094 var preferences = this.preferences;
1095 for (var i = 0; i < preferences.length; ++i) {
1096 var preference = preferences[i];
1097 preference.batching = true;
1098 preference.valueFromPreferences = preference.value;
1099 preference.batching = false;
1100 }
1101 if (aFlushToDisk) {
1102 var psvc = Components.classes["@mozilla.org/preferences-service;1"]
1103 .getService(Components.interfaces.nsIPrefService);
1104 psvc.savePrefFile(null);
1105 }
1106 ]]>
1107 </body>
1108 </method>
1109
1110 <property name="src"
1111 onget="return this.getAttribute('src');"
1112 onset="this.setAttribute('src', val); return val;"/>
1113 <property name="selected"
1114 onget="return this.getAttribute('selected') == 'true';"
1115 onset="this.setAttribute('selected', val); return val;"/>
1116 <property name="image"
1117 onget="return this.getAttribute('image');"
1118 onset="this.setAttribute('image', val); return val;"/>
1119 <property name="label"
1120 onget="return this.getAttribute('label');"
1121 onset="this.setAttribute('label', val); return val;"/>
1122
1123 <property name="preferenceElements"
1124 onget="return this.getElementsByAttribute('preference', '*');"/>
1125 <property name="preferences"
1126 onget="return this.getElementsByTagName('preference');"/>
1127
1128 <property name="helpTopic">
1129 <getter>
1130 <![CDATA[
1131 // if there are tabs, and the selected tab provides a helpTopic, return that
1132 var box = this.getElementsByTagName("tabbox");
1133 if (box[0]) {
1134 var tab = box[0].selectedTab;
1135 if (tab && tab.hasAttribute("helpTopic"))
1136 return tab.getAttribute("helpTopic");
1137 }
1138
1139 // otherwise, return the helpTopic of the current panel
1140 return this.getAttribute("helpTopic");
1141 ]]>
1142 </getter>
1143 </property>
1144
1145 <field name="_loaded">false</field>
1146 <property name="loaded"
1147 onget="return !this.src ? true : this._loaded;"
1148 onset="this._loaded = val; return val;"/>
1149
1150 <method name="preferenceForElement">
1151 <parameter name="aElement"/>
1152 <body>
1153 return document.getElementById(aElement.getAttribute("preference"));
1154 </body>
1155 </method>
1156
1157 <method name="getPreferenceElement">
1158 <parameter name="aStartElement"/>
1159 <body>
1160 <![CDATA[
1161 var temp = aStartElement;
1162 while (temp && temp.nodeType == Node.ELEMENT_NODE &&
1163 !temp.hasAttribute("preference"))
1164 temp = temp.parentNode;
1165 return temp.nodeType == Node.ELEMENT_NODE ? temp : aStartElement;
1166 ]]>
1167 </body>
1168 </method>
1169
1170 <method name="userChangedValue">
1171 <parameter name="aElement"/>
1172 <body>
1173 <![CDATA[
1174 var element = this.getPreferenceElement(aElement);
1175 if (element.hasAttribute("preference")) {
1176 var preference = document.getElementById(element.getAttribute("preference"));
1177 var prefVal = preference.getElementValue(element);
1178 preference.value = prefVal;
1179 }
1180 ]]>
1181 </body>
1182 </method>
1183
1184 <property name="contentHeight">
1185 <getter>
1186 var targetHeight = parseInt(window.getComputedStyle(this._content, "").height);
1187 targetHeight += parseInt(window.getComputedStyle(this._content, "").marginTop);
1188 targetHeight += parseInt(window.getComputedStyle(this._content, "").marginBottom);
1189 return targetHeight;
1190 </getter>
1191 </property>
1192 <field name="_content">
1193 document.getAnonymousElementByAttribute(this, "class", "content-box");
1194 </field>
1195 </implementation>
1196 <handlers>
1197 <handler event="command">
1198 // This "command" event handler tracks changes made to preferences by
1199 // the user in this window.
1200 this.userChangedValue(event.target);
1201 </handler>
1202 <handler event="select">
1203 // This "select" event handler tracks changes made to colorpicker
1204 // preferences by the user in this window.
1205 if (event.target.localName == "colorpicker")
1206 this.userChangedValue(event.target);
1207 </handler>
1208 <handler event="change">
1209 // This "change" event handler tracks changes made to preferences by
1210 // the user in this window.
1211 this.userChangedValue(event.target);
1212 </handler>
1213 <handler event="input">
1214 // This "input" event handler tracks changes made to preferences by
1215 // the user in this window.
1216 this.userChangedValue(event.target);
1217 </handler>
1218 <handler event="paneload">
1219 <![CDATA[
1220 // Initialize all values from preferences.
1221 var elements = this.preferenceElements;
1222 for (var i = 0; i < elements.length; ++i) {
1223 try {
1224 var preference = this.preferenceForElement(elements[i]);
1225 preference.setElementValue(elements[i]);
1226 }
1227 catch (e) {
1228 dump("*** No preference found for " + elements[i].getAttribute("preference") + "\n");
1229 }
1230 }
1231 ]]>
1232 </handler>
1233 </handlers>
1234 </binding>
1235
1236 <binding id="panebutton" extends="chrome://global/content/bindings/radio.xml#radio">
1237 <resources>
1238 <stylesheet src="chrome://global/skin/preferences.css"/>
1239 </resources>
1240 <content>
1241 <xul:image class="paneButtonIcon" xbl:inherits="src"/>
1242 <xul:label class="paneButtonLabel" xbl:inherits="value=label"/>
1243 </content>
1244 <implementation implements="nsIAccessible">
1245 <property name="accessibleType" readonly="true">
1246 <getter>
1247 <![CDATA[
1248 return Components.interfaces.nsIAccessibleProvider.XULListitem;
1249 ]]>
1250 </getter>
1251 </property>
1252 </implementation>
1253 </binding>
1254
1255 </bindings>
1256
1257 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
1258 # ***** BEGIN LICENSE BLOCK *****
1259 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
1260 #
1261 # The contents of this file are subject to the Mozilla Public License Version
1262 # 1.1 (the "License"); you may not use this file except in compliance with
1263 # the License. You may obtain a copy of the License at
1264 # http://www.mozilla.org/MPL/
1265 #
1266 # Software distributed under the License is distributed on an "AS IS" basis,
1267 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
1268 # for the specific language governing rights and limitations under the
1269 # License.
1270 #
1271 # The Original Code is the Preferences System.
1272 #
1273 # The Initial Developer of the Original Code is
1274 # Ben Goodger.
1275 # Portions created by the Initial Developer are Copyright (C) 2005
1276 # the Initial Developer. All Rights Reserved.
1277 #
1278 # Contributor(s):
1279 # Ben Goodger <ben@mozilla.org>
1280 # Josh Aas <josh@mozilla.com>
1281 #
1282 # Alternatively, the contents of this file may be used under the terms of
1283 # either the GNU General Public License Version 2 or later (the "GPL"), or
1284 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
1285 # in which case the provisions of the GPL or the LGPL are applicable instead
1286 # of those above. If you wish to allow use of your version of this file only
1287 # under the terms of either the GPL or the LGPL, and not to allow others to
1288 # use your version of this file under the terms of the MPL, indicate your
1289 # decision by deleting the provisions above and replace them with the notice
1290 # and other provisions required by the GPL or the LGPL. If you do not delete
1291 # the provisions above, a recipient may use your version of this file under
1292 # the terms of any one of the MPL, the GPL or the LGPL.
1293 #
1294 # ***** END LICENSE BLOCK *****
1295
1296 #
1297 # This is PrefWindow 6. The Code Could Well Be Ready, Are You?
1298 #
1299 # Historical References:
1300 # PrefWindow V (February 1, 2003)
1301 # PrefWindow IV (April 24, 2000)
1302 # PrefWindow III (January 6, 2000)
1303 # PrefWindow II (???)
1304 # PrefWindow I (June 4, 1999)
1305 #