diff options
Diffstat (limited to 'toolkit/content/widgets/preferences.xml')
-rw-r--r-- | toolkit/content/widgets/preferences.xml | 1411 |
1 files changed, 1411 insertions, 0 deletions
diff --git a/toolkit/content/widgets/preferences.xml b/toolkit/content/widgets/preferences.xml new file mode 100644 index 000000000..9fb2ac7ea --- /dev/null +++ b/toolkit/content/widgets/preferences.xml @@ -0,0 +1,1411 @@ +<?xml version="1.0"?> + +<!DOCTYPE bindings [ + <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd"> + %preferencesDTD; + <!ENTITY % globalKeysDTD SYSTEM "chrome://global/locale/globalKeys.dtd"> + %globalKeysDTD; +]> + +<bindings id="preferencesBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xbl="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +# +# = Preferences Window Framework +# +# The syntax for use looks something like: +# +# <prefwindow> +# <prefpane id="prefPaneA"> +# <preferences> +# <preference id="preference1" name="app.preference1" type="bool" onchange="foo();"/> +# <preference id="preference2" name="app.preference2" type="bool" useDefault="true"/> +# </preferences> +# <checkbox label="Preference" preference="preference1"/> +# </prefpane> +# </prefwindow> +# + + <binding id="preferences"> + <implementation implements="nsIObserver"> + <method name="_constructAfterChildren"> + <body> + <![CDATA[ + // This method will be called after each one of the child + // <preference> elements is constructed. Its purpose is to propagate + // the values to the associated form elements + + var elements = this.getElementsByTagName("preference"); + for (let element of elements) { + if (!element._constructed) { + return; + } + } + for (let element of elements) { + element.updateElements(); + } + ]]> + </body> + </method> + <method name="observe"> + <parameter name="aSubject"/> + <parameter name="aTopic"/> + <parameter name="aData"/> + <body> + <![CDATA[ + for (var i = 0; i < this.childNodes.length; ++i) { + var preference = this.childNodes[i]; + if (preference.name == aData) { + preference.value = preference.valueFromPreferences; + } + } + ]]> + </body> + </method> + + <method name="fireChangedEvent"> + <parameter name="aPreference"/> + <body> + <![CDATA[ + // Value changed, synthesize an event + try { + var event = document.createEvent("Events"); + event.initEvent("change", true, true); + aPreference.dispatchEvent(event); + } + catch (e) { + Components.utils.reportError(e); + } + ]]> + </body> + </method> + + <field name="service"> + Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService); + </field> + <field name="rootBranch"> + Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + </field> + <field name="defaultBranch"> + this.service.getDefaultBranch(""); + </field> + <field name="rootBranchInternal"> + Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranchInternal); + </field> + <property name="type" readonly="true"> + <getter> + <![CDATA[ + return document.documentElement.type || ""; + ]]> + </getter> + </property> + <property name="instantApply" readonly="true"> + <getter> + <![CDATA[ + var doc = document.documentElement; + return this.type == "child" ? doc.instantApply + : doc.instantApply || this.rootBranch.getBoolPref("browser.preferences.instantApply"); + ]]> + </getter> + </property> + </implementation> + </binding> + + <binding id="preference"> + <implementation> + <constructor> + <![CDATA[ + this._constructed = true; + + // if the element has been inserted without the name attribute set, + // we have nothing to do here + if (!this.name) + return; + + this.preferences.rootBranchInternal + .addObserver(this.name, this.preferences, false); + // In non-instant apply mode, we must try and use the last saved state + // from any previous opens of a child dialog instead of the value from + // preferences, to pick up any edits a user may have made. + + var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Components.interfaces.nsIScriptSecurityManager); + if (this.preferences.type == "child" && + !this.instantApply && window.opener && + secMan.isSystemPrincipal(window.opener.document.nodePrincipal)) { + var pdoc = window.opener.document; + + // Try to find a preference element for the same preference. + var preference = null; + var parentPreferences = pdoc.getElementsByTagName("preferences"); + for (var k = 0; (k < parentPreferences.length && !preference); ++k) { + var parentPrefs = parentPreferences[k] + .getElementsByAttribute("name", this.name); + for (var l = 0; (l < parentPrefs.length && !preference); ++l) { + if (parentPrefs[l].localName == "preference") + preference = parentPrefs[l]; + } + } + + // Don't use the value setter here, we don't want updateElements to be prematurely fired. + this._value = preference ? preference.value : this.valueFromPreferences; + } + else + this._value = this.valueFromPreferences; + this.preferences._constructAfterChildren(); + ]]> + </constructor> + <destructor> + this.preferences.rootBranchInternal + .removeObserver(this.name, this.preferences); + </destructor> + <field name="_constructed">false</field> + <property name="instantApply"> + <getter> + if (this.getAttribute("instantApply") == "false") + return false; + return this.getAttribute("instantApply") == "true" || this.preferences.instantApply; + </getter> + </property> + + <property name="preferences" onget="return this.parentNode"/> + <property name="name" onget="return this.getAttribute('name');"> + <setter> + if (val == this.name) + return val; + + this.preferences.rootBranchInternal + .removeObserver(this.name, this.preferences); + this.setAttribute('name', val); + this.preferences.rootBranchInternal + .addObserver(val, this.preferences, false); + + return val; + </setter> + </property> + <property name="type" onget="return this.getAttribute('type');" + onset="this.setAttribute('type', val); return val;"/> + <property name="inverted" onget="return this.getAttribute('inverted') == 'true';" + onset="this.setAttribute('inverted', val); return val;"/> + <property name="readonly" onget="return this.getAttribute('readonly') == 'true';" + onset="this.setAttribute('readonly', val); return val;"/> + + <field name="_value">null</field> + <method name="_setValue"> + <parameter name="aValue"/> + <body> + <![CDATA[ + if (this.value !== aValue) { + this._value = aValue; + if (this.instantApply) + this.valueFromPreferences = aValue; + this.preferences.fireChangedEvent(this); + } + return aValue; + ]]> + </body> + </method> + <property name="value" onget="return this._value" onset="return this._setValue(val);"/> + + <property name="locked"> + <getter> + return this.preferences.rootBranch.prefIsLocked(this.name); + </getter> + </property> + + <property name="disabled"> + <getter> + return this.getAttribute("disabled") == "true"; + </getter> + <setter> + <![CDATA[ + if (val) + this.setAttribute("disabled", "true"); + else + this.removeAttribute("disabled"); + + if (!this.id) + return val; + + var elements = document.getElementsByAttribute("preference", this.id); + for (var i = 0; i < elements.length; ++i) { + elements[i].disabled = val; + + var labels = document.getElementsByAttribute("control", elements[i].id); + for (var j = 0; j < labels.length; ++j) + labels[j].disabled = val; + } + + return val; + ]]> + </setter> + </property> + + <property name="tabIndex"> + <getter> + return parseInt(this.getAttribute("tabindex")); + </getter> + <setter> + <![CDATA[ + if (val) + this.setAttribute("tabindex", val); + else + this.removeAttribute("tabindex"); + + if (!this.id) + return val; + + var elements = document.getElementsByAttribute("preference", this.id); + for (var i = 0; i < elements.length; ++i) { + elements[i].tabIndex = val; + + var labels = document.getElementsByAttribute("control", elements[i].id); + for (var j = 0; j < labels.length; ++j) + labels[j].tabIndex = val; + } + + return val; + ]]> + </setter> + </property> + + <property name="hasUserValue"> + <getter> + <![CDATA[ + return this.preferences.rootBranch.prefHasUserValue(this.name) && + this.value !== undefined; + ]]> + </getter> + </property> + + <method name="reset"> + <body> + // defer reset until preference update + this.value = undefined; + </body> + </method> + + <field name="_useDefault">false</field> + <property name="defaultValue"> + <getter> + <![CDATA[ + this._useDefault = true; + var val = this.valueFromPreferences; + this._useDefault = false; + return val; + ]]> + </getter> + </property> + + <property name="_branch"> + <getter> + return this._useDefault ? this.preferences.defaultBranch : this.preferences.rootBranch; + </getter> + </property> + + <field name="batching">false</field> + + <method name="_reportUnknownType"> + <body> + <![CDATA[ + var consoleService = Components.classes["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService); + var msg = "<preference> with id='" + this.id + "' and name='" + + this.name + "' has unknown type '" + this.type + "'."; + consoleService.logStringMessage(msg); + ]]> + </body> + </method> + + <property name="valueFromPreferences"> + <getter> + <![CDATA[ + try { + // Force a resync of value with preferences. + switch (this.type) { + case "int": + return this._branch.getIntPref(this.name); + case "bool": + var val = this._branch.getBoolPref(this.name); + return this.inverted ? !val : val; + case "wstring": + return this._branch + .getComplexValue(this.name, Components.interfaces.nsIPrefLocalizedString) + .data; + case "string": + case "unichar": + return this._branch + .getComplexValue(this.name, Components.interfaces.nsISupportsString) + .data; + case "fontname": + var family = this._branch + .getComplexValue(this.name, Components.interfaces.nsISupportsString) + .data; + var fontEnumerator = Components.classes["@mozilla.org/gfx/fontenumerator;1"] + .createInstance(Components.interfaces.nsIFontEnumerator); + return fontEnumerator.getStandardFamilyName(family); + case "file": + var f = this._branch + .getComplexValue(this.name, Components.interfaces.nsILocalFile); + return f; + default: + this._reportUnknownType(); + } + } + catch (e) { } + return null; + ]]> + </getter> + <setter> + <![CDATA[ + // Exit early if nothing to do. + if (this.readonly || this.valueFromPreferences == val) + return val; + + // The special value undefined means 'reset preference to default'. + if (val === undefined) { + this.preferences.rootBranch.clearUserPref(this.name); + return val; + } + + // Force a resync of preferences with value. + switch (this.type) { + case "int": + this.preferences.rootBranch.setIntPref(this.name, val); + break; + case "bool": + this.preferences.rootBranch.setBoolPref(this.name, this.inverted ? !val : val); + break; + case "wstring": + var pls = Components.classes["@mozilla.org/pref-localizedstring;1"] + .createInstance(Components.interfaces.nsIPrefLocalizedString); + pls.data = val; + this.preferences.rootBranch + .setComplexValue(this.name, Components.interfaces.nsIPrefLocalizedString, pls); + break; + case "string": + case "unichar": + case "fontname": + var iss = Components.classes["@mozilla.org/supports-string;1"] + .createInstance(Components.interfaces.nsISupportsString); + iss.data = val; + this.preferences.rootBranch + .setComplexValue(this.name, Components.interfaces.nsISupportsString, iss); + break; + case "file": + var lf; + if (typeof(val) == "string") { + lf = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Components.interfaces.nsILocalFile); + lf.persistentDescriptor = val; + if (!lf.exists()) + lf.initWithPath(val); + } + else + lf = val.QueryInterface(Components.interfaces.nsILocalFile); + this.preferences.rootBranch + .setComplexValue(this.name, Components.interfaces.nsILocalFile, lf); + break; + default: + this._reportUnknownType(); + } + if (!this.batching) + this.preferences.service.savePrefFile(null); + return val; + ]]> + </setter> + </property> + + <method name="setElementValue"> + <parameter name="aElement"/> + <body> + <![CDATA[ + if (this.locked) + aElement.disabled = true; + + if (!this.isElementEditable(aElement)) + return; + + var rv = undefined; + if (aElement.hasAttribute("onsyncfrompreference")) { + // Value changed, synthesize an event + try { + var event = document.createEvent("Events"); + event.initEvent("syncfrompreference", true, true); + var f = new Function ("event", + aElement.getAttribute("onsyncfrompreference")); + rv = f.call(aElement, event); + } + catch (e) { + Components.utils.reportError(e); + } + } + var val = rv; + if (val === undefined) + val = this.instantApply ? this.valueFromPreferences : this.value; + // if the preference is marked for reset, show default value in UI + if (val === undefined) + val = this.defaultValue; + + /** + * Initialize a UI element property with a value. Handles the case + * where an element has not yet had a XBL binding attached for it and + * the property setter does not yet exist by setting the same attribute + * on the XUL element using DOM apis and assuming the element's + * constructor or property getters appropriately handle this state. + */ + function setValue(element, attribute, value) { + if (attribute in element) + element[attribute] = value; + else + element.setAttribute(attribute, value); + } + if (aElement.localName == "checkbox" || + aElement.localName == "listitem") + setValue(aElement, "checked", val); + else if (aElement.localName == "colorpicker") + setValue(aElement, "color", val); + else if (aElement.localName == "textbox") { + // XXXmano Bug 303998: Avoid a caret placement issue if either the + // preference observer or its setter calls updateElements as a result + // of the input event handler. + if (aElement.value !== val) + setValue(aElement, "value", val); + } + else + setValue(aElement, "value", val); + ]]> + </body> + </method> + + <method name="getElementValue"> + <parameter name="aElement"/> + <body> + <![CDATA[ + if (aElement.hasAttribute("onsynctopreference")) { + // Value changed, synthesize an event + try { + var event = document.createEvent("Events"); + event.initEvent("synctopreference", true, true); + var f = new Function ("event", + aElement.getAttribute("onsynctopreference")); + var rv = f.call(aElement, event); + if (rv !== undefined) + return rv; + } + catch (e) { + Components.utils.reportError(e); + } + } + + /** + * Read the value of an attribute from an element, assuming the + * attribute is a property on the element's node API. If the property + * is not present in the API, then assume its value is contained in + * an attribute, as is the case before a binding has been attached. + */ + function getValue(element, attribute) { + if (attribute in element) + return element[attribute]; + return element.getAttribute(attribute); + } + if (aElement.localName == "checkbox" || + aElement.localName == "listitem") + var value = getValue(aElement, "checked"); + else if (aElement.localName == "colorpicker") + value = getValue(aElement, "color"); + else + value = getValue(aElement, "value"); + + switch (this.type) { + case "int": + return parseInt(value, 10) || 0; + case "bool": + return typeof(value) == "boolean" ? value : value == "true"; + } + return value; + ]]> + </body> + </method> + + <method name="isElementEditable"> + <parameter name="aElement"/> + <body> + <![CDATA[ + switch (aElement.localName) { + case "checkbox": + case "colorpicker": + case "radiogroup": + case "textbox": + case "listitem": + case "listbox": + case "menulist": + return true; + } + return aElement.getAttribute("preference-editable") == "true"; + ]]> + </body> + </method> + + <method name="updateElements"> + <body> + <![CDATA[ + if (!this.id) + return; + + // This "change" event handler tracks changes made to preferences by + // sources other than the user in this window. + var elements = document.getElementsByAttribute("preference", this.id); + for (var i = 0; i < elements.length; ++i) + this.setElementValue(elements[i]); + ]]> + </body> + </method> + </implementation> + + <handlers> + <handler event="change"> + this.updateElements(); + </handler> + </handlers> + </binding> + + <binding id="prefwindow" + extends="chrome://global/content/bindings/dialog.xml#dialog"> + <resources> + <stylesheet src="chrome://global/skin/preferences.css"/> + </resources> + <content dlgbuttons="accept,cancel" persist="lastSelected screenX screenY" + closebuttonlabel="&preferencesCloseButton.label;" + closebuttonaccesskey="&preferencesCloseButton.accesskey;" + role="dialog" +#ifdef XP_WIN + title="&preferencesDefaultTitleWin.title;"> +#else + title="&preferencesDefaultTitleMac.title;"> +#endif + <xul:windowdragbox orient="vertical"> + <xul:radiogroup anonid="selector" orient="horizontal" class="paneSelector chromeclass-toolbar" + role="listbox"/> <!-- Expose to accessibility APIs as a listbox --> + </xul:windowdragbox> + <xul:hbox flex="1" class="paneDeckContainer"> + <xul:deck anonid="paneDeck" flex="1"> + <children includes="prefpane"/> + </xul:deck> + </xul:hbox> + <xul:hbox anonid="dlg-buttons" class="prefWindow-dlgbuttons" pack="end"> +#ifdef XP_UNIX + <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/> + <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/> + <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/> + <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/> + <xul:spacer anonid="spacer" flex="1"/> + <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/> + <xul:button dlgtype="accept" class="dialog-button" icon="accept"/> +#else + <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/> + <xul:spacer anonid="spacer" flex="1"/> + <xul:button dlgtype="accept" class="dialog-button" icon="accept"/> + <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/> + <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/> + <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/> + <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/> +#endif + </xul:hbox> + <xul:hbox> + <children/> + </xul:hbox> + </content> + <implementation implements="nsITimerCallback"> + <constructor> + <![CDATA[ + if (this.type != "child") { + if (!this._instantApplyInitialized) { + let psvc = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + this.instantApply = psvc.getBoolPref("browser.preferences.instantApply"); + } + if (this.instantApply) { + var docElt = document.documentElement; + var acceptButton = docElt.getButton("accept"); + acceptButton.hidden = true; + var cancelButton = docElt.getButton("cancel"); + if (/Mac/.test(navigator.platform)) { + // no buttons on Mac except Help + cancelButton.hidden = true; + // Move Help button to the end + document.getAnonymousElementByAttribute(this, "anonid", "spacer").hidden = true; + // Also, don't fire onDialogAccept on enter + acceptButton.disabled = true; + } else { + // morph the Cancel button into the Close button + cancelButton.setAttribute ("icon", "close"); + cancelButton.label = docElt.getAttribute("closebuttonlabel"); + cancelButton.accesskey = docElt.getAttribute("closebuttonaccesskey"); + } + } + } + this.setAttribute("animated", this._shouldAnimate ? "true" : "false"); + var panes = this.preferencePanes; + + var lastPane = null; + if (this.lastSelected) { + lastPane = document.getElementById(this.lastSelected); + if (!lastPane) { + this.lastSelected = ""; + } + } + + var paneToLoad; + if ("arguments" in window && window.arguments[0] && document.getElementById(window.arguments[0]) && document.getElementById(window.arguments[0]).nodeName == "prefpane") { + paneToLoad = document.getElementById(window.arguments[0]); + this.lastSelected = paneToLoad.id; + } + else if (lastPane) + paneToLoad = lastPane; + else + paneToLoad = panes[0]; + + for (var i = 0; i < panes.length; ++i) { + this._makePaneButton(panes[i]); + if (panes[i].loaded) { + // Inline pane content, fire load event to force initialization. + this._fireEvent("paneload", panes[i]); + } + } + this.showPane(paneToLoad); + + if (panes.length == 1) + this._selector.setAttribute("collapsed", "true"); + ]]> + </constructor> + + <destructor> + <![CDATA[ + // Release timers to avoid reference cycles. + if (this._animateTimer) { + this._animateTimer.cancel(); + this._animateTimer = null; + } + if (this._fadeTimer) { + this._fadeTimer.cancel(); + this._fadeTimer = null; + } + ]]> + </destructor> + + <!-- Derived bindings can set this to true to cause us to skip + reading the browser.preferences.instantApply pref in the constructor. + Then they can set instantApply to their wished value. --> + <field name="_instantApplyInitialized">false</field> + <!-- Controls whether changed pref values take effect immediately. --> + <field name="instantApply">false</field> + + <property name="preferencePanes" + onget="return this.getElementsByTagName('prefpane');"/> + + <property name="type" onget="return this.getAttribute('type');"/> + <property name="_paneDeck" + onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'paneDeck');"/> + <property name="_paneDeckContainer" + onget="return document.getAnonymousElementByAttribute(this, 'class', 'paneDeckContainer');"/> + <property name="_selector" + onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'selector');"/> + <property name="lastSelected" + onget="return this.getAttribute('lastSelected');"> + <setter> + this.setAttribute("lastSelected", val); + document.persist(this.id, "lastSelected"); + return val; + </setter> + </property> + <property name="currentPane" + onset="return this._currentPane = val;"> + <getter> + if (!this._currentPane) + this._currentPane = this.preferencePanes[0]; + + return this._currentPane; + </getter> + </property> + <field name="_currentPane">null</field> + + + <method name="_makePaneButton"> + <parameter name="aPaneElement"/> + <body> + <![CDATA[ + var radio = document.createElement("radio"); + radio.setAttribute("pane", aPaneElement.id); + radio.setAttribute("label", aPaneElement.label); + // Expose preference group choice to accessibility APIs as an unchecked list item + // The parent group is exposed to accessibility APIs as a list + if (aPaneElement.image) + radio.setAttribute("src", aPaneElement.image); + radio.style.listStyleImage = aPaneElement.style.listStyleImage; + this._selector.appendChild(radio); + return radio; + ]]> + </body> + </method> + + <method name="showPane"> + <parameter name="aPaneElement"/> + <body> + <![CDATA[ + if (!aPaneElement) + return; + + this._selector.selectedItem = document.getAnonymousElementByAttribute(this, "pane", aPaneElement.id); + if (!aPaneElement.loaded) { + let OverlayLoadObserver = function(aPane) + { + this._pane = aPane; + } + OverlayLoadObserver.prototype = { + _outer: this, + observe: function (aSubject, aTopic, aData) + { + this._pane.loaded = true; + this._outer._fireEvent("paneload", this._pane); + this._outer._selectPane(this._pane); + } + }; + + var obs = new OverlayLoadObserver(aPaneElement); + document.loadOverlay(aPaneElement.src, obs); + } + else + this._selectPane(aPaneElement); + ]]> + </body> + </method> + + <method name="_fireEvent"> + <parameter name="aEventName"/> + <parameter name="aTarget"/> + <body> + <![CDATA[ + // Panel loaded, synthesize a load event. + try { + var event = document.createEvent("Events"); + event.initEvent(aEventName, true, true); + var cancel = !aTarget.dispatchEvent(event); + if (aTarget.hasAttribute("on" + aEventName)) { + var fn = new Function ("event", aTarget.getAttribute("on" + aEventName)); + var rv = fn.call(aTarget, event); + if (rv == false) + cancel = true; + } + return !cancel; + } + catch (e) { + Components.utils.reportError(e); + } + return false; + ]]> + </body> + </method> + + <field name="_initialized">false</field> + <method name="_selectPane"> + <parameter name="aPaneElement"/> + <body> + <![CDATA[ + if (/Mac/.test(navigator.platform)) { + var paneTitle = aPaneElement.label; + if (paneTitle != "") + document.title = paneTitle; + } + var helpButton = document.documentElement.getButton("help"); + if (aPaneElement.helpTopic) + helpButton.hidden = false; + else + helpButton.hidden = true; + + // Find this pane's index in the deck and set the deck's + // selectedIndex to that value to switch to it. + var prefpanes = this.preferencePanes; + for (var i = 0; i < prefpanes.length; ++i) { + if (prefpanes[i] == aPaneElement) { + this._paneDeck.selectedIndex = i; + + if (this.type != "child") { + if (aPaneElement.hasAttribute("flex") && this._shouldAnimate && + prefpanes.length > 1) + aPaneElement.removeAttribute("flex"); + // Calling sizeToContent after the first prefpane is loaded + // will size the windows contents so style information is + // available to calculate correct sizing. + if (!this._initialized && prefpanes.length > 1) { + if (this._shouldAnimate) + this.style.minHeight = 0; + window.sizeToContent(); + } + + var oldPane = this.lastSelected ? document.getElementById(this.lastSelected) : this.preferencePanes[0]; + oldPane.selected = !(aPaneElement.selected = true); + this.lastSelected = aPaneElement.id; + this.currentPane = aPaneElement; + this._initialized = true; + + // Only animate if we've switched between prefpanes + if (this._shouldAnimate && oldPane.id != aPaneElement.id) { + aPaneElement.style.opacity = 0.0; + this.animate(oldPane, aPaneElement); + } + else if (!this._shouldAnimate && prefpanes.length > 1) { + var targetHeight = parseInt(window.getComputedStyle(this._paneDeckContainer, "").height); + var verticalPadding = parseInt(window.getComputedStyle(aPaneElement, "").paddingTop); + verticalPadding += parseInt(window.getComputedStyle(aPaneElement, "").paddingBottom); + if (aPaneElement.contentHeight > targetHeight - verticalPadding) { + // To workaround the bottom border of a groupbox from being + // cutoff an hbox with a class of bottomBox may enclose it. + // This needs to include its padding to resize properly. + // See bug 394433 + var bottomPadding = 0; + var bottomBox = aPaneElement.getElementsByAttribute("class", "bottomBox")[0]; + if (bottomBox) + bottomPadding = parseInt(window.getComputedStyle(bottomBox, "").paddingBottom); + window.innerHeight += bottomPadding + verticalPadding + aPaneElement.contentHeight - targetHeight; + } + + // XXX rstrong - extend the contents of the prefpane to + // prevent elements from being cutoff (see bug 349098). + if (aPaneElement.contentHeight + verticalPadding < targetHeight) + aPaneElement._content.style.height = targetHeight - verticalPadding + "px"; + } + } + break; + } + } + ]]> + </body> + </method> + + <property name="_shouldAnimate"> + <getter> + <![CDATA[ + var psvc = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + var animate = /Mac/.test(navigator.platform); + try { + animate = psvc.getBoolPref("browser.preferences.animateFadeIn"); + } + catch (e) { } + return animate; + ]]> + </getter> + </property> + + <method name="animate"> + <parameter name="aOldPane"/> + <parameter name="aNewPane"/> + <body> + <![CDATA[ + // if we are already resizing, use currentHeight + var oldHeight = this._currentHeight ? this._currentHeight : aOldPane.contentHeight; + + this._multiplier = aNewPane.contentHeight > oldHeight ? 1 : -1; + var sizeDelta = Math.abs(oldHeight - aNewPane.contentHeight); + this._animateRemainder = sizeDelta % this._animateIncrement; + + this._setUpAnimationTimer(oldHeight); + ]]> + </body> + </method> + + <property name="_sizeIncrement"> + <getter> + <![CDATA[ + var lastSelectedPane = document.getElementById(this.lastSelected); + var increment = this._animateIncrement * this._multiplier; + var newHeight = this._currentHeight + increment; + if ((this._multiplier > 0 && this._currentHeight >= lastSelectedPane.contentHeight) || + (this._multiplier < 0 && this._currentHeight <= lastSelectedPane.contentHeight)) + return 0; + + if ((this._multiplier > 0 && newHeight > lastSelectedPane.contentHeight) || + (this._multiplier < 0 && newHeight < lastSelectedPane.contentHeight)) + increment = this._animateRemainder * this._multiplier; + return increment; + ]]> + </getter> + </property> + + <method name="notify"> + <parameter name="aTimer"/> + <body> + <![CDATA[ + if (!document) + aTimer.cancel(); + + if (aTimer == this._animateTimer) { + var increment = this._sizeIncrement; + if (increment != 0) { + window.innerHeight += increment; + this._currentHeight += increment; + } + else { + aTimer.cancel(); + this._setUpFadeTimer(); + } + } else if (aTimer == this._fadeTimer) { + var elt = document.getElementById(this.lastSelected); + var newOpacity = parseFloat(window.getComputedStyle(elt, "").opacity) + this._fadeIncrement; + if (newOpacity < 1.0) + elt.style.opacity = newOpacity; + else { + aTimer.cancel(); + elt.style.opacity = 1.0; + } + } + ]]> + </body> + </method> + + <method name="_setUpAnimationTimer"> + <parameter name="aStartHeight"/> + <body> + <![CDATA[ + if (!this._animateTimer) + this._animateTimer = Components.classes["@mozilla.org/timer;1"] + .createInstance(Components.interfaces.nsITimer); + else + this._animateTimer.cancel(); + this._currentHeight = aStartHeight; + + this._animateTimer.initWithCallback(this, this._animateDelay, + Components.interfaces.nsITimer.TYPE_REPEATING_SLACK); + ]]> + </body> + </method> + + <method name="_setUpFadeTimer"> + <body> + <![CDATA[ + if (!this._fadeTimer) + this._fadeTimer = Components.classes["@mozilla.org/timer;1"] + .createInstance(Components.interfaces.nsITimer); + else + this._fadeTimer.cancel(); + + this._fadeTimer.initWithCallback(this, this._fadeDelay, + Components.interfaces.nsITimer.TYPE_REPEATING_SLACK); + ]]> + </body> + </method> + + <field name="_animateTimer">null</field> + <field name="_fadeTimer">null</field> + <field name="_animateDelay">15</field> + <field name="_animateIncrement">40</field> + <field name="_fadeDelay">5</field> + <field name="_fadeIncrement">0.40</field> + <field name="_animateRemainder">0</field> + <field name="_currentHeight">0</field> + <field name="_multiplier">0</field> + + <method name="addPane"> + <parameter name="aPaneElement"/> + <body> + <![CDATA[ + this.appendChild(aPaneElement); + + // Set up pane button + this._makePaneButton(aPaneElement); + ]]> + </body> + </method> + + <method name="openSubDialog"> + <parameter name="aURL"/> + <parameter name="aFeatures"/> + <parameter name="aParams"/> + <body> + return openDialog(aURL, "", "modal,centerscreen,resizable=no" + (aFeatures != "" ? ("," + aFeatures) : ""), aParams); + </body> + </method> + + <method name="openWindow"> + <parameter name="aWindowType"/> + <parameter name="aURL"/> + <parameter name="aFeatures"/> + <parameter name="aParams"/> + <body> + <![CDATA[ + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var win = aWindowType ? wm.getMostRecentWindow(aWindowType) : null; + if (win) { + if ("initWithParams" in win) + win.initWithParams(aParams); + win.focus(); + } + else { + var features = "resizable,dialog=no,centerscreen" + (aFeatures != "" ? ("," + aFeatures) : ""); + var parentWindow = (this.instantApply || !window.opener || window.opener.closed) ? window : window.opener; + win = parentWindow.openDialog(aURL, "_blank", features, aParams); + } + return win; + ]]> + </body> + </method> + </implementation> + <handlers> + <handler event="dialogaccept"> + <![CDATA[ + if (!this._fireEvent("beforeaccept", this)) { + return false; + } + + var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Components.interfaces.nsIScriptSecurityManager); + if (this.type == "child" && window.opener && + secMan.isSystemPrincipal(window.opener.document.nodePrincipal)) { + let psvc = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + var pdocEl = window.opener.document.documentElement; + if (pdocEl.instantApply) { + let panes = this.preferencePanes; + for (let i = 0; i < panes.length; ++i) + panes[i].writePreferences(true); + } + else { + // Clone all the preferences elements from the child document and + // insert them into the pane collection of the parent. + var pdoc = window.opener.document; + if (pdoc.documentElement.localName == "prefwindow") { + var currentPane = pdoc.documentElement.currentPane; + var id = window.location.href + "#childprefs"; + var childPrefs = pdoc.getElementById(id); + if (!childPrefs) { + childPrefs = pdoc.createElement("preferences"); + currentPane.appendChild(childPrefs); + childPrefs.id = id; + } + let panes = this.preferencePanes; + for (let i = 0; i < panes.length; ++i) { + var preferences = panes[i].preferences; + for (var j = 0; j < preferences.length; ++j) { + // Try to find a preference element for the same preference. + var preference = null; + var parentPreferences = pdoc.getElementsByTagName("preferences"); + for (var k = 0; (k < parentPreferences.length && !preference); ++k) { + var parentPrefs = parentPreferences[k] + .getElementsByAttribute("name", preferences[j].name); + for (var l = 0; (l < parentPrefs.length && !preference); ++l) { + if (parentPrefs[l].localName == "preference") + preference = parentPrefs[l]; + } + } + if (!preference) { + // No matching preference in the parent window. + preference = pdoc.createElement("preference"); + childPrefs.appendChild(preference); + preference.name = preferences[j].name; + preference.type = preferences[j].type; + preference.inverted = preferences[j].inverted; + preference.readonly = preferences[j].readonly; + preference.disabled = preferences[j].disabled; + } + preference.value = preferences[j].value; + } + } + } + } + } + else { + let panes = this.preferencePanes; + for (var i = 0; i < panes.length; ++i) + panes[i].writePreferences(false); + + let psvc = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService); + psvc.savePrefFile(null); + } + + return true; + ]]> + </handler> + <handler event="command"> + if (event.originalTarget.hasAttribute("pane")) { + var pane = document.getElementById(event.originalTarget.getAttribute("pane")); + this.showPane(pane); + } + </handler> + + <handler event="keypress" key="&windowClose.key;" modifiers="accel" phase="capturing"> + <![CDATA[ + if (this.instantApply) + window.close(); + event.stopPropagation(); + event.preventDefault(); + ]]> + </handler> + + <handler event="keypress" +#ifdef XP_MACOSX + key="&openHelpMac.commandkey;" modifiers="accel" +#else + keycode="&openHelp.commandkey;" +#endif + phase="capturing"> + <![CDATA[ + var helpButton = this.getButton("help"); + if (helpButton.disabled || helpButton.hidden) + return; + this._fireEvent("dialoghelp", this); + event.stopPropagation(); + event.preventDefault(); + ]]> + </handler> + </handlers> + </binding> + + <binding id="prefpane"> + <resources> + <stylesheet src="chrome://global/skin/preferences.css"/> + </resources> + <content> + <xul:vbox class="content-box" xbl:inherits="flex"> + <children/> + </xul:vbox> + </content> + <implementation> + <method name="writePreferences"> + <parameter name="aFlushToDisk"/> + <body> + <![CDATA[ + // Write all values to preferences. + if (this._deferredValueUpdateElements.size) { + this._finalizeDeferredElements(); + } + + var preferences = this.preferences; + for (var i = 0; i < preferences.length; ++i) { + var preference = preferences[i]; + preference.batching = true; + preference.valueFromPreferences = preference.value; + preference.batching = false; + } + if (aFlushToDisk) { + var psvc = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService); + psvc.savePrefFile(null); + } + ]]> + </body> + </method> + + <property name="src" + onget="return this.getAttribute('src');" + onset="this.setAttribute('src', val); return val;"/> + <property name="selected" + onget="return this.getAttribute('selected') == 'true';" + onset="this.setAttribute('selected', val); return val;"/> + <property name="image" + onget="return this.getAttribute('image');" + onset="this.setAttribute('image', val); return val;"/> + <property name="label" + onget="return this.getAttribute('label');" + onset="this.setAttribute('label', val); return val;"/> + + <property name="preferenceElements" + onget="return this.getElementsByAttribute('preference', '*');"/> + <property name="preferences" + onget="return this.getElementsByTagName('preference');"/> + + <property name="helpTopic"> + <getter> + <![CDATA[ + // if there are tabs, and the selected tab provides a helpTopic, return that + var box = this.getElementsByTagName("tabbox"); + if (box[0]) { + var tab = box[0].selectedTab; + if (tab && tab.hasAttribute("helpTopic")) + return tab.getAttribute("helpTopic"); + } + + // otherwise, return the helpTopic of the current panel + return this.getAttribute("helpTopic"); + ]]> + </getter> + </property> + + <field name="_loaded">false</field> + <property name="loaded" + onget="return !this.src ? true : this._loaded;" + onset="this._loaded = val; return val;"/> + + <method name="preferenceForElement"> + <parameter name="aElement"/> + <body> + return document.getElementById(aElement.getAttribute("preference")); + </body> + </method> + + <method name="getPreferenceElement"> + <parameter name="aStartElement"/> + <body> + <![CDATA[ + var temp = aStartElement; + while (temp && temp.nodeType == Node.ELEMENT_NODE && + !temp.hasAttribute("preference")) + temp = temp.parentNode; + return temp.nodeType == Node.ELEMENT_NODE ? temp : aStartElement; + ]]> + </body> + </method> + + <property name="DeferredTask" readonly="true"> + <getter><![CDATA[ + let module = {}; + Components.utils.import("resource://gre/modules/DeferredTask.jsm", module); + Object.defineProperty(this, "DeferredTask", { + configurable: true, + enumerable: true, + writable: true, + value: module.DeferredTask + }); + return module.DeferredTask; + ]]></getter> + </property> + <method name="_deferredValueUpdate"> + <parameter name="aElement"/> + <body> + <![CDATA[ + delete aElement._deferredValueUpdateTask; + let preference = document.getElementById(aElement.getAttribute("preference")); + let prefVal = preference.getElementValue(aElement); + preference.value = prefVal; + this._deferredValueUpdateElements.delete(aElement); + ]]> + </body> + </method> + <field name="_deferredValueUpdateElements"> + new Set(); + </field> + <method name="_finalizeDeferredElements"> + <body> + <![CDATA[ + for (let el of this._deferredValueUpdateElements) { + if (el._deferredValueUpdateTask) { + el._deferredValueUpdateTask.finalize(); + } + } + ]]> + </body> + </method> + <method name="userChangedValue"> + <parameter name="aElement"/> + <body> + <![CDATA[ + let element = this.getPreferenceElement(aElement); + if (element.hasAttribute("preference")) { + if (element.getAttribute("delayprefsave") != "true") { + var preference = document.getElementById(element.getAttribute("preference")); + var prefVal = preference.getElementValue(element); + preference.value = prefVal; + } else { + if (!element._deferredValueUpdateTask) { + element._deferredValueUpdateTask = new this.DeferredTask(this._deferredValueUpdate.bind(this, element), 1000); + this._deferredValueUpdateElements.add(element); + } else { + // Each time the preference is changed, restart the delay. + element._deferredValueUpdateTask.disarm(); + } + element._deferredValueUpdateTask.arm(); + } + } + ]]> + </body> + </method> + + <property name="contentHeight"> + <getter> + var targetHeight = parseInt(window.getComputedStyle(this._content, "").height); + targetHeight += parseInt(window.getComputedStyle(this._content, "").marginTop); + targetHeight += parseInt(window.getComputedStyle(this._content, "").marginBottom); + return targetHeight; + </getter> + </property> + <field name="_content"> + document.getAnonymousElementByAttribute(this, "class", "content-box"); + </field> + </implementation> + <handlers> + <handler event="command"> + // This "command" event handler tracks changes made to preferences by + // the user in this window. + if (event.sourceEvent) + event = event.sourceEvent; + this.userChangedValue(event.target); + </handler> + <handler event="select"> + // This "select" event handler tracks changes made to colorpicker + // preferences by the user in this window. + if (event.target.localName == "colorpicker") + this.userChangedValue(event.target); + </handler> + <handler event="change"> + // This "change" event handler tracks changes made to preferences by + // the user in this window. + this.userChangedValue(event.target); + </handler> + <handler event="input"> + // This "input" event handler tracks changes made to preferences by + // the user in this window. + this.userChangedValue(event.target); + </handler> + <handler event="paneload"> + <![CDATA[ + // Initialize all values from preferences. + var elements = this.preferenceElements; + for (var i = 0; i < elements.length; ++i) { + try { + var preference = this.preferenceForElement(elements[i]); + preference.setElementValue(elements[i]); + } + catch (e) { + dump("*** No preference found for " + elements[i].getAttribute("preference") + "\n"); + } + } + ]]> + </handler> + </handlers> + </binding> + + <binding id="panebutton" role="xul:listitem" + extends="chrome://global/content/bindings/radio.xml#radio"> + <resources> + <stylesheet src="chrome://global/skin/preferences.css"/> + </resources> + <content> + <xul:image class="paneButtonIcon" xbl:inherits="src"/> + <xul:label class="paneButtonLabel" xbl:inherits="value=label"/> + </content> + </binding> + +</bindings> + +# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# +# This is PrefWindow 6. The Code Could Well Be Ready, Are You? +# +# Historical References: +# PrefWindow V (February 1, 2003) +# PrefWindow IV (April 24, 2000) +# PrefWindow III (January 6, 2000) +# PrefWindow II (???) +# PrefWindow I (June 4, 1999) +# |