<?xml version="1.0"?>

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

  <!-- dummy widget to force this file to load -->
  <binding id="dummy" extends="xul:box"/>
  <!-- Message Pane Widgets -->
  <binding id="mail-emailaddress" extends="xul:box">
    <content>
      <xul:box inherits="orient" autostretch="never" flex="1">
        <xul:text class="emailDisplayButton" popup="emailAddressPopup" context="emailAddressPopup" inherits="value=label,crop"/>
        <xul:image class="emailDisplayImage" context="emailAddressPopup" inherits="label,crop"/>
      </xul:box>
    </content>
    
    <implementation>  
      <property name="label"      onset="return document.getAnonymousNodes(this)[0].firstChild.setAttribute('label',val);"
                                  onget="return document.getAnonymousNodes(this)[0].firstChild.getAttribute('label');"/>
      <property name="crop"       onset="return document.getAnonymousNodes(this)[0].firstChild.setAttribute('crop',val);"
                                  onget="return document.getAnonymousNodes(this)[0].firstChild.getAttribute('crop');"/>
      <property name="disabled"   onset="return document.getAnonymousNodes(this)[0].firstChild.setAttribute('disabled',val);"
                                  onget="return document.getAnonymousNodes(this)[0].firstChild.getAttribute('disabled');"/>
      <property name="src"        onset="return document.getAnonymousNodes(this)[0].childNodes[1].setAttribute('src',val);"
                                  onget="return document.getAnonymousNodes(this)[0].childNodes[1].getAttribute('src');"/>
      <property name="imgalign"   onset="return document.getAnonymousNodes(this)[0].childNodes[1].setAttribute('imgalign',val);"
                                  onget="return document.getAnonymousNodes(this)[0].childNodes[1].getAttribute('imgalign');"/>
      <method name="setTextAttribute">
        <parameter name="attributeName"/>
        <parameter name="attributeValue"/>
        <body>
          <![CDATA[   
            var IconNode = document.getAnonymousNodes(this)[0].childNodes[1];
            IconNode.setAttribute(attributeName, attributeValue);          
            var textNode = document.getAnonymousNodes(this)[0].firstChild;
            textNode.setAttribute(attributeName, attributeValue);
          ]]>
        </body>
      </method>

      <method name="getTextAttribute">
        <parameter name="attributeName"/>
        <parameter name="attributeValue"/>
        <body>
          <![CDATA[   
            var textNode = document.getAnonymousNodes(this)[0].firstChild;
            return textNode.getAttribute(attributeName, attributeValue);
          ]]>
        </body>
      </method>

      <method name="GetIconNode">
        <body>
          <![CDATA[            
            var ImageNode = document.getAnonymousNodes(this)[0].childNodes[1];
            return ImageNode;
          ]]>
        </body>
      </method>
    </implementation>
  </binding>

  <binding id="search-menulist-abstract" name="searchMenulistAbstract" extends="xul:box">
    <content>
      <xul:menulist class="search-menulist" inherits="flex" oncommand="this.parentNode.onSelect(event)">
        <xul:menupopup class="search-menulist-popup"/>
      </xul:menulist>
    </content>

    <implementation>
      <property name="internalScope">null</property>
      <property readonly="true" name="validityManager">
        <![CDATA[
           Components.classes['@mozilla.org/mail/search/validityManager;1'].getService(Components.interfaces.nsIMsgSearchValidityManager);
        ]]>
      </property>
      <property name="searchScope" onget="return this.internalScope;">
        <!-- scope ID - retrieve the table -->
        <setter>
          <![CDATA[
            // if scope isn't changing this is a noop
            if (this.internalScope == val) return val;

            this.internalScope = val;
            this.refreshList();
            var targets = this.targets;
            if (targets) {
              for (var i=0; i< targets.length; i++) {
                targets[i].searchScope = val;
              }
            }
            return val;
          ]]>
        </setter>
      </property>

      <property name="validityTable" readonly="true" onget="return this.validityManager.getTable(this.searchScope)"/>

      <property name="valueStrings" readonly="true">
        <getter>
          <![CDATA[
            var strings = new Array;
            var ids = this.valueIds;
            var bundle = this.stringBundle;
            for (var i=0; i<ids.length; i++)
                strings[i] = this.stringBundle.GetStringFromID(ids[i]);
            return strings;
          ]]>
        </getter>
      </property>
      <property name="targets" readonly="true">
        <getter>
          <![CDATA[
            var forAttrs =  this.getAttribute("for");
            if (!forAttrs) return null;
            var targetIds = forAttrs.split(",");
            if (targetIds.length == 0) return null;

            var targets = new Array;
            var j=0;
            for (var i=0; i<targetIds.length;i++) {
              var target = document.getElementById(targetIds[i]);
              if (target) targets[j++] = target;
            }
            return targets;
          ]]>
        </getter>
      </property>

      <!-- value forwards to the internal menulist's "value" attribute -->
      <property name="value" onget="return document.getAnonymousNodes(this)[0].selectedItem.getAttribute('value');">
        <setter>
          <![CDATA[
            var menulist = document.getAnonymousNodes(this)[0];
            var dataItems = menulist.getElementsByAttribute("value", val);
            if (dataItems.length > 0)
                menulist.selectedItem = dataItems[0];

            // now notify targets of new parent's value
            var targets = this.targets;
            if (targets) {
              for (var i=0; i< targets.length; i++) {
                targets[i].parentValue = val;
              }
            } else {
                //dump("Doh! No targets!\n");
            }
            return val;
          ]]>
        </setter>
      </property>
      <method name="refreshList">
        <body>
          <![CDATA[
            var menuItemIds = this.valueIds;
            var menuItemStrings = this.valueStrings;

            var menulist = document.getAnonymousNodes(this)[0];
            var popup = menulist.firstChild;

            // save our old "value" so we can restore it later
            var oldData = menulist.value;

            // remove the old popup children
            while (popup.hasChildNodes())
              popup.removeChild(popup.lastChild);

            var newSelection;
            for (var i=0; i<menuItemIds.length; i++) {
              // create the menuitem
              var menuitem = document.createElement("menuitem");
              menuitem.setAttribute("label", menuItemStrings[i]);
              menuitem.setAttribute("value", menuItemIds[i]);
              popup.appendChild(menuitem);

              // try to restore the selection
              if (!newSelection || oldData == menuItemIds[i].toString()) {
                newSelection = menuitem;
              }
            }

            // now restore the selection
            menulist.selectedItem = newSelection
          ]]>
        </body>
      </method>
      <method name="onSelect">
        <parameter name="event"/>
        <body>
          <![CDATA[
            var menulist = document.getAnonymousNodes(this)[0];

            // notify targets
            var targets = this.targets;
            if (targets) {
              for (var i=0; i< targets.length; i++) {
                targets[i].parentValue = menulist.value;
              }
            }
          ]]>
        </body>
      </method>
    </implementation>
  </binding>

  <!-- searchattribute - Subject, Sender, To, CC, etc. -->
  <binding id="searchattribute" name="searchAttribute"
           extends="chrome://messenger/content/mailWidgets.xml#search-menulist-abstract">
    <implementation>
      <property name="stringBundle">
          <![CDATA[
           srGetStrBundle("chrome://messenger/locale/search-attributes.properties");
          ]]>
      </property>
      <property name="valueIds" readonly="true">
        <getter>
          <![CDATA[
            var length = new Object;
            return this.validityTable.getAvailableAttributes(length);
          ]]>
        </getter>
      </property>
    </implementation>
  </binding>

  <!-- searchoperator - Contains, Is Less than, etc -->
  <binding id="searchoperator" name="searchOperator"
           extends="chrome://messenger/content/mailWidgets.xml#search-menulist-abstract">
    <implementation>
      <property name="searchAttribute">null</property>
      <property name="stringBundle">
          <![CDATA[
            srGetStrBundle("chrome://messenger/locale/search-operators.properties");
          ]]>
      </property>
      <property name="valueIds" readonly="true">
        <getter>
          <![CDATA[
            var length = new Object;
            return this.validityTable.getAvailableOperators(this.searchAttribute,length);
          ]]>
        </getter>
      </property>
      <property name="parentValue">
        <setter>
          <![CDATA[
            if (this.searchAttribute == val) return val;
            this.searchAttribute = val;
            this.refreshList();
            return val;
          ]]>
        </setter>
        <getter>
          <![CDATA[
            return this.searchAttribute;
          ]]>
        </getter>
      </property>
    </implementation>
  </binding>

  <!-- searchvalue - a widget which dynamically changes it's user interface
       depending on what type of data it's supposed to be showing
       currently handles arbitrary text entry, and menulists for priority and
       status
  -->
  <binding id="searchvalue" name="searchValue">
    <!-- yeah yeah, this stuff needs to be localized. I'm working on it! -->
    <content>
      <xul:textbox flex="1" class="search-value-textbox"/>
      <xul:menulist flex="1" class="search-value-menulist">
        <xul:menupopup class="search-value-popup">
          <xul:menuitem value="2" stringTag="priorityLowest" class="search-value-menuitem"/>
          <xul:menuitem value="3" stringTag="priorityLow" class="search-value-menuitem"/>
          <xul:menuitem value="4" stringTag="priorityNormal" class="search-value-menuitem"/>
          <xul:menuitem value="5" stringTag="priorityHigh" class="search-value-menuitem"/>
          <xul:menuitem value="6" stringTag="priorityHighest" class="search-value-menuitem"/>
        </xul:menupopup>
      </xul:menulist>
      <xul:menulist flex="1" class="search-value-menulist">
        <xul:menupopup class="search-value-popup">
          <xul:menuitem value="2" stringTag="replied" class="search-value-menuitem"/>
          <xul:menuitem value ="1" stringTag="read" class="search-value-menuitem"/>
          <xul:menuitem value ="1048576" stringTag="new" class="search-value-menuitem"/>
          <xul:menuitem value ="65536" stringTag="forwarded" class="search-value-menuitem"/>
        </xul:menupopup>
      </xul:menulist>
    </content>
    <implementation>
      <property name="internalAttribute">null</property>
      <property name="internalValue">null</property>
      <!-- parentValue forwards to the attribute -->
      <property name="parentValue" onset="return this.searchAttribute=val;"
                                   onget="return this.searchAttribute;"/>
      <property name="searchAttribute" onget="return this.internalAttribute;">
        <setter>
          <![CDATA[
            // noop if we're not changing it
            if (this.internalAttribute == val) return val;
            this.internalAttribute = val;
            // we inherit from a deck, so just use it's index attribute
            // to hide/show widgets
            if (val == Components.interfaces.nsMsgSearchAttrib.Priority)
              this.setAttribute("index", "1");
            else if (val == Components.interfaces.nsMsgSearchAttrib.MsgStatus)
              this.setAttribute("index", "2");
            else
              this.setAttribute("index", "0");
            return val;
          ]]>
        </setter>
      </property>
      <property name="value" onget="return this.internalValue;">
        <setter>
          <![CDATA[
          // val is a nsIMsgSearchValue object
          this.internalValue = val;
          var attrib = val.attrib;
          var nsMsgSearchAttrib = Components.interfaces.nsMsgSearchAttrib;
          var children = document.getAnonymousNodes(this);
          this.searchAttribute = attrib;
          if (attrib == nsMsgSearchAttrib.Priority) {
            var matchingPriority =
                children[1].getElementsByAttribute("value", val.priority);
            if (matchingPriority.length > 0)
                children[1].selectedItem = matchingPriority[0];
          }
          else if (attrib == nsMsgSearchAttrib.MsgStatus) {
            var matchingStatus =
              children[2].getElementsByAttribute("value", val.status);
            if (matchingStatus.length > 0) 
              children[2].selectedItem = matchingStatus[0];
          }
          else if (attrib == nsMsgSearchAttrib.AgeInDays)
            children[0].value = val.age;
          else
            children[0].value = val.str;
          return val;
          ]]>
        </setter>
      </property>
      <method name="save">
        <body>
          <![CDATA[
            var searchValue = this.value;
            var searchAttribute = this.searchAttribute;
            var nsMsgSearchAttrib = Components.interfaces.nsMsgSearchAttrib;
            var children = document.getAnonymousNodes(this);

            searchValue.attrib = searchAttribute;
            if (searchAttribute == nsMsgSearchAttrib.Priority) {
               searchValue.priority = children[1].selectedItem.value;
            }
            else if (searchAttribute == nsMsgSearchAttrib.MsgStatus)
               searchValue.status = children[2].selectedItem.value;
            else if (searchAttribute == nsMsgSearchAttrib.AgeInDays)
               searchValue.age = children[0].value;
            else
               searchValue.str = children[0].value;
          ]]>
        </body>
      </method>
      <method name="saveTo">
        <parameter name="searchValue"/>
        <body>
          <![CDATA[
            this.internalValue = searchValue;
            this.save();
          ]]>
        </body>
      </method>
      <method name="fillStringsForChildren">
        <parameter name="parentNode"/>
        <parameter name="bundle"/>
        <body>
          <![CDATA[
            var children = parentNode.childNodes;
            var len=children.length;
            for (var i=0; i<len; i++) {
              var node = children[i];
              var stringTag = node.getAttribute("stringTag");
              if (stringTag) {
                var attr = (node.tagName == "text") ? "value" : "label";
                node.setAttribute(attr, bundle.GetStringFromName(stringTag));
              }
            }
          ]]>
        </body>
      </method>
	  <constructor>
      <![CDATA[
        // initialize strings
        var bundle = srGetStrBundle("chrome://messenger/locale/messenger.properties");
        this.fillStringsForChildren(document.getAnonymousNodes(this)[1].firstChild, bundle);
        this.fillStringsForChildren(document.getAnonymousNodes(this)[2].firstChild, bundle);
      ]]>
      </constructor>
    </implementation>
    <handlers>
	<handler event="keypress"><![CDATA[
	  if (event.keyCode == 13) {
	    onEnterInSearchTerm(); 
          }
        ]]></handler>
    </handlers>
  </binding>
  <binding id="searchterm" name="searchTerm" extends="xul:box">
    <implementation>
      <property name="internalSearchTerm">null</property>
      <property name="internalBooleanAnd">null</property>
      <!-- the actual nsIMsgSearchTerm object -->
      <property name="searchTerm" onget="return this.internalSearchTerm">
        <setter>
          <![CDATA[
            this.internalSearchTerm = val;

            var term = val;
            // val is a nsIMsgSearchTerm
            var searchAttribute=this.searchattribute;
            var searchOperator=this.searchoperator;
            var searchValue=this.searchvalue;

            // now reflect all attributes of the searchterm into the widgets
            if (searchAttribute) searchAttribute.value = term.attrib;
            if (searchOperator) searchOperator.value = val.op;
            if (searchValue) searchValue.value = term.value;

            this.booleanAnd = val.booleanAnd;
          ]]>
        </setter>
      </property>
 
      <property name="searchScope">
        <getter>
          <![CDATA[
             var searchAttribute = this.searchattribute;
             if (searchAttribute)
               return searchAttribute.searchScope;
             return undefined;
           ]]>
        </getter>
        <setter>
          <![CDATA[
            var searchAttribute = this.searchattribute;
            if (searchAttribute) searchAttribute.searchScope=val;
           ]]>
        </setter>
      </property>
      <!-- the three tags that make up a term - to use, set the
           attribute in the XUL to the ID of the term.
        -->
      <property name="searchattribute" 
                onget="return document.getElementById(this.getAttribute('searchattribute'));"
                onset="this.setAttribute('searchattribute',val.id)"/>

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

      <property name="searchvalue" 
                onget="return document.getElementById(this.getAttribute('searchvalue'));"
                onset="this.setAttribute('searchvalue',val.id)"/>
      <property name="booleanNodes">
        <![CDATA[
          null;
        ]]>
      </property>
      <property name="stringBundle">
          <![CDATA[
             srGetStrBundle("chrome://messenger/locale/search.properties");
          ]]>
      </property>
      <property name="booleanAnd" onget="return this.internalBooleanAnd">
        <setter>
          <![CDATA[
            // whenever you set this, all nodes in booleanNodes
            // are updated to reflect the string

            if (this.internalBooleanAnd == val) return;
            this.internalBooleanAnd = val;

            var booleanNodes = this.booleanNodes;
            if (!booleanNodes) return;

            var stringBundle = this.stringBundle;
            var andString = val ? "And" : "Or";
            for (var i=0; i<booleanNodes.length; i++) {
              try {              
                var staticString =
                    stringBundle.GetStringFromName("search" + andString + i);
                if (staticString && staticString.length>0)
                    booleanNodes[i].setAttribute("value", staticString);
              } catch (ex) { /* no error, means string not found */}
            }
          ]]>
        </setter>
      </property>
      <method name="save">
        <body>
          <![CDATA[
            var searchTerm = this.searchTerm;
            searchTerm.attrib = this.searchattribute.value;
            searchTerm.op = this.searchoperator.value;
            if (this.searchvalue.value)
              this.searchvalue.save();
            else
              this.searchvalue.saveTo(searchTerm.value);
            searchTerm.value = this.searchvalue.value;
            searchTerm.booleanAnd = this.booleanAnd;
          ]]>
        </body>
      </method>
      <!-- if you have a search term element with no search term -->
      <method name="saveTo">
        <parameter name="searchTerm"/>
        <body>
          <![CDATA[
            this.internalSearchTerm = searchTerm;
            this.save();
          ]]>
        </body>
      </method>
    </implementation>
  </binding>
</bindings>
