<?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/. -->

<!--
  This file has the following external dependencies:
      -gFilterActionStrings from FilterEditor.js
      -gFilterList from FilterEditor.js
      -gFilter from FilterEditor.js
      -gCustomActions from FilterEditor.js
      -gFilterType from FilterEditor.js
      -checkActionsReorder from FilterEditor.js
      -GetMsgFolderFromUri, SetFolderPickerElement from msgFolderPickerOverlay.js
-->

<!DOCTYPE dialog [
  <!ENTITY % filterEditorDTD SYSTEM "chrome://messenger/locale/FilterEditor.dtd" >
%filterEditorDTD;
  <!ENTITY % msgFolderPickerOverlayDTD SYSTEM "chrome://messenger/locale/msgFolderPickerOverlay.dtd" >
%msgFolderPickerOverlayDTD;
]>

<bindings   id="filterBindings"
            xmlns="http://www.mozilla.org/xbl"
            xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
            xmlns:nc="http://home.netscape.com/NC-rdf#"
            xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="ruleactiontype-menulist">
    <content>
      <xul:menulist class="ruleaction-type">
          <xul:menupopup>
            <xul:menuitem label="&moveMessage.label;" value="movemessage" enablefornews="false"/>
            <xul:menuitem label="&copyMessage.label;" value="copymessage"/>
            <xul:menuseparator enablefornews="false"/>
            <xul:menuitem label="&forwardTo.label;" value="forwardmessage" enablefornews="false"/>
            <xul:menuitem label="&replyWithTemplate.label;" value="replytomessage" enablefornews="false"/>
            <xul:menuseparator/>
            <xul:menuitem label="&markMessageRead.label;" value="markasread"/>
            <xul:menuitem label="&markMessageUnread.label;" value="markasunread"/>
            <xul:menuitem label="&markMessageStarred.label;" value="markasflagged"/>
            <xul:menuitem label="&setPriority.label;"  value="setpriorityto"/>
            <xul:menuitem label="&addTag.label;"  value="addtagtomessage"/>
            <xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
            <xul:menuseparator enableforpop3="true"/>
            <xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
            <xul:menuitem label="&deleteFromPOP.label;" value="deletefrompopserver" enableforpop3="true"/>
            <xul:menuitem label="&fetchFromPOP.label;"  value="fetchfrompopserver" enableforpop3="true"/>
            <xul:menuseparator/>
            <xul:menuitem label="&ignoreThread.label;" value="ignorethread"/>
            <xul:menuitem label="&ignoreSubthread.label;" value="ignoresubthread"/>
            <xul:menuitem label="&watchThread.label;" value="watchthread"/>
            <xul:menuseparator/>
            <xul:menuitem label="&stopExecution.label;" value="stopexecution"/>
          </xul:menupopup>
      </xul:menulist>
    </content>

    <implementation>
      <constructor>
        <![CDATA[
          this.addCustomActions();
          this.hideInvalidActions();
          // differentiate between creating a new, next available action,
          // and creating a row which will be initialized with an action
          if (!this.parentNode.hasAttribute('initialActionIndex'))
          {
            var unavailableActions = this.usedActionsList();
            // select the first one that's not in the list
            for (var index = 0; index < this.menuitems.length; index++)
            {
              var menu = this.menuitems[index];
              if (!(menu.value in unavailableActions) && !menu.hidden)
              {
                this.menulist.value = menu.value;
                this.parentNode.setAttribute('value', menu.value);
                break;
              }
            }
          }
          else
          {
            this.parentNode.mActionTypeInitialized = true;
            this.parentNode.clearInitialActionIndex();
          }
        ]]>
      </constructor>

      <field name="menulist">document.getAnonymousNodes(this)[0]</field>
      <field name="menuitems">this.menulist.getElementsByTagNameNS(this.menulist.namespaceURI, 'menuitem')</field>

      <method name="hideInvalidActions">
        <body>
          <![CDATA[
            let menupopup = this.menulist.menupopup;
            let scope = getScopeFromFilterList(gFilterList);

            // walk through the list of filter actions and hide any actions which aren't valid
            // for our given scope (news, imap, pop, etc) and context
            let elements, i;

            // disable / enable all elements in the "filteractionlist"
            // based on the scope and the "enablefornews" attribute
            elements = menupopup.getElementsByAttribute("enablefornews", "true");
            for (i = 0; i < elements.length; i++)
              elements[i].hidden = scope != Components.interfaces.nsMsgSearchScope.newsFilter;

            elements = menupopup.getElementsByAttribute("enablefornews", "false");
            for (i = 0; i < elements.length; i++)
              elements[i].hidden = scope == Components.interfaces.nsMsgSearchScope.newsFilter;

            elements = menupopup.getElementsByAttribute("enableforpop3", "true");
            for (i = 0; i < elements.length; i++)
              elements[i].hidden = !((gFilterList.folder.server.type == "pop3") ||
                                     (gFilterList.folder.server.type == "none"));

            elements = menupopup.getElementsByAttribute("isCustom", "true");
            // Note there might be an additional element here as a placeholder
            // for a missing action, so we iterate over the known actions
            // instead of the elements.
            for (i = 0; i < gCustomActions.length; i++)
              elements[i].hidden = !gCustomActions[i]
                                     .isValidForType(gFilterType, scope);

            // Disable "Reply with Template" if there are no templates.
            if (!this.getTemplates(false)) {
              elements = menupopup.getElementsByAttribute("value", "replytomessage");
              if (elements.length == 1)
                elements[0].hidden = true;
            }
          ]]>
        </body>
      </method>

      <method name="addCustomActions">
        <body>
          <![CDATA[
            var menupopup = this.menulist.menupopup;
            for (var i = 0; i < gCustomActions.length; i++)
            {
              var customAction = gCustomActions[i];
              var menuitem = document.createElementNS(
                "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                "xul:menuitem");
              menuitem.setAttribute("label", customAction.name);
              menuitem.setAttribute("value", customAction.id);
              menuitem.setAttribute("isCustom", "true");
              menupopup.appendChild(menuitem);
            }
          ]]>
        </body>
      </method>

      <!-- returns a hash containing all of the filter actions which are currently being used by other filteractionrows -->
      <method name="usedActionsList">
        <body>
          <![CDATA[
            var usedActions = {};
            var currentFilterActionRow = this.parentNode;
            var listBox = currentFilterActionRow.mListBox; // need to account for the list item
            // now iterate over each list item in the list box
            for (var index = 0; index < listBox.getRowCount(); index++)
            {
              var filterActionRow = listBox.getItemAtIndex(index);
              if (filterActionRow != currentFilterActionRow)
              {
                var actionValue = filterActionRow.getAttribute('value');

                // let custom actions decide if dups are allowed
                var isCustom = false;
                for (var i = 0; i < gCustomActions.length; i++)
                {
                  if (gCustomActions[i].id == actionValue)
                  {
                    isCustom = true;
                    if (!gCustomActions[i].allowDuplicates)
                      usedActions[actionValue] = true;
                    break;
                  }
                }

                if (!isCustom) {
                  // The following actions can appear more than once in a single filter
                  // so do not set them as already used.
                  if (actionValue != 'addtagtomessage' &&
                      actionValue != 'forwardmessage' &&
                      actionValue != 'copymessage')
                    usedActions[actionValue] = true;
                  // If either Delete message or Move message exists, disable the other one.
                  // It does not make sense to apply both to the same message.
                  if (actionValue == 'deletemessage')
                    usedActions['movemessage'] = true;
                  else if (actionValue == 'movemessage')
                    usedActions['deletemessage'] = true;
                  // The same with Mark as read/Mark as Unread.
                  else if (actionValue == 'markasread')
                    usedActions['markasunread'] = true;
                  else if (actionValue == 'markasunread')
                    usedActions['markasread'] = true;
                }
              }
            }
            return usedActions;
          ]]>
        </body>
      </method>

      <!--
        - Check if there exist any templates in this account.
        -
        - @param populateTemplateList  If true, create menuitems representing
        -                              the found templates.
        - @param templateMenuList      The menulist element to create items in.
        -
        - @return  True if at least one template was found, otherwise false.
      -->
      <method name="getTemplates">
        <parameter name="populateTemplateList"/>
        <parameter name="templateMenuList"/>
        <body>
          <![CDATA[
            Components.utils.import("resource:///modules/iteratorUtils.jsm", this);
            let identitiesRaw = MailServices.accounts
              .getIdentitiesForServer(gFilterList.folder.server);
            let identities = [a for each (a in this.fixIterator(identitiesRaw,
                              Components.interfaces.nsIMsgIdentity))];

            if (!identities.length) // typically if this is Local Folders
              identities.push(MailServices.accounts.defaultAccount.defaultIdentity);

            let templateFound = false;
            let foldersScanned = [];

            for each (let identity in identities) {
              let enumerator = null;
              let msgFolder;
              try {
                msgFolder = Components.classes["@mozilla.org/rdf/rdf-service;1"]
                                      .getService(Components.interfaces.nsIRDFService)
                                      .GetResource(identity.stationeryFolder)
                                      .QueryInterface(Components.interfaces.nsIMsgFolder);
                // If we already processed this folder, do not set enumerator
                // so that we skip this identity.
                if (foldersScanned.indexOf(msgFolder) == -1) {
                  foldersScanned.push(msgFolder);
                  enumerator = msgFolder.msgDatabase.EnumerateMessages();
                }
              } catch (e) {
                // The Templates folder may not exist, that is OK.
              }

              if (!enumerator)
                continue;

              while (enumerator.hasMoreElements()) {
                let header = enumerator.getNext();
                if (header instanceof Components.interfaces.nsIMsgDBHdr) {
                  templateFound = true;
                  if (!populateTemplateList)
                    return true;
                  let msgTemplateUri = msgFolder.URI + "?messageId=" +
                    header.messageId + '&subject=' + header.mime2DecodedSubject;
                  let newItem = templateMenuList.appendItem(header.mime2DecodedSubject,
                                                            msgTemplateUri);
                }
              }
            }

            return templateFound;
          ]]>
        </body>
      </method>

    </implementation>

    <handlers>
      <handler event="command">
        <![CDATA[
          this.parentNode.setAttribute('value', this.menulist.value);
          checkActionsReorder();
        ]]>
      </handler>

      <handler event="popupshowing">
        <![CDATA[
          var unavailableActions = this.usedActionsList();
          for (var index = 0; index < this.menuitems.length; index++)
          {
            var menu = this.menuitems[index];
            menu.setAttribute('disabled', menu.value in unavailableActions);
          }
        ]]>
      </handler>
    </handlers>
  </binding>

  <!-- This binding exists to disable the default binding of a listitem
       in the search terms. -->
  <binding id="listitem">
    <implementation>
      <method name="_fireEvent">
        <parameter name="aName"/>
        <body>
          <![CDATA[
            /* This provides a dummy _fireEvent function that
               the listbox expects to be able to call.
               See bug 202036. */
          ]]>
        </body>
      </method>
    </implementation>
  </binding>

  <binding id="ruleaction" extends="#listitem">
    <content allowevents="true">
      <xul:listcell class="ruleactiontype"
                    orient="vertical" align="stretch" pack="center"/>
      <xul:listcell class="ruleactiontarget" xbl:inherits="type=value"
                    orient="vertical" align="stretch" pack="center"/>
      <xul:listcell>
        <xul:button class="small-button"
                    label="+"
                    tooltiptext="&addAction.tooltip;"
                    oncommand="this.parentNode.parentNode.addRow();"/>
        <xul:button class="small-button"
                    label="&#x2212;"
                    tooltiptext="&removeAction.tooltip;"
                    oncommand="this.parentNode.parentNode.removeRow();"
                    anonid="removeButton"/>
      </xul:listcell>
    </content>

    <implementation>
      <field name="mListBox">this.parentNode</field>
      <field name="mRemoveButton">document.getAnonymousElementByAttribute(this, "anonid", "removeButton")</field>
      <field name="mActionTypeInitialized">false</field>
      <field name="mRuleActionTargetInitialized">false</field>
      <field name="mRuleActionType">document.getAnonymousNodes(this)[0]</field>

      <method name="clearInitialActionIndex">
        <body>
          <![CDATA[
            // we should only remove the initialActionIndex after we have been told that
            // both the rule action type and the rule action target have both been built since they both need
            // this piece of information. This complication arises because both of these child elements are getting
            // bound asynchronously after the search row has been constructed

            if (this.mActionTypeInitialized && this.mRuleActionTargetInitialized)
              this.removeAttribute('initialActionIndex');
          ]]>
        </body>
      </method>

      <method name="initWithAction">
        <parameter name="aFilterAction"/>
        <body>
          <![CDATA[
            var filterActionStr;
            var actionTarget = document.getAnonymousNodes(this)[1];
            var actionItem = document.getAnonymousNodes(actionTarget);
            var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
            switch (aFilterAction.type)
            {
              case nsMsgFilterAction.Custom:
                filterActionStr = aFilterAction.customId;
                if (actionItem)
                  actionItem[0].value = aFilterAction.strValue;

                // Make sure the custom action has been added. If not, it
                // probably was from an extension that has been removed. We'll
                // show a dummy menuitem to warn the user.
                var needCustomLabel = true;
                for (var i = 0; i < gCustomActions.length; i++)
                {
                  if (gCustomActions[i].id == filterActionStr)
                  {
                    needCustomLabel = false;
                    break;
                  }
                }
                if (needCustomLabel)
                {
                  var menuitem = document.createElementNS(
                      "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                      "xul:menuitem");
                  menuitem.setAttribute("label",
                      gFilterBundle.getString("filterMissingCustomAction"));
                  menuitem.setAttribute("value", filterActionStr);
                  menuitem.disabled = true;
                  this.mRuleActionType.menulist.menupopup.appendChild(menuitem);
                  var scriptError = Components.classes["@mozilla.org/scripterror;1"]
                      .createInstance(Components.interfaces.nsIScriptError);
                  scriptError.init("Missing custom action " + filterActionStr,
                      null, null, 0, 0,
                      Components.interfaces.nsIScriptError.errorFlag,
                      "component javascript");
                  Services.console.logMessage(scriptError);
                }
                break;
              case nsMsgFilterAction.MoveToFolder:
              case nsMsgFilterAction.CopyToFolder:
                actionItem[0].value = aFilterAction.targetFolderUri;
                break;
              case nsMsgFilterAction.Reply:
              case nsMsgFilterAction.Forward:
                actionItem[0].value = aFilterAction.strValue;
                break;
              case nsMsgFilterAction.Label:
                actionItem[0].value = aFilterAction.label;
                break;
              case nsMsgFilterAction.ChangePriority:
                actionItem[0].value = aFilterAction.priority;
                break;
              case nsMsgFilterAction.JunkScore:
                actionItem[0].value = aFilterAction.junkScore;
                break;
              case nsMsgFilterAction.AddTag:
                actionItem[0].value = aFilterAction.strValue;
                break;
              default:
                break;
            }
            if (aFilterAction.type != nsMsgFilterAction.Custom)
              filterActionStr = gFilterActionStrings[aFilterAction.type];
            document.getAnonymousNodes(this.mRuleActionType)[0]
                    .value = filterActionStr;
            this.mRuleActionTargetInitialized = true;
            this.clearInitialActionIndex();
            checkActionsReorder();
          ]]>
        </body>
      </method>

      <method name="validateAction">
        <body>
          <![CDATA[
            // returns true if this row represents a valid filter action and false otherwise.
            // This routine also prompts the user.
            var filterActionString = this.getAttribute('value');
            var actionTarget = document.getAnonymousNodes(this)[1];
            var errorString, customError;

            switch (filterActionString)
            {
              case "movemessage":
              case "copymessage":
                let msgFolder = document.getAnonymousNodes(actionTarget)[0].value ?
                  GetMsgFolderFromUri(document.getAnonymousNodes(actionTarget)[0].value) : null;
                if (!msgFolder || !msgFolder.canFileMessages)
                  errorString = "mustSelectFolder";
                break;
              case "forwardmessage":
                if (document.getAnonymousNodes(actionTarget)[0].value.length < 3 ||
                    document.getAnonymousNodes(actionTarget)[0].value.indexOf('@') < 1)
                  errorString = "enterValidEmailAddress";
                break;
              case "replytomessage":
                if (!document.getAnonymousNodes(actionTarget)[0].selectedItem)
                   errorString = "pickTemplateToReplyWith";
                break;
              default:
                // some custom actions have no action value node
                if (!document.getAnonymousNodes(actionTarget))
                  return true;
                // locate the correct custom action, and check validity
                for (var i = 0; i < gCustomActions.length; i++)
                  if (gCustomActions[i].id == filterActionString)
                  {
                    customError =
                        gCustomActions[i].validateActionValue(
                          document.getAnonymousNodes(actionTarget)[0].value,
                          gFilterList.folder, gFilterType);
                    break;
                  }
                break;
            }

            errorString = errorString ?
                          gFilterBundle.getString(errorString) :
                          customError;
            if (errorString)
              Services.prompt.alert(window, null, errorString);

            return !errorString;
          ]]>
        </body>
      </method>

      <method name="saveToFilter">
        <parameter name="aFilter"/>
        <body>
          <![CDATA[
            // create a new filter action, fill it in, and then append it to the filter
            var filterAction = aFilter.createAction();
            var filterActionString = this.getAttribute('value');
            filterAction.type = gFilterActionStrings.indexOf(filterActionString);
            var actionTarget = document.getAnonymousNodes(this)[1];
            var actionItem = document.getAnonymousNodes(actionTarget);
            var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
            switch (filterAction.type)
            {
              case nsMsgFilterAction.Label:
                filterAction.label = actionItem[0].getAttribute("value");
                break;
              case nsMsgFilterAction.ChangePriority:
                filterAction.priority = actionItem[0].getAttribute("value");
                break;
              case nsMsgFilterAction.MoveToFolder:
              case nsMsgFilterAction.CopyToFolder:
                filterAction.targetFolderUri = actionItem[0].value;
                break;
              case nsMsgFilterAction.JunkScore:
                filterAction.junkScore = actionItem[0].value;
                break;
              case nsMsgFilterAction.Custom:
                filterAction.customId = filterActionString;
                // fall through to set the value
              default:
                if (actionItem)
                  filterAction.strValue = actionItem[0].value;
                break;
              }
            aFilter.appendAction(filterAction);
          ]]>
        </body>
      </method>

      <method name="getActionStrings">
        <parameter name="aActionStrings"/>
        <body>
          <![CDATA[
            // Collect the action names and arguments in a plain string form.
            let actionTarget = document.getAnonymousNodes(this)[1];
            let actionItem = document.getAnonymousNodes(actionTarget);

            aActionStrings.push({
              label: document.getAnonymousNodes(this.mRuleActionType)[0].label,
              argument: actionItem ?
                        (actionItem[0].label ?
                         actionItem[0].label : actionItem[0].value) : ""
            });
          ]]>
        </body>
      </method>

      <method name="updateRemoveButton">
        <body>
          <![CDATA[
            // if we only have one row of actions, then disable the remove button for that row
            this.mListBox.getItemAtIndex(0).mRemoveButton.disabled = this.mListBox.getRowCount() == 1;
          ]]>
        </body>
      </method>

      <method name="addRow">
        <body>
          <![CDATA[
            let listItem = document.createElement('listitem');
            listItem.className = 'ruleaction';
            listItem.setAttribute('onfocus','this.storeFocus();');
            this.mListBox.insertBefore(listItem, this.nextSibling);
            this.mListBox.ensureElementIsVisible(listItem);

            // make sure the first remove button is enabled
            this.updateRemoveButton();
            checkActionsReorder();
          ]]>
        </body>
      </method>

      <method name="removeRow">
        <body>
          <![CDATA[
            // this.mListBox will fail after the row is removed, so save it
            let listBox = this.mListBox;
            if (listBox.getRowCount() > 1)
              listBox.removeChild(this);
            // can't use 'this' as it is destroyed now
            listBox.getItemAtIndex(0).updateRemoveButton();
            checkActionsReorder();
          ]]>
        </body>
      </method>

      <method name="storeFocus">
        <body>
          <![CDATA[
            // When this action row is focused, store its index in the parent listbox.
            this.mListBox.setAttribute("focusedAction", this.mListBox.getIndexOfItem(this));
          ]]>
        </body>
      </method>

    </implementation>
  </binding>

  <binding id="ruleactiontarget-base">
    <implementation>
      <constructor>
        <![CDATA[
          if (this.parentNode.hasAttribute('initialActionIndex'))
          {
            let actionIndex = this.parentNode.getAttribute('initialActionIndex');
            let filterAction = gFilter.getActionAt(actionIndex);
            this.parentNode.initWithAction(filterAction);
          }
          this.parentNode.updateRemoveButton();
        ]]>
      </constructor>
    </implementation>
  </binding>

  <binding id="ruleactiontarget-tag" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
    <content>
      <xul:menulist class="ruleactionitem">
        <xul:menupopup>
        </xul:menupopup>
      </xul:menulist>
    </content>

    <implementation>
      <constructor>
        <![CDATA[
            let menuPopup = document.getAnonymousNodes(this)[0].menupopup;
            let tagArray = MailServices.tags.getAllTags({});
            for (let i = 0; i < tagArray.length; ++i)
            {
              var taginfo = tagArray[i];
              var newMenuItem = document.createElement('menuitem');
              newMenuItem.setAttribute('label', taginfo.tag);
              newMenuItem.setAttribute('value', taginfo.key);
              menuPopup.appendChild(newMenuItem);
            }
          // propagating a pre-existing hack to make the tag get displayed correctly in the menulist
          // now that we've changed the tags for each menu list. We need to use the current selectedIndex
          // (if its defined) to handle the case where we were initialized with a filter action already.
          var currentItem = document.getAnonymousNodes(this)[0].selectedItem;
          document.getAnonymousNodes(this)[0].selectedItem = null;
          document.getAnonymousNodes(this)[0].selectedItem = currentItem;
        ]]>
      </constructor>
    </implementation>
  </binding>

  <binding id="ruleactiontarget-priority" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
    <content>
      <xul:menulist class="ruleactionitem">
        <xul:menupopup>
          <xul:menuitem value="6" label="&highestPriorityCmd.label;"/>
          <xul:menuitem value="5" label="&highPriorityCmd.label;"/>
          <xul:menuitem value="4" label="&normalPriorityCmd.label;"/>
          <xul:menuitem value="3" label="&lowPriorityCmd.label;"/>
          <xul:menuitem value="2" label="&lowestPriorityCmd.label;"/>
        </xul:menupopup>
      </xul:menulist>
    </content>
  </binding>

  <binding id="ruleactiontarget-junkscore" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
    <content>
      <xul:menulist class="ruleactionitem">
        <xul:menupopup>
          <xul:menuitem value="100" label="&junk.label;"/>
          <xul:menuitem value="0"   label="&notJunk.label;"/>
        </xul:menupopup>
      </xul:menulist>
    </content>
  </binding>

  <binding id="ruleactiontarget-replyto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
    <content>
      <xul:menulist class="ruleactionitem">
        <xul:menupopup>
        </xul:menupopup>
      </xul:menulist>
    </content>

    <implementation>
      <constructor>
        <![CDATA[
          document.getAnonymousElementByAttribute(
            this.parentNode, "class", "ruleactiontype")
            .getTemplates(true, document.getAnonymousNodes(this)[0]);
        ]]>
      </constructor>
    </implementation>
  </binding>

  <binding id="ruleactiontarget-forwardto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
    <content>
      <xul:textbox class="ruleactionitem" flex="1"/>
    </content>
  </binding>

  <binding id="ruleactiontarget-folder" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
    <content>
      <xul:menulist class="ruleactionitem"
                    oncommand="this.parentNode.setPicker(event);">
        <xul:menupopup type="folder"
                       mode="filing"
                       class="menulist-menupopup"
                       showRecent="true"
                       recentLabel="&recentfolders.label;"
                       showFileHereLabel="true"
                       fileHereLabel="&filemessageschoosethis.label;"/>
      </xul:menulist>
    </content>

    <implementation>
      <constructor>
        <![CDATA[
          if (!this.menulist.value)
            this.menulist.value = gFilterList.folder.URI;
          SetFolderPickerElement(this.menulist.value, this.menulist);
        ]]>
      </constructor>

      <field name="menulist">document.getAnonymousNodes(this)[0]</field>
      <method name="setPicker">
        <parameter name="aEvent"/>
        <body>
          <![CDATA[
            aEvent.currentTarget.value = aEvent.target._folder.URI;
            SetFolderPickerElement(aEvent.currentTarget.value, aEvent.currentTarget);
          ]]>
        </body>
      </method>
    </implementation>
  </binding>

</bindings>
