<?xml version="1.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="accessible">
        <getter>
          <![CDATA[
            var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
            return accService.createXULButtonAccessible(this);
          ]]>
        </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');"
                onset="if (val) this.setAttribute('open', 'true');
                       else this.removeAttribute('open'); return val;"/>

      <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)
              if (sibs[i].checked)
                sibs[i].checked = false;
          }

          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>
    </implementation>

    <handlers>
      <handler event="command">
      <![CDATA[
        if (this.autoCheck || !this.hasAttribute("autoCheck")) {
          if (this.type == "checkbox") {
            this.checked = !this.checked;
          } else if (this.type == "radio") {
            this.checked = true;
          }
        }
      ]]>
      </handler>

      <handler event="keypress">
      <![CDATA[
        if (event.keyCode == KeyEvent.DOM_VK_UP ||
            event.keyCode == KeyEvent.DOM_VK_LEFT)
          return window.document.commandDispatcher.rewindFocus();

        if (event.keyCode == KeyEvent.DOM_VK_DOWN ||
            event.keyCode == KeyEvent.DOM_VK_RIGHT)
          return window.document.commandDispatcher.advanceFocus();

        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|tooltip"/>
      <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient"
                align="center" pack="center" flex="1">
        <children>
          <xul:image class="button-icon" xbl:inherits="src=image"/>
          <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop" flex="1"/>
        </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|tooltip"/>
      <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient"
                align="center" pack="center" flex="1">
        <children>
          <xul:image class="button-icon" xbl:inherits="src=image"/>
          <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop" flex="1"/>
          <xul:dropmarker class="button-menu-dropmarker" xbl:inherits="open,disabled"/>
        </children>
      </xul:hbox>
    </content>
  </binding>           

  <binding id="menu-button-base"
           extends="chrome://global/content/bindings/button.xml#button-base">
    <implementation>
      <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\"";
          
          btn._menubuttonParent = this;
          btn.addEventListener("mouseover", function() { 
            if (!this.disabled)
              this._menubuttonParent.buttonover = true; 
          }, true);
          btn.addEventListener("mouseout", function() {
            this._menubuttonParent.buttonover = false;
           }, true);
          btn.addEventListener("mousedown", function() {
            if (!this.disabled) {
              this._menubuttonParent.buttondown = true;
              this._menubuttonParent._captureMouseUp();
            }
          }, 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="_captureMouseUp">
        <body>
        <![CDATA[
          document.__ButtonMenuMouseDown__ = this;
          document.addEventListener("mouseup", this._onGlobalMouseUp, true);
        ]]>
        </body>
      </method>

      <method name="_onGlobalMouseUp" readonly="true">
        <body>
        <![CDATA[
          var btn = document.__ButtonMenuMouseDown__;
          btn._onMouseReallyUp();
          document.removeEventListener("mouseup", btn._onGlobalMouseUp, true);
          delete document.__ButtonMenuMouseDown__;
        ]]>
        </body>
      </method>

      <method name="_onMouseReallyUp">
        <body>
        <![CDATA[
          this._pendingActive = false;
          this.buttondown = false;
        ]]>
        </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|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"/>
    </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>
  
</bindings>