Files
binoc-central-mirror/mail/base/content/search.xml
T
2020-05-10 13:52:36 -04:00

277 lines
9.9 KiB
XML

<?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/. -->
<!DOCTYPE bindings [
<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd">
%messengerDTD;
]>
<bindings id="SearchBindings"
xmlns="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"
xmlns:xbl="http://www.mozilla.org/xbl">
<!--
- The glodaSearch binding implements a gloda-backed search mechanism. The
- actual search logic comes from the glodaFacet tab mode in the
- glodaFacetTabType definition. This binding serves as a means to display
- and alter the current search query if a "glodaFacet" tab is displayed,
- or enter a search query and spawn a new "glodaFacet" tab if one is
- currently not displayed.
-
- This widget used to have many weird implementation nuances. Now we are
- just a little bit of extra stuff on top of the toolkit autocomplete
- implementation. Our deviations are:
- - We collapse ourselves when gloda is disabled; we track the state.
- -
-->
<binding id="glodaSearch"
extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<handlers>
<handler event="drop" phase="capturing"><![CDATA[
nsDragAndDrop.drop(event, this.searchInputDNDObserver);
]]></handler>
<handler event="keypress" keycode="VK_RETURN"><![CDATA[
this.doSearch();
event.preventDefault();
event.stopPropagation();
]]></handler>
<handler event="keypress" keycode="VK_ESCAPE"><![CDATA[
this.clearSearch();
event.preventDefault();
event.stopPropagation();
]]></handler>
</handlers>
<implementation implements="nsIObserver">
<constructor><![CDATA[
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource:///modules/errUtils.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
try {
this.setAttribute(
"placeholder",
this.getAttribute("emptytextbase")
.replace("#1", this.getAttribute(
(AppConstants.platform == "macosx") ?
"keyLabelMac" : "keyLabelNonMac")));
Services.prefs.addObserver("mailnews.database.global.indexer.enabled",
this._prefObserver, false);
this.glodaCompleter =
Components.classes["@mozilla.org/autocomplete/search;1?name=gloda"]
.getService()
.wrappedJSObject;
Services.obs.addObserver(this, "autocomplete-did-enter-text", false);
this.glodaEnabled =
Services.prefs.getBoolPref("mailnews.database.global.indexer.enabled");
this.collapsed = !this.glodaEnabled;
// make sure we set our emptytext here from the get-go
if (this.hasAttribute("placeholder"))
this.placeholder = this.getAttribute("placeholder");
} catch (e) {
logException(e, true);
}
]]></constructor>
<destructor>
<![CDATA[
Components.utils.import("resource://gre/modules/Services.jsm");
Services.prefs.removeObserver("mailnews.database.global.indexer.enabled",
this._prefObserver);
Services.obs.removeObserver(this, "autocomplete-did-enter-text");
]]>
</destructor>
<field name="_prefObserver">({
inputSearch: this,
observe: function(subject, topic, data)
{
Components.utils.import("resource://gre/modules/Services.jsm");
if (topic == "nsPref:changed") {
subject.QueryInterface(Components.interfaces.nsIPrefBranch);
switch (data) {
case "mailnews.database.global.indexer.enabled":
this.inputSearch.glodaEnabled =
Services.prefs.getBoolPref(
"mailnews.database.global.indexer.enabled");
this.inputSearch.collapsed = !this.inputSearch.glodaEnabled;
break;
}
}
},
QueryInterface: function(aIID)
{
if (aIID.equals(Components.interfaces.nsIObserver) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
}
});
</field>
<field name="glodaCompleter">null</field>
<property name="menupopup" readonly="true">
<getter><![CDATA[
return document.getAnonymousElementByAttribute(
this, 'anonid', 'quick-search-menupopup');
]]></getter>
</property>
<property name="state">
<getter><![CDATA[
return {
'string': this.value
};
]]></getter>
<setter><![CDATA[
this.value = val['string'];
]]></setter>
</property>
// DND Observer
<field name="searchInputDNDObserver" readonly="true"><![CDATA[
({
inputSearch: this,
onDrop: function (aEvent, aXferData, aDragSession) {
try {
if (aXferData.data) {
this.inputSearch.focus();
this.inputSearch.value = aXferData.data;
// XXX for some reason the input field is _cleared_ even though
// the search works.
this.inputSearch.doSearch();
}
} catch (e) {
logException(e);
}
},
getSupportedFlavours: function () {
var flavourSet = new FlavourSet();
flavourSet.appendFlavour("text/unicode");
return flavourSet;
}
})
]]></field>
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body><![CDATA[
try {
if (aTopic == "autocomplete-did-enter-text" && aSubject == this) {
let selectedIndex = this.popup.selectedIndex;
let curResult = this.glodaCompleter.curResult;
if (! curResult)
return; // autocomplete didn't even finish.
let row = curResult.getObjectAt(selectedIndex);
if (row == null)
return;
if (row.fullText) {
// The autocomplete-did-enter-text notification is synchronously
// generated by nsAutoCompleteController which will attempt to
// call ClosePopup after we return and then tell the searchbox
// about the text entered. Since doSearch may close the current
// tab (and thus destroy the XUL document that owns the popup and
// the input field), the search box may no longer have its XBL
// binding attached when we return and telling it about the
// entered text could fail.
// To avoid this, we defer the doSearch call to the next turn of
// the event loop by using setTimeout.
setTimeout(this.doSearch.bind(this), 0);
} else if (row.nounDef) {
let theQuery = Gloda.newQuery(Gloda.NOUN_MESSAGE);
if (row.nounDef.name == "tag") {
theQuery = theQuery.tags(row.item);
} else if (row.nounDef.name == "identity") {
theQuery = theQuery.involves(row.item);
}
theQuery.orderBy('-date');
document.getElementById("tabmail").openTab("glodaFacet", {
query: theQuery
});
}
}
} catch (e) {
logException(e);
}
]]></body>
</method>
<method name="doSearch">
<body><![CDATA[
Components.utils.import("resource://gre/modules/Services.jsm");
try {
if (this.value) {
let tabmail = document.getElementById("tabmail");
// If the current tab is a gloda search tab, reset the value
// to the initial search value. Otherwise, clear it. This
// is the value that 3is going to be saved with the current
// tab when we switch back to it next.
let searchString = this.value;
if (tabmail.currentTabInfo.mode.name == "glodaFacet") {
// we'd rather reuse the existing tab (and somehow do something
// smart with any preexisting facet choices, but that's a
// bit hard right now, so doing the cheap thing and closing
// this tab and starting over
tabmail.closeTab();
}
this.value = ''; // clear our value, to avoid persistence
let args = {
searcher: new GlodaMsgSearcher(null, searchString)
};
tabmail.openTab("glodaFacet", args);
}
} catch (e) {
logException(e);
}
]]>
</body>
</method>
<method name="clearSearch">
<body><![CDATA[
this.value = "";
]]></body>
</method>
</implementation>
</binding>
<binding id="searchBarDropMarker">
<resources>
<stylesheet src="chrome://messenger/skin/searchBox.css"/>
</resources>
<content popup="_child">
<children/>
<xul:stack flex="1">
<xul:hbox align="center">
<xul:image class="quick-search-button-image" xbl:inherits="src"/>
</xul:hbox>
<xul:hbox align="center">
<xul:image class="quick-search-button-dropmarker"/>
</xul:hbox>
</xul:stack>
</content>
</binding>
</bindings>