toolkit/content/widgets/button.xml
author Atte Kemppilä <atte.kemppila@iki.fi>
Tue, 26 Jun 2012 15:39:33 -0700
changeset 97736 2ee8a6148b943a6df6b4d1f36b2f6dbc472b8d9b
parent 94475 f4157e8c410708d76703f19e4dfb61859bfe32d8
child 108383 eb7c07373996caf4a69414214d492f9cd00ca62e
permissions -rw-r--r--
Bug 745703 - Fix menu-button xbl:inherits typo: accessKey -> accesskey. r=enndeakin

<?xml version="1.0"?>
<!-- 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/. -->


<bindings id="buttonBindings"
   xmlns="http://www.mozilla.org/xbl"
   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="button-base" extends="chrome://global/content/bindings/general.xml#basetext">
    <implementation implements="nsIDOMXULButtonElement, nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            return Components.interfaces.nsIAccessibleProvider.XULButton;
          ]]>
        </getter>
      </property>

      <property name="type"
                onget="return this.getAttribute('type');"
                onset="this.setAttribute('type', val); return val;"/>

      <property name="dlgType"
                onget="return this.getAttribute('dlgtype');"
                onset="this.setAttribute('dlgtype', val); return val;"/>

      <property name="group"
                onget="return this.getAttribute('group');"
                onset="this.setAttribute('group', val); return val;"/>

      <property name="open" onget="return this.hasAttribute('open');">
        <setter><![CDATA[
          if (this.boxObject instanceof
              Components.interfaces.nsIMenuBoxObject) {
            this.boxObject.openMenu(val);
          } else {
            // Fall back to just setting the attribute
            if (val) {
              this.setAttribute('open', 'true');
            } else {
              this.removeAttribute('open');
            }
          }
          return val;
        ]]></setter>
      </property>

      <property name="checked" onget="return this.hasAttribute('checked');">
        <setter><![CDATA[
          if (this.type == "checkbox") {
            this.checkState = val ? 1 : 0;
          } else if (this.type == "radio" && val) {
            var sibs = this.parentNode.getElementsByAttribute("group", this.group);
            for (var i = 0; i < sibs.length; ++i)
              sibs[i].removeAttribute("checked");
          }

          if (val)
            this.setAttribute("checked", "true");
          else
            this.removeAttribute("checked");
          
          return val;
        ]]></setter>
      </property>
      
      <property name="checkState">
        <getter><![CDATA[
          var state = this.getAttribute("checkState");
          if (state == "")
            return this.checked ? 1 : 0;
          else
            return state == "0" ? 0 : (state == "2" ? 2 : 1);
        ]]></getter>
        <setter><![CDATA[
          this.setAttribute("checkState", val);
          return val;
        ]]></setter>
      </property>
      
      <property name="autoCheck"
                onget="return this.getAttribute('autoCheck') == 'true';"
                onset="this.setAttribute('autoCheck', val); return val;"/>

      <method name ="filterButtons">
        <parameter name="node"/>
        <body>
        <![CDATA[
          if (node.localName == "button" && node.accessKey &&
            !node.disabled && !node.collapsed && !node.hidden)
            return NodeFilter.FILTER_ACCEPT;
          return NodeFilter.FILTER_SKIP;
        ]]>
        </body>
      </method>

      <method name="fireAccessKeyButton">
        <parameter name="aSubtree"/>
        <parameter name="aAccessKeyLower"/>
        <body>
        <![CDATA[
          var iterator = aSubtree.ownerDocument.createTreeWalker(aSubtree, 
                                                                 NodeFilter.SHOW_ELEMENT, 
                                                                 this.filterButtons, false);
          while (iterator.nextNode()) {
            var test = iterator.currentNode;
            if (test.accessKey.toLowerCase() == aAccessKeyLower && 
                !test.disabled && !test.collapsed && !test.hidden) {
              test.focus();
              test.click();
              return true;
            }
          }
          return false;
        ]]>
        </body>
      </method>

      <method name="_handleClick">
        <body>
        <![CDATA[
          if (!this.disabled &&
              (this.autoCheck || !this.hasAttribute("autoCheck"))) {

            if (this.type == "checkbox") {
              this.checked = !this.checked;
            } else if (this.type == "radio") {
              this.checked = true;
            }
          }
        ]]>
        </body>
      </method>
    </implementation>

    <handlers>
      <!-- While it would seem we could do this by handling oncommand, we can't
           because any external oncommand handlers might get called before ours,
           and then they would see the incorrect value of checked. Additionally
           a command attribute would redirect the command events anyway.-->
      <handler event="click" button="0" action="this._handleClick();"/>
      <handler event="keypress" key=" " action="this._handleClick();"/>

      <handler event="keypress">
      <![CDATA[
        if (this.boxObject instanceof Components.interfaces.nsIMenuBoxObject) {
          if (this.open)
            return;
        } else {
          if (event.keyCode == KeyEvent.DOM_VK_UP ||
              (event.keyCode == KeyEvent.DOM_VK_LEFT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "ltr") ||
              (event.keyCode == KeyEvent.DOM_VK_RIGHT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "rtl")) {
            event.preventDefault();
            window.document.commandDispatcher.rewindFocus();
            return;
          }

          if (event.keyCode == KeyEvent.DOM_VK_DOWN ||
              (event.keyCode == KeyEvent.DOM_VK_RIGHT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "ltr") ||
              (event.keyCode == KeyEvent.DOM_VK_LEFT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "rtl")) {
            event.preventDefault();
            window.document.commandDispatcher.advanceFocus();
            return;
          }
        }

        if (event.keyCode || event.charCode <= 32 || event.altKey || 
            event.ctrlKey || event.metaKey)
          return;  // No printable char pressed, not a potential accesskey

        // Possible accesskey pressed
        var charPressedLower = String.fromCharCode(event.charCode).toLowerCase();

        // If the accesskey of the current button is pressed, just activate it
        if (this.accessKey.toLowerCase() == charPressedLower) {
          this.click();
          return;
        }

        // Search for accesskey in the list of buttons for this doc and each subdoc
        // Get the buttons for the main document and all sub-frames
        for (var frameCount = -1; frameCount < window.top.frames.length; frameCount++) {
          var doc = (frameCount == -1)? window.top.document: 
            window.top.frames[frameCount].document
          if (this.fireAccessKeyButton(doc.documentElement, charPressedLower))
            return;
        }

        // Test anonymous buttons
        var dlg = window.top.document;
        var buttonBox = dlg.getAnonymousElementByAttribute(dlg.documentElement,
                                                         "anonid", "buttons");
        if (buttonBox)
          this.fireAccessKeyButton(buttonBox, charPressedLower);
      ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="button" display="xul:button"
           extends="chrome://global/content/bindings/button.xml#button-base">
    <resources>
      <stylesheet src="chrome://global/skin/button.css"/>
    </resources>

    <content>
      <children includes="observes|template|menupopup|panel|tooltip"/>
      <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient"
                align="center" pack="center" flex="1" anonid="button-box">
        <children>
          <xul:image class="button-icon" xbl:inherits="src=image"/>
          <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop"/>
        </children>
      </xul:hbox>
    </content>
  </binding>

  <binding id="menu" display="xul:menu"
           extends="chrome://global/content/bindings/button.xml#button">
    <content>
      <children includes="observes|template|menupopup|panel|tooltip"/>
      <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient"
                align="center" pack="center" flex="1">
        <children>
          <xul:hbox class="box-inherit" xbl:inherits="align,dir,pack,orient"
                    align="center" pack="center" flex="1">
            <xul:image class="button-icon" xbl:inherits="src=image"/>
            <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop"/>
          </xul:hbox>
          <xul:dropmarker class="button-menu-dropmarker" xbl:inherits="open,disabled,label"/>
        </children>
      </xul:hbox>
    </content>

    <handlers>
      <handler event="keypress" keycode="VK_RETURN" action="this.open = true;"/>
      <handler event="keypress" key=" " action="this.open = true;"/>
    </handlers>
  </binding>

  <binding id="menu-button-base"
           extends="chrome://global/content/bindings/button.xml#button-base">
    <implementation implements="nsIDOMEventListener">
      <constructor>
        this.init();
      </constructor>
      
      <method name="init">
        <body>
        <![CDATA[
          var btn = document.getAnonymousElementByAttribute(this, "anonid", "button");
          if (!btn)
            throw "XBL binding for <button type=\"menu-button\"/> binding must contain an element with anonid=\"button\"";
          
          var menubuttonParent = this;
          btn.addEventListener("mouseover", function() { 
            if (!this.disabled)
              menubuttonParent.buttonover = true;
          }, true);
          btn.addEventListener("mouseout", function() {
            menubuttonParent.buttonover = false;
          }, true);
          btn.addEventListener("mousedown", function() {
            if (!this.disabled) {
              menubuttonParent.buttondown = true;
              document.addEventListener("mouseup", menubuttonParent, true);
            }
          }, true);
        ]]>
        </body>
      </method>
      
      <property name="buttonover" onget="return this.getAttribute('buttonover');">
        <setter>
        <![CDATA[
          var v = val || val == "true";
          if (!v && this.buttondown) { 
            this.buttondown = false;
            this._pendingActive = true;
          } 
          else {
            if (this._pendingActive) {
              this.buttondown = true;
              this._pendingActive = false;
            }
          }

          if (v)
            this.setAttribute("buttonover", "true");
          else
            this.removeAttribute("buttonover");
          return val;
        ]]>
        </setter>
      </property>
      
      <property name="buttondown" onget="return this.getAttribute('buttondown') == 'true';">
        <setter>
        <![CDATA[
          if (val || val == "true")
            this.setAttribute("buttondown", "true");
          else
            this.removeAttribute("buttondown");
          return val;
        ]]>
        </setter>
      </property>
      
      <field name="_pendingActive">false</field>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          this._pendingActive = false;
          this.buttondown = false;
          document.removeEventListener("mouseup", this, true);
        ]]>
        </body>
      </method>

    </implementation>
  </binding>

  <binding id="menu-button" display="xul:menu"
           extends="chrome://global/content/bindings/button.xml#menu-button-base">
    <resources>
      <stylesheet src="chrome://global/skin/button.css"/>
    </resources>

    <content>
      <children includes="observes|template|menupopup|panel|tooltip"/>
      <xul:button class="box-inherit button-menubutton-button"
                  anonid="button" flex="1" allowevents="true"
                  xbl:inherits="disabled,crop,image,label,accesskey,command,
                                buttonover,buttondown,align,dir,pack,orient">
        <children/>
      </xul:button>
      <xul:dropmarker class="button-menubutton-dropmarker" xbl:inherits="open,disabled,label"/>
    </content>
  </binding>
  
  <binding id="button-image" display="xul:button"
           extends="chrome://global/content/bindings/button.xml#button">
    <content>
      <xul:image class="button-image-icon" xbl:inherits="src=image"/>
    </content>
  </binding>

  <binding id="button-repeat" display="xul:autorepeatbutton"
           extends="chrome://global/content/bindings/button.xml#button"/>
  
</bindings>