<?xml version="1.0"?>

<bindings id="popupBindings"
   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="popup-base">
    <resources>
      <stylesheet src="chrome://global/skin/popup.css"/>
    </resources>
  </binding>

  <binding id="popup" extends="chrome://global/content/bindings/popup.xml#popup-base">
    <content>
      <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical">
        <children/>
      </xul:arrowscrollbox>
    </content>

    <implementation implements="nsIDOMXULPopupElement, nsIAccessibleProvider">
      <property name="accessible">
        <getter>
          <![CDATA[
            var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
            if (this.localName == "popup" || this.localName == "menupopup")
              return (this.parentNode.localName == "menulist")? accService.createXULSelectListAccessible(this): accService.createXULMenupopupAccessible(this);
            else if (this.localName == "tooltip")
              return accService.createXULTooltipAccessible(this);
            return null;
          ]]>
        </getter>
      </property>

      <property name="position" onget="return this.getAttribute('position');"
                                onset="this.setAttribute('position', val); return val;"/>
      <property name="popupBoxObject">
        <getter>
          return this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject);
        </getter>
      </property>
      
      <method name="showPopup">
        <parameter name="element"/>
        <parameter name="xpos"/>
        <parameter name="ypos"/>
        <parameter name="popuptype"/>
        <parameter name="anchoralignment"/>
        <parameter name="popupalignment"/>
        <body>
        <![CDATA[
          var popupBox = null;
          var menuBox = null;
          try {
            popupBox = this.popupBoxObject;
          } catch(e) {}
          try {
            menuBox = this.parentNode.boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject);
          } catch(e) {}
          if (menuBox)
            menuBox.openMenu(true);
          else if (popupBox)
            popupBox.showPopup(element, this, xpos, ypos, popuptype, anchoralignment, popupalignment);
        ]]>
        </body>
      </method>
      
      <method name="hidePopup">
        <body>
        <![CDATA[
          var popupBox = null;
          var menuBox = null;
          try {
            popupBox = this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject);
          } catch(e) {}
          try {
            menuBox = this.parentNode.boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject);
          } catch(e) {}
          if (menuBox)
            menuBox.openMenu(false);
          else if (popupBox)
            popupBox.hidePopup();
        ]]>
        </body>
      </method>

      <property name="autoPosition">
        <getter>
        <![CDATA[
          return this.popupBoxObject.autoPosition;
        ]]>
        </getter>
        <setter>
        <![CDATA[
          return this.popupBoxObject.autoPosition = val;
        ]]>
        </setter>
      </property>
      
      <method name="enableKeyboardNavigator">
        <parameter name="aEnableKeyboardNavigator"/>
        <body>
        <![CDATA[
          this.popupBoxObject.enableKeyboardNavigator(aEnableKeyboardNavigator);
        ]]>
        </body>
      </method>
      
      <method name="enableRollup">
        <parameter name="aEnableRollup"/>
        <body>
        <![CDATA[
          this.popupBoxObject.enableRollup(aEnableRollup);
        ]]>
        </body>
      </method>
      
      <method name="sizeTo">
        <parameter name="aWidth"/>
        <parameter name="aHeight"/>
        <body>
        <![CDATA[
          this.popupBoxObject.sizeTo(aWidth, aHeight);
        ]]>
        </body>
      </method>
      
      <method name="moveTo">
        <parameter name="aLeft"/>
        <parameter name="aTop"/>
        <body>
        <![CDATA[
          this.popupBoxObject.moveTo(aLeft, aTop);
        ]]>
        </body>
      </method>
    </implementation>     

    <handlers>
      <handler event="contextmenu" action="event.preventDefault();"/>

      <handler event="popupshowing" phase="target">
        <![CDATA[
          var array = [];
          var width = 0;
          for (var menuitem = this.firstChild; menuitem; menuitem = menuitem.nextSibling) {
            if (menuitem.localName == "menuitem" && menuitem.hasAttribute("acceltext")) {
              var accel = document.getAnonymousElementByAttribute(menuitem, "anonid", "accel");
              if (accel && accel.boxObject) {
                array.push(accel);
                if (accel.boxObject.width > width)
                  width = accel.boxObject.width;
              }
            }
          }
          for (var i = 0; i < array.length; i++)
            array[i].width = width;
        ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="tooltip" extends="chrome://global/content/bindings/popup.xml#popup">
    <content>
      <children>
        <xul:label class="tooltip-label" xbl:inherits="value=label,crop" crop="right" flex="1"/>
      </children>
    </content>
    
    <implementation>
      <field name="_mouseOutCount">0</field>
      <field name="_isMouseOver">false</field>
      
      <property name="label"
                onget="return this.getAttribute('label');"
                onset="this.setAttribute('label', val); return val;"/>
    </implementation>

    <handlers>
      <handler event="mouseover"><![CDATA[
        var rel = event.relatedTarget;
        //dump("ENTERING " + (rel ? rel.localName : "null") + "\n");
        if (!rel)
          return;
          
        // find out if the node we entered from is one of our anonymous children
        while (rel) {
          if (rel == this)
            break;
          rel = rel.parentNode;
        }

        // if the exited node is not a descendant of ours, we are entering for the first time
        if (rel != this)
          this._isMouseOver = true;
      ]]></handler>

      <handler event="mouseout"><![CDATA[
        var rel = event.relatedTarget;
        //dump("LEAVING " + (rel ? rel.localName : "null") + "\n");

        // relatedTarget is null when the titletip is first shown: a mouseout event fires
        // because the mouse is exiting the main window and entering the titletip "window".
        // relatedTarget is also null when the mouse exits the main window completely,
        // so count how many times relatedTarget was null after titletip is first shown 
        // and hide popup the 2nd time
        if (!rel) {
          ++this._mouseOutCount;
          if (this._mouseOutCount > 1)
            this.hidePopup();
          return;
        }
          
        // find out if the node we are entering is one of our anonymous children
        while (rel) {
          if (rel == this)
            break;
          rel = rel.parentNode;
        }
        
        // if the entered node is not a descendant of ours, hide the tooltip
        if (rel != this && this._isMouseOver) {
          this.hidePopup();
        }        
      ]]></handler>

      <handler event="popupshowing">
      <![CDATA[
        // fill in the label automatically for the default tooltip
        if (this.getAttribute("default") == "true") {
          var label = "";
          var ttNode = document.tooltipNode;
          if (ttNode && ttNode.hasAttribute("tooltiptext"))
            this.label = ttNode.getAttribute("tooltiptext");
        }

        // This is a special consideration for tree titletips. There is a
        // case where the user moves the mouse from over the treebody to
        // completely outside of the window, while passing over a cell that
        // displays a titletip.  This causes the titletip to show, even though
        // the mouse is not over the corresponding cell, so we need to check
        // if the titletip was entered by the mouse, and if not, hide it.
        // XXX commenting this out for now because it doesn't work on Mac 
        //     become mouseover isn't fired when the tooltip appears with the mouse over it
        //if (this.hasAttribute("titletip"))
        //  window.setTimeout(function(me) { if (!me._isMouseOver) me.hidePopup(); }, 100, this);
      ]]>
      </handler>

      <handler event="popuphiding"><![CDATA[
        this._isMouseOver = false;
        this._mouseOutCount = 0;
      ]]></handler>
    </handlers>
  </binding>
  
  <!-- XXXben this binding is not currently used -->
  <binding id="resizerbase" extends="xul:box">
    <implementation>
      <property name="popup">
        <getter>
        <![CDATA[
          var currNode = this.parentNode;
          while (currNode) {
            try {
              var bo = currNode.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject);
              if (bo)
                return currNode;
            }
            catch (e) {
            }
            currNode = currNode.parentNode;
          }
          return null;
        ]]>
        </getter>
      </property>
      
      <method name="handleMouseMove">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
        ]]>
        </body>
      </method> 
      
      <field name="screenLeft">0</field>
      <field name="screenTop">0</field>
      <field name="resizerDirection">0</field>
    </implementation>
    
    <handlers>
      <handler event="mousedown" phase="capturing"> 
      <![CDATA[
        var popup = event.target.popup;
        var boxObject = popup.boxObject;
        boxObject.captureMouseEvents = true;
        event.target.screenLeft = event.screenX;
        event.target.screenTop = event.screenY;
        event.target.setMode();
        event.preventDefault();
      ]]>
      </handler>
      <handler event="mouseup" phase="capturing">
      <![CDATA[
        var popup = event.target.popup;
        var boxObject = popup.boxObject;
        boxObject.captureMouseEvents = false;
        event.target.screenLeft = event.screenX;
        event.target.screenTop = event.screenY;
        event.target.direction = 0;
        event.preventDefault();
      ]]>
      </handler>
      <handler event="mousemove" phase="capturing">
      <![CDATA[
        event.target.handleMouseMove(event);
        event.preventDefault();
      ]]>
      </handler>
    </handlers>
  </binding>
  
  <!-- East-West resizer -->
  <binding id="ew-resizer" extends="xul:box"> <!-- extends="chrome://global/content/bindings/popup.xml#resizerbase"> -->
    <content>
      <xul:hbox class="ew-resizer-box" flex="1"/>
    </content>
    <!--
    <implementation>
      <method name="handleMouseMove">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          var boxObject = aEvent.target.boxObject;
          if (boxObject.captureMouseEvents && this.resizerDirection == "right") {
            var delta = aEvent.screenX - this.screenLeft;
            this.screenLeft = aEvent.screenX;
            
            // We can set this directly as we're only modifying one dimension
            aEvent.target.width += delta;
          }
        ]]>
        </body>
      </method>
      <method name="setMode">
        <body>
        <![CDATA[
          this.resizerDirection = "right";
        ]]>
        </body>
      </method>
    </implementation>
    -->
  </binding>
  
  <!-- North-South resizer -->
  <binding id="ns-resizer" extends="xul:box"> <!-- extends="chrome://global/content/bindings/popup.xml#resizerbase"> -->
    <content>
      <xul:hbox class="ns-resizer-box" flex="1"/>
    </content>
    <!--
    <implementation>
      <method name="handleMouseMove">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          var boxObject = aEvent.target.boxObject;
          if (boxObject.captureMouseEvents && this.resizerDirection == "bottom") {
            var delta = aEvent.screenY - this.screenTop;
            this.screenTop = aEvent.screenY;
            var currWidth = aEvent.target.boxObject.width;
            
            // We can set this directly as we're only modifying one dimension
            aEvent.target.height += delta;
          }
        ]]>
        </body>
      </method>
      <method name="setMode">
        <body>
        <![CDATA[
          this.resizerDirection = "bottom";
        ]]>
        </body>
      </method>
    </implementation>
    -->
  </binding>
  
  <!-- Diagonal resizer -->
  <binding id="diag-resizer" extends="xul:box"> <!-- extends="chrome://global/content/bindings/popup.xml#resizerbase"> -->
    <content>
      <xul:hbox class="diag-resizer-box" align="center" flex="1">
        <xul:image class="diag-resizer-image"/>
      </xul:hbox>
    </content>
    <!--
    <implementation>
      <method name="handleMouseMove">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          var boxObject = aEvent.target.boxObject;
          if (boxObject.captureMouseEvents && this.resizerDirection == "bottomright") {
            if (!this.screenLeft || !this.screenTop) {
              this.screenLeft = aEvent.screenX; 
              this.screenTop = aEvent.screenY;
            }
            var deltaX = aEvent.screenX - this.screenLeft;
            var deltaY = aEvent.screenY - this.screenTop;
            this.screenLeft = aEvent.screenX;
            this.screenTop = aEvent.screenY;
            var currWidth = aEvent.target.boxObject.width;
            var currHeight = aEvent.target.boxObject.height;
            aEvent.target.sizeTo(currWidth + deltaX, currHeight + deltaY);
          }
        ]]>
        </body>
      </method>
      <method name="setMode">
        <body>
        <![CDATA[
          this.resizerDirection = "bottomright";
        ]]>
        </body>
      </method>
    </implementation>
    -->
  </binding>
  
  <binding id="titlebar" extends="xul:box"> <!-- extends="chrome://global/content/bindings/popup.xml#resizerbase"> -->
    <content>
      <xul:hbox class="titlebar-box" flex="1">
        <xul:hbox class="titlebar-title-box" flex="1" tooltiptext="Click and drag to float">
          <xul:label class="titlebar-title" xbl:inherits="value=title" flex="1" crop="right"/>
        </xul:hbox>
        <xul:button class="popupClose" tooltiptext="Close"/>
      </xul:hbox>
    </content>
    <!--
    <implementation>
      <method name="handleMouseMove">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          if (!this.popup) this.popup = aEvent.target.popup;
          var boxObject = this.popup.boxObject;
          if (boxObject.captureMouseEvents && this.direction == "titlebar") {
            if (!this.screenLeft || !this.screenTop) {
              this.screenLeft = aEvent.screenX; 
              this.screenTop = aEvent.screenY;
            }
            var deltaX = aEvent.screenX - this.screenLeft;
            var deltaY = aEvent.screenY - this.screenTop;
            this.screenLeft = aEvent.screenX;
            this.screenTop = aEvent.screenY;
            var currX = aEvent.target.boxObject.screenX;
            var currY = aEvent.target.boxObject.screenY;
            aEvent.target.moveTo(currX + deltaX, currY + deltaY);
          }
        ]]>
        </body>
      </method>
      <method name="setMode">
        <body>
        <![CDATA[
          this.direction = "titlebar";
        ]]>
        </body>
      </method>
    </implementation>
    -->
  </binding>          

  <binding id="floater-base" display="xul:popup" extends="chrome://global/content/bindings/popup.xml#popup">
    <implementation>
      
      <!-- Popup Manipulation Constants -->
      <field name="MANIPULATE_NONE"      readonly="true">0</field>
      <field name="MANIPULATE_MOVE"      readonly="true">1</field>
      <field name="MANIPULATE_SIZE_EW"   readonly="true">2</field>
      <field name="MANIPULATE_SIZE_NS"   readonly="true">3</field>
      <field name="MANIPULATE_SIZE_DIAG" readonly="true">4</field>
      
      <method name="handleMouseMove">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          var boxObject = this.boxObject;
          if (boxObject.captureMouseEvents) {
            var dX, dY;
            var eScreenX = aEvent.screenX; 
            var eScreenY = aEvent.screenY;
            switch (this.manipulateMode) {
              case this.MANIPULATE_SIZE_EW:
                var width = boxObject.width;
                dX = eScreenX - this.screenLeft;
                this.sizeTo(width + dX, this.height);
                break;
              case this.MANIPULATE_SIZE_NS:
                var height = boxObject.height;
                dY = eScreenY - this.screenTop;
                this.sizeTo(this.width, height + dY);
                break;
              case this.MANIPULATE_SIZE_DIAG:
                dX = eScreenX - this.screenLeft;
                dY = eScreenY - this.screenTop;
                this.sizeTo(this.width + dX, this.height + dY);
                break;
              case this.MANIPULATE_MOVE:
                // XXXben this may not yet be complete. When we drag away from the 
                //        owner, we set some properties to ensure that we aren't
                //        positioned as a menu, automatically closed or steal
                //        keyboard navigation in an inappropriate way. 
                this.autoPosition = false;
                this.enableRollup(false);
                this.enableKeyboardNavigator(false);
                dX = eScreenX - this.screenLeft;
                dY = eScreenY - this.screenTop;
                this.moveTo(this.left + dX, this.top + dY);
                break;
              default:
                break;                      
            }
            this.screenLeft = eScreenX;
            this.screenTop = eScreenY;
          }
        ]]>
        </body>
      </method> 
      <method name="findParentByLocalName">
        <parameter name="aNode"/>
        <parameter name="aLocalName"/>
        <body>
        <![CDATA[
          var parent = aNode;
          var names = [].concat(aLocalName);
          while (parent) {
            for (var i = 0; i < names.length; ++i) {
              if (parent.localName == names[i])
                return parent;
            }
            parent = parent.parentNode;
          }
          return null;
        ]]>
        </body>
      </method>
      <method name="setMode">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          var widget = this.findParentByLocalName(aEvent.originalTarget, ["resizer", "titlebar"]);
          if (widget) {
            if (widget.localName == "titlebar")
              this.manipulateMode = this.MANIPULATE_MOVE;
            else 
              this.sizeDirection = widget.getAttribute("direction");
          }
        ]]>
        </body>
      </method>
      <method name="cleanUp">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          this.boxObject.captureMouseEvents = false;
          this.screenLeft = aEvent.screenX;
          this.screenTop = aEvent.screenY;
          this.manipulateMode = this.MANIPULATE_NONE;
          aEvent.preventDefault();
        ]]>
        </body>
      </method>
      <field name="screenLeft">0</field>
      <field name="screenTop">0</field>
      <field name="manipulateMode">0</field>
    </implementation>
    <handlers>
      <handler event="mousedown" phase="capturing"> 
      <![CDATA[
        this.boxObject.captureMouseEvents = true;
        this.screenLeft = event.screenX;
        this.screenTop = event.screenY;
        this.setMode(event);
      ]]>
      </handler>
      <handler event="mouseup" phase="capturing">
      <![CDATA[
        this.cleanUp(event);
      ]]>
      </handler>
      <handler event="mousemove" phase="capturing">
      <![CDATA[
        this.handleMouseMove(event);
        event.preventDefault();
      ]]>
      </handler>
      <!-- clean up, release the mouse, etc -->
      <handler event="popuphiding">
      <![CDATA[
        this.cleanUp(event);
      ]]>
      </handler>
      <handler event="click">
      <![CDATA[
        // Hide the popup if the [X] box is clicked. 
        // XXXben this may not really belong here, but rather in a derived binding. 
        if (event.originalTarget.className.indexOf("popupClose") != -1)
          this.popupBoxObject.hidePopup();
      ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="popup-scrollbars" extends="chrome://global/content/bindings/popup.xml#popup">
    <content>
      <xul:hbox class="popup-internal-box" flex="1" orient="vertical" style="overflow: auto;">
        <children/>
      </xul:hbox>
    </content>
  </binding>

  <binding id="floater-normal" extends="chrome://global/content/bindings/popup.xml#floater-base">
    <content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
      <vbox class="floater-box" flex="1">
        <box class="floater-box-top">
          <titlebar flex="1" xbl:inherits="title" style="border: 1px outset grey; background-color: grey;"/>
        </box>
        <box class="floater-box-center" flex="1">
          <box class="floater-children" flex="1"> 
            <children/>
          </box>
        </box>
        <box class="floater-box-bottom">
          <resizer direction="bottom" flex="1"/>
          <resizer direction="bottomright" tooltiptext="Click and drag to resize"/>
        </box>
      </vbox>
    </content>
  </binding>

  <binding id="floater-dock-left" extends="chrome://global/content/bindings/popup.xml#floater-base">
    <content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
      <vbox class="floater-box" flex="1">
        <box class="floater-box-top">
          <titlebar flex="1" xbl:inherits="title"/>
        </box>
        <box class="floater-box-center" flex="1">
          <box class="floater-children" flex="1"> 
            <children/>
          </box>
          <resizer direction="right" tooltiptext="Click and drag to resize"/>
        </box>
      </vbox>
    </content>
  </binding>  
  
  <binding id="close-button" extends="chrome://global/content/bindings/button.xml#button-base">
    <content>
      <xul:hbox align="center" flex="1">
        <xul:image class="close-button-x"/>
      </xul:hbox>
    </content>
  </binding>

</bindings>

