diff options
Diffstat (limited to 'mailnews/base/content/folderWidgets.xml')
-rw-r--r-- | mailnews/base/content/folderWidgets.xml | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/mailnews/base/content/folderWidgets.xml b/mailnews/base/content/folderWidgets.xml new file mode 100644 index 000000000..605f4230c --- /dev/null +++ b/mailnews/base/content/folderWidgets.xml @@ -0,0 +1,829 @@ +<?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="mailFolderBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xbl="http://www.mozilla.org/xbl" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <binding id="folder-menupopup" + extends="chrome://global/content/bindings/popup.xml#popup"> + <implementation> + <constructor><![CDATA[ + Components.utils.import("resource:///modules/FeedUtils.jsm", this); + Components.utils.import("resource:///modules/folderUtils.jsm", this); + Components.utils.import("resource:///modules/iteratorUtils.jsm", this); + Components.utils.import("resource:///modules/mailServices.js", this); + Components.utils.import("resource:///modules/MailUtils.js", this); + Components.utils.import("resource:///modules/StringBundle.js", this); + this._stringBundle = new this + .StringBundle("chrome://messenger/locale/folderWidgets.properties"); + + // Get the displayformat if set. + if (this.parentNode && this.parentNode.localName == "menulist") + this._displayformat = this.parentNode.getAttribute("displayformat"); + + // Find out if we are in a wrapper (customize toolbars mode is active). + let inWrapper = false; + let node = this; + while (node instanceof XULElement) { + if (node.id.startsWith("wrapper-")) { + inWrapper = true; + break; + } + node = node.parentNode; + } + + if (!inWrapper) { + if (this.hasAttribute("original-width")) { + // If we were in a wrapper before and have a width stored, restore it now. + if (this.getAttribute("original-width") == "none") + this.removeAttribute("width"); + else + this.setAttribute("width", this.getAttribute("original-width")); + + this.removeAttribute("original-width"); + } + + // If we are a child of a menulist, and we aren't in a wrapper, we + // need to build our content right away, otherwise the menulist + // won't have proper sizing. + if (this.parentNode && this.parentNode.localName == "menulist") + this._ensureInitialized(); + } else { + // But if we're in a wrapper, remove our children, because we're + // getting re-created when the toolbar customization closes. + this._teardown(); + + // Store our current width and set a safe small width when we show + // in a wrapper. + if (!this.hasAttribute("original-width")) { + this.setAttribute("original-width", this.hasAttribute("width") ? + this.getAttribute("width") : "none"); + this.setAttribute("width", "100"); + } + } + ]]></constructor> + <destructor><![CDATA[ + // Clean up when being destroyed. + this._removeListener(); + ]]></destructor> + <!-- + - Make sure we remove our listener when the window is being destroyed + - or the widget teared down. + --> + <method name="_removeListener"> + <body><![CDATA[ + if (!this._initialized) + return; + + this.MailServices.mailSession.RemoveFolderListener(this._listener); + ]]></body> + </method> + + <field name="_stringBundle">null</field> + + <!-- + - If non-null, the subFolders of this nsIMsgFolder will be used to + - populate this menu. If this is null, the menu will be populated + - using the root-folders for all accounts + --> + <field name="_parentFolder">null</field> + <property name="parentFolder" + onget="return this._parentFolder;" + onset="return this._parentFolder = val;"/> + + <!-- + - Various filtering modes can be used with this menu-binding. To use + - one of them, append the mode="foo" attribute to the element. When + - building the menu, we will then use this._filters[mode] as a filter + - function to eliminate folders that should not be shown. + - + - Note that extensions should feel free to plug in here! + --> + <field name="_filters"><![CDATA[({ + // Returns true if messages can be filed in the folder + filing: function filter_filing(aFolder) { + if (!aFolder.server.canFileMessagesOnServer) + return false; + + return (aFolder.canFileMessages || aFolder.hasSubFolders); + }, + + // Returns true if we can get mail for this folder. (usually this just + // means the "root" fake folder) + getMail: function filter_getMail(aFolder) { + if (aFolder.isServer && aFolder.server.type != "none") + return true; + if (aFolder.server.type == "nntp" || aFolder.server.type == "rss") + return true; + return false; + }, + + // Returns true if we can add filters to this folder/account + filters: function filter_filter(aFolder) { + // We can always filter news + if (aFolder.server.type == "nntp") + return true; + + return aFolder.server.canHaveFilters; + }, + + subscribe: function filter_subscribe(aFolder) { + return aFolder.canSubscribe; + }, + + newFolder: function filter_newFolder(aFolder) { + return aFolder.canCreateSubfolders && + aFolder.server.canCreateFoldersOnServer; + }, + + deferred: function filter_defered(aFolder) { + return aFolder.server.canCreateFoldersOnServer && + !aFolder.supportsOffline; + }, + + // Folders that are not in a deferred account + notDeferred: function(aFolder) { + let server = aFolder.server; + return !(server instanceof Components.interfaces.nsIPop3IncomingServer && + server.deferredToAccount); + }, + + // Folders that can be searched. + search: function filter_search(aFolder) { + if (!aFolder.server.canSearchMessages || + aFolder.getFlag(Components.interfaces.nsMsgFolderFlags.Virtual)) + return false; + return true; + }, + + // Folders that can subscribe feeds. + feeds: function filter_feeds(aFolder) { + if (aFolder.server.type != "rss" || + aFolder.getFlag(Components.interfaces.nsMsgFolderFlags.Trash) || + aFolder.getFlag(Components.interfaces.nsMsgFolderFlags.Virtual)) + return false; + return true; + }, + + junk: function filter_junk(aFolder) { + // Don't show servers (nntp & any others) which do not allow search or filing + // I don't really understand why canSearchMessages is needed, but it was included in + // earlier code, so I include it as well. + if (!aFolder.server.canFileMessagesOnServer || !aFolder.server.canSearchMessages) + return false; + // show parents that might have usable subfolders, or usable folders + return aFolder.hasSubFolders || aFolder.canFileMessages; + } + })]]></field> + + <!-- + - The maximum number of entries in the "Recent" menu + --> + <field name="_MAXRECENT">15</field> + + <!-- + - Is this list containing only servers (accounts) and no real folders? + --> + <field name="_serversOnly">true</field> + + <!-- + - Our listener to let us know when folders change/appear/disappear so + - we can know to rebuild ourselves. + --> + <field name="_listener"> + <![CDATA[({ + _menu: this, + _clearMenu: function(aMenu) { + if (aMenu._teardown) + aMenu._teardown(); + }, + OnItemAdded: function act_add(aRDFParentItem, aItem) { + if (!(aItem instanceof Components.interfaces.nsIMsgFolder)) + return; + if (this._filterFunction && !this._filterFunction(aItem)) { + return; + } + //xxx we can optimize this later + //xxx I'm not quite sure why this isn't always a function + if (this._menu._teardown) + this._menu._teardown(); + }, + + OnItemRemoved: function act_remove(aRDFParentItem, aItem) { + if (!(aItem instanceof Components.interfaces.nsIMsgFolder)) + return; + if (this._filterFunction && !this._filterFunction(aItem)) { + return; + } + //xxx we can optimize this later + if (this._menu._teardown) + this._menu._teardown(); + }, + + //xxx I stole this listener list from nsMsgFolderDatasource.cpp, but + // someone should really document what events are fired when, so that + // we make sure we're updating at the right times. + OnItemPropertyChanged: function(aItem, aProperty, aOld, aNew) {}, + OnItemIntPropertyChanged: function(aItem, aProperty, aOld, aNew) { + var child = this._getChildForItem(aItem); + if (child) + this._menu._setCssSelectors(child._folder, child); + }, + OnItemBoolPropertyChanged: function(aItem, aProperty, aOld, aNew) { + var child = this._getChildForItem(aItem); + if (child) + this._menu._setCssSelectors(child._folder, child); + }, + OnItemUnicharPropertyChanged: function(aItem, aProperty, aOld, aNew) { + var child = this._getChildForItem(aItem); + if (child) + this._menu._setCssSelectors(child._folder, child); + }, + OnItemPropertyFlagChanged: function(aItem, aProperty, aOld, aNew) {}, + OnItemEvent: function(aFolder, aEvent) { + if (aEvent.toString() == "MRMTimeChanged") { + if (this._menu.getAttribute("showRecent") != "true" || + !this._menu.firstChild || !this._menu.firstChild.firstChild) + return; + // if this folder is already in the recent menu, return. + if (this._getChildForItem(aFolder, + this._menu.firstChild.firstChild)) + return; + } + // Special casing folder renames here, since they require more work + // since sort-order may have changed. + else if (aEvent.toString() == "RenameCompleted") { + if (!this._getChildForItem(aFolder)) + return; + } + else + return; + // folder renamed, or new recent folder, so rebuild. + setTimeout(this._clearMenu, 0, this._menu); + }, + + /** + * Helper function to check and see whether we have a menuitem for this + * particular nsIMsgFolder + * + * @param aItem the nsIMsgFolder to check + * @param aMenu (optional) menu to look in, defaults to this._menu. + * @returns null if no child for that folder exists, otherwise the + * menuitem for that child + */ + _getChildForItem: function act__itemIsChild(aItem, aMenu) { + aMenu = aMenu || this._menu; + if (!aMenu || !aMenu.childNodes) + return null; + + if (!(aItem instanceof Components.interfaces.nsIMsgFolder)) + return null; + for (let i = 0; i < aMenu.childNodes.length; i++) { + let folder = aMenu.childNodes[i]._folder; + if (folder && folder.URI == aItem.URI) + return aMenu.childNodes[i]; + } + return null; + } + })]]></field> + + <!-- + - True if we have already built our menu-items and are now just + - listening for changes + --> + <field name="_initialized">false</field> + + <!-- + - Call this if you are unsure whether the menu-items have been built, + - but know that they need to be built now if they haven't. + --> + <method name="_ensureInitialized"> + <body><![CDATA[ + if (this._initialized) + return; + + // I really wish they'd just make this global... + const Ci = Components.interfaces; + + let folders; + + // Figure out which folders to build. If we don't have a parent, then + // we assume we should build the top-level accounts. (Actually we + // build the fake root folders for those accounts.) + if (!this._parentFolder) { + let accounts = this.allAccountsSorted(true); + + // Now generate our folder-list. Note that we'll special case this + // situation below, to avoid destroying the sort order we just made + folders = accounts.map(acct => acct.incomingServer.rootFolder); + } else { + // If we do have a parent folder, then we just build based on those + // subFolders for that parent. + folders = this.toArray(this.fixIterator(this._parentFolder.subFolders, + Ci.nsIMsgFolder)); + } + + this._build(folders); + + // Lastly, we add a listener to get notified of changes in the folder + // structure. + this.MailServices.mailSession.AddFolderListener(this._listener, + Ci.nsIFolderListener.all); + + this._initialized = true; + ]]></body> + </method> + + <!-- + - Actually constructs the menu-items based on the folders given. + - + - @param aFolders An array of nsIMsgFolders to use for building. + --> + <method name="_build"> + <parameter name="aFolders"/> + <body><![CDATA[ + let folders; + let excludeServers = []; + let disableServers = []; + + // excludeServers attribute is a comma separated list of server keys. + if (this.hasAttribute("excludeServers")) + excludeServers = this.getAttribute("excludeServers").split(","); + + // disableServers attribute is a comma separated list of server keys. + if (this.hasAttribute("disableServers")) + disableServers = this.getAttribute("disableServers").split(","); + + // Extensions and other consumers can add to these modes too, see the + // above note on the _filters field. + var mode = this.getAttribute("mode"); + if (mode && mode != "") { + var filterFunction = this._filters[mode]; + folders = aFolders.filter(filterFunction); + this._listener._filterFunction = filterFunction; + } else { + folders = aFolders; + } + + if (excludeServers.length > 0) { + folders = folders.filter(function(aFolder) { + return !(excludeServers.indexOf(aFolder.server.key) != -1); }); + } + + /* This code block will do the following: Add a menu item that refers + back to the parent folder when there is a showFileHereLabel + attribute or no mode attribute. However the code won't add such a + menu item if one of the following conditions is met: + (*) There is no parent folder + (*) Folder is server and showAccountsFileHere is explicitly false + (*) Current folder has a mode, the parent folder can be selected, + no messages can be filed into the parent folder (e.g. when the + parent folder is a news group or news server) and the folder + mode is not equal to newFolder + + The menu item will have the value of the fileHereLabel attribute as + label or if the attribute does not exist the name of the parent + folder instead. + */ + let parent = this._parentFolder; + if (parent && (this.getAttribute("showFileHereLabel") == "true" || !mode)) { + let showAccountsFileHere = this.getAttribute("showAccountsFileHere"); + if ((!parent.isServer || showAccountsFileHere != "false") && + (!mode || mode == "newFolder" || parent.noSelect || + parent.canFileMessages || showAccountsFileHere == "true")) { + var menuitem = document.createElement("menuitem"); + menuitem._folder = this._parentFolder; + menuitem.setAttribute("generated", "true"); + if (this.hasAttribute("fileHereLabel")) { + menuitem.setAttribute("label", this.getAttribute("fileHereLabel")); + menuitem.setAttribute("accesskey", this.getAttribute("fileHereAccessKey")); + } else { + menuitem.setAttribute("label", this._parentFolder.prettyName); + menuitem.setAttribute("class", "folderMenuItem menuitem-iconic"); + this._setCssSelectors(this._parentFolder, menuitem); + } + // Eww. have to support some legacy code here... + menuitem.setAttribute("id", this._parentFolder.URI); + this.appendChild(menuitem); + + if (this._parentFolder.noSelect) + menuitem.setAttribute("disabled", "true"); + + var sep= document.createElement("menuseparator"); + sep.setAttribute("generated", "true"); + this.appendChild(sep); + } + } + + let globalInboxFolder = null; + // See if this is the toplevel menu (usually with accounts). + if (!this._parentFolder) { + // Some menus want a "Recent" option, but that should only be on our + // top-level menu + if (this.getAttribute("showRecent") == "true") + this._buildRecentMenu(); + // If we are showing the accounts for deferring, move Local Folders to the top. + if (mode == "deferred") { + globalInboxFolder = this.MailServices.accounts.localFoldersServer + .rootFolder; + let localFoldersIndex = folders.indexOf(globalInboxFolder); + if (localFoldersIndex != -1) { + folders.splice(localFoldersIndex, 1); + folders.unshift(globalInboxFolder); + } + } + // If we're the root of the folder hierarchy, then we actually don't + // want to sort the folders, but rather the accounts to which the + // folders belong. Since that sorting was already done, we don't need + // to do anything for that case here. + } else { + // Sorts the list of folders. We give first priority to the sortKey + // property if it is available, otherwise a case-insensitive + // comparison of names. + folders = folders.sort(function nameCompare(a, b) { + return a.compareSortKeys(b); + }); + } + + /* In some cases, the user wants to have a list of subfolders for only + * some account types (or maybe all of them). So we use this to + * determine what the user wanted. + */ + var shouldExpand; + var labels = null; + if (this.getAttribute("expandFolders") == "true" || + !this.hasAttribute("expandFolders")) { + shouldExpand = function (e) { return true; }; + } else if (this.getAttribute("expandFolders") == "false") { + shouldExpand = function (e) { return false; }; + } else { + /* We want a subfolder list for only some servers. We also may need + * to create headers to select the servers. If so, then headlabels + * is a comma-delimited list of labels corresponding to the server + * types specified in expandFolders. + */ + var types = this.getAttribute("expandFolders").split(/ *, */); + // Set the labels. labels[type] = label + if (this.hasAttribute("headlabels")) { + var labelNames = this.getAttribute("headlabels").split(/ *, */); + labels = {}; + // If the length isn't equal, don't give them any of the labels, + // since any combination will probably be wrong. + if (labelNames.length == types.length) { + for (var index in types) + labels[types[index]] = labelNames[index]; + } + } + shouldExpand = function (e) { return types.indexOf(e) != -1; }; + } + + // We need to call this, or hasSubFolders will always return false. + // Remove this workaround when Bug 502900 is fixed. + this.MailUtils.discoverFolders(); + this._serversOnly = true; + + for (let folder of folders) { + let node; + if (!folder.isServer) + this._serversOnly = false; + + // If we're going to add subFolders, we need to make menus, not + // menuitems. + if (!folder.hasSubFolders || !shouldExpand(folder.server.type)) { + node = document.createElement("menuitem"); + // Grumble, grumble, legacy code support + node.setAttribute("id", folder.URI); + node.setAttribute("class", "folderMenuItem menuitem-iconic"); + node.setAttribute("generated", "true"); + this.appendChild(node); + } else { + this._serversOnly = false; + //xxx this is slightly problematic in that we haven't confirmed + // whether any of the subfolders will pass the filter + node = document.createElement("menu"); + node.setAttribute("class", "folderMenuItem menu-iconic"); + node.setAttribute("generated", "true"); + this.appendChild(node); + + // Create the submenu + // (We must use cloneNode here because on OS X the native menu + // functionality and very sad limitations of XBL1 cause the bindings + // to never get created for popup if we create a new element. We + // perform a shallow clone to avoid picking up any of our children.) + var popup = this.cloneNode(false); + popup._parentFolder = folder; + popup.setAttribute("class", this.getAttribute("class")); + popup.setAttribute("type", this.getAttribute("type")); + if (this.hasAttribute("fileHereLabel")) + popup.setAttribute("fileHereLabel", + this.getAttribute("fileHereLabel")); + popup.setAttribute("showFileHereLabel", + this.getAttribute("showFileHereLabel")); + popup.setAttribute("oncommand", + this.getAttribute("oncommand")); + popup.setAttribute("mode", + this.getAttribute("mode")); + if (this.hasAttribute("disableServers")) + popup.setAttribute("disableServers", + this.getAttribute("disableServers")); + if (this.hasAttribute("position")) + popup.setAttribute("position", + this.getAttribute("position")); + + // If there are labels, add the labels now + if (labels) { + var serverNode = document.createElement("menuitem"); + serverNode.setAttribute("label", labels[folder.server.type]); + serverNode._folder = folder; + serverNode.setAttribute("generated", "true"); + popup.appendChild(serverNode); + var sep = document.createElement("menuseparator"); + sep.setAttribute("generated", "true"); + popup.appendChild(sep); + } + + popup.setAttribute("generated", "true"); + node.appendChild(popup); + } + + if (disableServers.indexOf(folder.server.key) != -1) + node.setAttribute("disabled", "true"); + + node._folder = folder; + let label = ""; + if (mode == "deferred" && folder.isServer && + folder.server.rootFolder == globalInboxFolder) { + label = this._stringBundle.get("globalInbox", [folder.prettyName]); + } else { + label = folder.prettyName; + } + node.setAttribute("label", label); + this._setCssSelectors(folder, node); + } + ]]></body> + </method> + + <!-- + - Builds a submenu with all of the recently used folders in it, to + - allow for easy access. + --> + <method name="_buildRecentMenu"> + <body><![CDATA[ + const Ci = Components.interfaces; + + // Iterate through all folders in all accounts, and find 15 (_MAXRECENT) + // of most recently modified ones. + let allFolders = this.toArray( + this.fixIterator(this.MailServices.accounts.allFolders, Ci.nsIMsgFolder)); + + allFolders = allFolders.filter(f => f.canFileMessages); + + let recentFolders = this.getMostRecentFolders(allFolders, + this._MAXRECENT, + "MRMTime"); + + // Cache the pretty names so that they do not need to be fetched + // _MAXRECENT^2 times later. + recentFolders = recentFolders.map( + function (f) { return { folder: f, name: f.prettyName } }); + + // Because we're scanning across multiple accounts, we can end up with + // several folders with the same name. Find those dupes. + let dupeNames = new Set(); + for (let i = 0; i < recentFolders.length; i++) { + for (let j = i + 1; j < recentFolders.length; j++) { + if (recentFolders[i].name == recentFolders[j].name) + dupeNames.add(recentFolders[i].name); + } + } + + for (let folderItem of recentFolders) { + // If this folder name appears multiple times in the recent list, + // append the server name to disambiguate. + // TODO: + // - maybe this could use verboseFolderFormat from messenger.properties + // instead of hardcoded " - ". + // - disambiguate folders with same name in same account + // (in different subtrees). + let label = folderItem.name; + if (dupeNames.has(label)) + label += " - " + folderItem.folder.server.prettyName; + + folderItem.label = label; + } + + // Make sure the entries are sorted alphabetically. + recentFolders.sort((a, b) => this.folderNameCompare(a.label, b.label)); + + // Now create the Recent folder and its children + var menu = document.createElement("menu"); + menu.setAttribute("label", this.getAttribute("recentLabel")); + menu.setAttribute("accesskey", this.getAttribute("recentAccessKey")); + var popup = document.createElement("menupopup"); + popup.setAttribute("class", this.getAttribute("class")); + popup.setAttribute("generated", "true"); + menu.appendChild(popup); + + // Create entries for each of the recent folders. + for (let folderItem of recentFolders) { + let node = document.createElement("menuitem"); + + node.setAttribute("label", folderItem.label); + node._folder = folderItem.folder; + + node.setAttribute("class", "folderMenuItem menuitem-iconic"); + this._setCssSelectors(folderItem.folder, node); + node.setAttribute("generated", "true"); + popup.appendChild(node); + } + menu.setAttribute("generated", "true"); + this.appendChild(menu); + if (!recentFolders.length) + menu.setAttribute("disabled", "true"); + + var sep = document.createElement("menuseparator"); + sep.setAttribute("generated", "true"); + this.appendChild(sep); + ]]></body> + </method> + + <!-- + - This function adds attributes on menu/menuitems to make it easier for + - css to style them. + - + - @param aFolder the folder that corresponds to the menu/menuitem + - @param aMenuNode the actual DOM node to set attributes on + --> + <method name="_setCssSelectors"> + <parameter name="aFolder"/> + <parameter name="aMenuNode"/> + <body><![CDATA[ + + // First set the SpecialFolder attribute + aMenuNode.setAttribute("SpecialFolder", this.getSpecialFolderString(aFolder)); + + // Now set the biffState + let biffStates = ["NewMail", "NoMail", "UnknownMail"]; + for (let state of biffStates) { + if (aFolder.biffState == + Components.interfaces.nsIMsgFolder["nsMsgBiffState_" + state]) { + aMenuNode.setAttribute("BiffState", state); + break; + } + } + + aMenuNode.setAttribute("IsServer", aFolder.isServer); + aMenuNode.setAttribute("IsSecure", aFolder.server.isSecure); + aMenuNode.setAttribute("ServerType", aFolder.server.type); + aMenuNode.setAttribute("IsFeedFolder", + (this.FeedUtils.getFeedUrlsInFolder(aFolder) ? true : false)); + ]]></body> + </method> + + <!-- + - This function returns a formatted display name for a menulist + - selected folder. The desired format is set as the 'displayformat' + - attribute of the folderpicker's <menulist>, one of: + - 'name' (default) - Folder + - 'verbose' - Folder on Account + - 'path' - Account/Folder/Subfolder + - + - @param aFolder the folder that corresponds to the menu/menuitem + - @return string display name + --> + <field name="_displayformat">null</field> + <method name="getDisplayName"> + <parameter name="aFolder"/> + <body><![CDATA[ + if (aFolder.isServer) + return aFolder.prettyName; + + if (this._displayformat == "verbose") + return this._stringBundle.getFormattedString("verboseFolderFormat", + [aFolder.prettyName, aFolder.server.prettyName]); + + if (this._displayformat == "path") + return this.FeedUtils.getFolderPrettyPath(aFolder) || aFolder.name; + + return aFolder.name; + ]]></body> + </method> + + <!-- + - Makes a given folder selected. + - + - @param aFolder the folder to select (if none, then Choose Folder) + - @note If aFolder is not in this popup, but is instead a descendant of + - a member of the popup, that ancestor will be selected. + - @return true if any usable folder was found, otherwise false. + --> + <method name="selectFolder"> + <parameter name="aFolder"/> + <body><![CDATA[ + // Set the label of the menulist element as if aFolder had been selected. + function setupParent(aFolder, aMenulist, aNoFolders) { + let menupopup = aMenulist.menupopup; + if (aFolder) { + aMenulist.setAttribute("label", menupopup.getDisplayName(aFolder)); + } else { + aMenulist.setAttribute("label", menupopup._stringBundle.getString( + aNoFolders ? "noFolders" : + (menupopup._serversOnly ? "chooseAccount" : "chooseFolder"))); + } + aMenulist.setAttribute("value", + aFolder ? aFolder.URI : ""); + aMenulist.setAttribute("IsServer", + aFolder ? aFolder.isServer : false); + aMenulist.setAttribute("IsSecure", + aFolder ? aFolder.server.isSecure : false); + aMenulist.setAttribute("ServerType", + aFolder ? aFolder.server.type : "none"); + aMenulist.setAttribute("SpecialFolder", + aFolder ? menupopup.getSpecialFolderString(aFolder) : "none"); + aMenulist.setAttribute("IsFeedFolder", Boolean( + aFolder && menupopup.FeedUtils.getFeedUrlsInFolder(aFolder))); + } + + let folder; + if (aFolder) { + for (let child of this.childNodes) { + if (child && child._folder && !child.disabled && + (child._folder.URI == aFolder.URI || + (child.tagName == "menu" && + child._folder.isAncestorOf(aFolder)))) { + if (child._folder.URI == aFolder.URI) + this.parentNode.selectedItem = child; + folder = aFolder; + break; + } + } + } + + // If the caller specified a folder to select and it was not + // found, or if the caller didn't pass a folder (meaning a logical + // and valid folder wasn't determined), don't blow up but reset + // attributes and set a nice Choose Folder label so the user may + // select a valid folder per the filter for this picker. If there are + // no children, then no folder passed the filter; disable the menulist + // as there's nothing to choose from. + let noFolders; + if (!this.childElementCount) + { + this.parentNode.setAttribute("disabled", true); + noFolders = true; + } + else + { + this.parentNode.removeAttribute("disabled"); + noFolders = false; + } + + setupParent(folder, this.parentNode, noFolders); + return folder ? true : false; + ]]></body> + </method> + + <!-- + - Removes all menu-items for this popup, resets all fields, and + - removes the listener. This function is invoked when a change + - that affects this menu is detected by our listener. + --> + <method name="_teardown"> + <body><![CDATA[ + for (let i = this.childNodes.length - 1; i >= 0; i--) { + let child = this.childNodes[i]; + if (child.getAttribute("generated") != "true") + continue; + if ("_teardown" in child) + child._teardown(); + child.remove(); + } + + this._removeListener(); + + this._initialized = false; + ]]></body> + </method> + </implementation> + + <handlers> + <!-- + - In order to improve performance, we're not going to build any of the + - menu until we're shown (unless we're the child of a menulist, see + - note in the constructor). + - + - @note _ensureInitialized can be called repeatedly without issue, so + - don't worry about it here. + --> + <handler event="popupshowing" phase="capturing"> + this._ensureInitialized(); + </handler> + </handlers> + </binding> +</bindings> |