Bug 1884181 - Move EngineStore/EngineView functions out of gSearchPane and into the relevant class. r=mconley
Differential Revision: https://phabricator.services.mozilla.com/D204022
This commit is contained in:
@@ -38,11 +38,13 @@ const SEARCH_KEY = "defaultSearch";
|
||||
var gEngineView = null;
|
||||
|
||||
var gSearchPane = {
|
||||
init() {
|
||||
let engineStore = new EngineStore();
|
||||
gEngineView = new EngineView(engineStore);
|
||||
_engineStore: null,
|
||||
|
||||
engineStore.init().catch(console.error);
|
||||
init() {
|
||||
this._engineStore = new EngineStore();
|
||||
gEngineView = new EngineView(this._engineStore);
|
||||
|
||||
this._engineStore.init().catch(console.error);
|
||||
|
||||
if (
|
||||
Services.policies &&
|
||||
@@ -54,12 +56,7 @@ var gSearchPane = {
|
||||
addEnginesLink.setAttribute("href", lazy.SearchUIUtils.searchEnginesURL);
|
||||
}
|
||||
|
||||
window.addEventListener("click", this);
|
||||
window.addEventListener("command", this);
|
||||
window.addEventListener("dragstart", this);
|
||||
window.addEventListener("keypress", this);
|
||||
window.addEventListener("select", this);
|
||||
window.addEventListener("dblclick", this);
|
||||
|
||||
Services.obs.addObserver(this, "browser-search-engine-modified");
|
||||
Services.obs.addObserver(this, "intl:app-locales-changed");
|
||||
@@ -114,7 +111,6 @@ var gSearchPane = {
|
||||
this._initDefaultEngines();
|
||||
this._initShowSearchTermsCheckbox();
|
||||
this._updateSuggestionCheckboxes();
|
||||
this._showAddEngineButton();
|
||||
this._initRecentSeachesCheckbox();
|
||||
this._initAddressBar();
|
||||
},
|
||||
@@ -255,17 +251,6 @@ var gSearchPane = {
|
||||
this._updateTrendingCheckbox(!suggestsPref.value || permanentPB);
|
||||
},
|
||||
|
||||
_showAddEngineButton() {
|
||||
let aliasRefresh = Services.prefs.getBoolPref(
|
||||
"browser.urlbar.update2.engineAliasRefresh",
|
||||
false
|
||||
);
|
||||
if (aliasRefresh) {
|
||||
let addButton = document.getElementById("addEngineButton");
|
||||
addButton.hidden = false;
|
||||
}
|
||||
},
|
||||
|
||||
_initRecentSeachesCheckbox() {
|
||||
this._recentSearchesEnabledPref = Preferences.get(
|
||||
"browser.urlbar.recentsearches.featureGate"
|
||||
@@ -497,7 +482,7 @@ var gSearchPane = {
|
||||
*/
|
||||
async _buildEngineDropDown(list, currentEngine) {
|
||||
// If the current engine isn't in the list any more, select the first item.
|
||||
let engines = gEngineView._engineStore._engines;
|
||||
let engines = this._engineStore._engines;
|
||||
if (!engines.length) {
|
||||
return;
|
||||
}
|
||||
@@ -507,7 +492,7 @@ var gSearchPane = {
|
||||
|
||||
// Now clean-up and rebuild the list.
|
||||
list.removeAllItems();
|
||||
gEngineView._engineStore._engines.forEach(e => {
|
||||
this._engineStore._engines.forEach(e => {
|
||||
let item = list.appendItem(e.name);
|
||||
item.setAttribute(
|
||||
"class",
|
||||
@@ -524,86 +509,23 @@ var gSearchPane = {
|
||||
},
|
||||
|
||||
handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "dblclick":
|
||||
if (aEvent.target.id == "engineChildren") {
|
||||
let cell = aEvent.target.parentNode.getCellAt(
|
||||
aEvent.clientX,
|
||||
aEvent.clientY
|
||||
);
|
||||
if (cell.col?.id == "engineKeyword") {
|
||||
this.startEditingAlias(gEngineView.selectedIndex);
|
||||
if (aEvent.type != "command") {
|
||||
return;
|
||||
}
|
||||
switch (aEvent.target.id) {
|
||||
case "":
|
||||
if (aEvent.target.parentNode && aEvent.target.parentNode.parentNode) {
|
||||
if (aEvent.target.parentNode.parentNode.id == "defaultEngine") {
|
||||
gSearchPane.setDefaultEngine();
|
||||
} else if (
|
||||
aEvent.target.parentNode.parentNode.id == "defaultPrivateEngine"
|
||||
) {
|
||||
gSearchPane.setDefaultPrivateEngine();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "click":
|
||||
if (
|
||||
aEvent.target.id != "engineChildren" &&
|
||||
!aEvent.target.classList.contains("searchEngineAction")
|
||||
) {
|
||||
let engineList = document.getElementById("engineList");
|
||||
// We don't want to toggle off selection while editing keyword
|
||||
// so proceed only when the input field is hidden.
|
||||
// We need to check that engineList.view is defined here
|
||||
// because the "click" event listener is on <window> and the
|
||||
// view might have been destroyed if the pane has been navigated
|
||||
// away from.
|
||||
if (engineList.inputField.hidden && engineList.view) {
|
||||
let selection = engineList.view.selection;
|
||||
if (selection?.count > 0) {
|
||||
selection.toggleSelect(selection.currentIndex);
|
||||
}
|
||||
engineList.blur();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "command":
|
||||
switch (aEvent.target.id) {
|
||||
case "":
|
||||
if (
|
||||
aEvent.target.parentNode &&
|
||||
aEvent.target.parentNode.parentNode
|
||||
) {
|
||||
if (aEvent.target.parentNode.parentNode.id == "defaultEngine") {
|
||||
gSearchPane.setDefaultEngine();
|
||||
} else if (
|
||||
aEvent.target.parentNode.parentNode.id == "defaultPrivateEngine"
|
||||
) {
|
||||
gSearchPane.setDefaultPrivateEngine();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "restoreDefaultSearchEngines":
|
||||
gSearchPane.onRestoreDefaults();
|
||||
break;
|
||||
case "removeEngineButton":
|
||||
Services.search.removeEngine(
|
||||
gEngineView.selectedEngine.originalEngine
|
||||
);
|
||||
break;
|
||||
case "addEngineButton":
|
||||
gSubDialog.open(
|
||||
"chrome://browser/content/preferences/dialogs/addEngine.xhtml",
|
||||
{ features: "resizable=no, modal=yes" }
|
||||
);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "dragstart":
|
||||
if (aEvent.target.id == "engineChildren") {
|
||||
onDragEngineStart(aEvent);
|
||||
}
|
||||
break;
|
||||
case "keypress":
|
||||
if (aEvent.target.id == "engineList") {
|
||||
gSearchPane.onTreeKeyPress(aEvent);
|
||||
}
|
||||
break;
|
||||
case "select":
|
||||
if (aEvent.target.id == "engineList") {
|
||||
gSearchPane.onTreeSelect();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gEngineView.handleEvent(aEvent);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -615,60 +537,6 @@ var gSearchPane = {
|
||||
await gEngineView.loadL10nNames();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the default engine UI and engine tree view as appropriate when engine changes
|
||||
* or locale changes occur.
|
||||
*
|
||||
* @param {Object} engine
|
||||
* @param {string} data
|
||||
*/
|
||||
browserSearchEngineModified(engine, data) {
|
||||
engine.QueryInterface(Ci.nsISearchEngine);
|
||||
switch (data) {
|
||||
case "engine-added":
|
||||
gEngineView._engineStore.addEngine(engine);
|
||||
gEngineView.rowCountChanged(gEngineView.lastEngineIndex, 1);
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
break;
|
||||
case "engine-changed":
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
gEngineView._engineStore.updateEngine(engine);
|
||||
gEngineView.invalidate();
|
||||
break;
|
||||
case "engine-removed":
|
||||
gSearchPane.remove(engine);
|
||||
break;
|
||||
case "engine-default": {
|
||||
// If the user is going through the drop down using up/down keys, the
|
||||
// dropdown may still be open (eg. on Windows) when engine-default is
|
||||
// fired, so rebuilding the list unconditionally would get in the way.
|
||||
let selectedEngine =
|
||||
document.getElementById("defaultEngine").selectedItem.engine;
|
||||
if (selectedEngine.name != engine.name) {
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
}
|
||||
gSearchPane._updateSuggestionCheckboxes();
|
||||
break;
|
||||
}
|
||||
case "engine-default-private": {
|
||||
if (
|
||||
this._separatePrivateDefaultEnabledPref.value &&
|
||||
this._separatePrivateDefaultPref.value
|
||||
) {
|
||||
// If the user is going through the drop down using up/down keys, the
|
||||
// dropdown may still be open (eg. on Windows) when engine-default is
|
||||
// fired, so rebuilding the list unconditionally would get in the way.
|
||||
const selectedEngine = document.getElementById("defaultPrivateEngine")
|
||||
.selectedItem.engine;
|
||||
if (selectedEngine.name != engine.name) {
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* nsIObserver implementation.
|
||||
*/
|
||||
@@ -679,144 +547,48 @@ var gSearchPane = {
|
||||
break;
|
||||
}
|
||||
case "browser-search-engine-modified": {
|
||||
this.browserSearchEngineModified(subject, data);
|
||||
break;
|
||||
let engine = subject.QueryInterface(Ci.nsISearchEngine);
|
||||
switch (data) {
|
||||
case "engine-default": {
|
||||
// If the user is going through the drop down using up/down keys, the
|
||||
// dropdown may still be open (eg. on Windows) when engine-default is
|
||||
// fired, so rebuilding the list unconditionally would get in the way.
|
||||
let selectedEngine =
|
||||
document.getElementById("defaultEngine").selectedItem.engine;
|
||||
if (selectedEngine.name != engine.name) {
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
}
|
||||
gSearchPane._updateSuggestionCheckboxes();
|
||||
break;
|
||||
}
|
||||
case "engine-default-private": {
|
||||
if (
|
||||
this._separatePrivateDefaultEnabledPref.value &&
|
||||
this._separatePrivateDefaultPref.value
|
||||
) {
|
||||
// If the user is going through the drop down using up/down keys, the
|
||||
// dropdown may still be open (eg. on Windows) when engine-default is
|
||||
// fired, so rebuilding the list unconditionally would get in the way.
|
||||
const selectedEngine = document.getElementById(
|
||||
"defaultPrivateEngine"
|
||||
).selectedItem.engine;
|
||||
if (selectedEngine.name != engine.name) {
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this._engineStore.browserSearchEngineModified(subject, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onTreeSelect() {
|
||||
document.getElementById("removeEngineButton").disabled =
|
||||
!gEngineView.isEngineSelectedAndRemovable();
|
||||
},
|
||||
|
||||
onTreeKeyPress(aEvent) {
|
||||
let index = gEngineView.selectedIndex;
|
||||
let tree = document.getElementById("engineList");
|
||||
if (tree.hasAttribute("editing")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
|
||||
// Space toggles the checkbox.
|
||||
let newValue = !gEngineView.getCellValue(
|
||||
index,
|
||||
tree.columns.getNamedColumn("engineShown")
|
||||
);
|
||||
gEngineView.setCellValue(
|
||||
index,
|
||||
tree.columns.getFirstColumn(),
|
||||
newValue.toString()
|
||||
);
|
||||
// Prevent page from scrolling on the space key.
|
||||
aEvent.preventDefault();
|
||||
} else {
|
||||
let isMac = Services.appinfo.OS == "Darwin";
|
||||
if (
|
||||
(isMac && aEvent.keyCode == KeyEvent.DOM_VK_RETURN) ||
|
||||
(!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2)
|
||||
) {
|
||||
this.startEditingAlias(index);
|
||||
} else if (
|
||||
aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
|
||||
(isMac &&
|
||||
aEvent.shiftKey &&
|
||||
aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE &&
|
||||
gEngineView.isEngineSelectedAndRemovable())
|
||||
) {
|
||||
// Delete and Shift+Backspace (Mac) removes selected engine.
|
||||
Services.search.removeEngine(gEngineView.selectedEngine.originalEngine);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
startEditingAlias(index) {
|
||||
// Local shortcut aliases can't be edited.
|
||||
if (gEngineView._getLocalShortcut(index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tree = document.getElementById("engineList");
|
||||
let engine = gEngineView._engineStore.engines[index];
|
||||
tree.startEditing(index, tree.columns.getLastColumn());
|
||||
tree.inputField.value = engine.alias || "";
|
||||
tree.inputField.select();
|
||||
},
|
||||
|
||||
async onRestoreDefaults() {
|
||||
let num = await gEngineView._engineStore.restoreDefaultEngines();
|
||||
gEngineView.rowCountChanged(0, num);
|
||||
gEngineView.invalidate();
|
||||
},
|
||||
|
||||
showRestoreDefaults(aEnable) {
|
||||
document.getElementById("restoreDefaultSearchEngines").disabled = !aEnable;
|
||||
},
|
||||
|
||||
remove(aEngine) {
|
||||
let index = gEngineView._engineStore.removeEngine(aEngine);
|
||||
if (!gEngineView.tree) {
|
||||
// Only update the selection if it's visible in the UI.
|
||||
return;
|
||||
}
|
||||
|
||||
gEngineView.rowCountChanged(index, -1);
|
||||
gEngineView.invalidate();
|
||||
|
||||
gEngineView.selection.select(Math.min(index, gEngineView.rowCount - 1));
|
||||
gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
|
||||
|
||||
document.getElementById("engineList").focus();
|
||||
},
|
||||
|
||||
async editKeyword(aEngine, aNewKeyword) {
|
||||
let keyword = aNewKeyword.trim();
|
||||
if (keyword) {
|
||||
let eduplicate = false;
|
||||
let dupName = "";
|
||||
|
||||
// Check for duplicates in Places keywords.
|
||||
let bduplicate = !!(await PlacesUtils.keywords.fetch(keyword));
|
||||
|
||||
// Check for duplicates in changes we haven't committed yet
|
||||
let engines = gEngineView._engineStore.engines;
|
||||
let lc_keyword = keyword.toLocaleLowerCase();
|
||||
for (let engine of engines) {
|
||||
if (
|
||||
engine.alias &&
|
||||
engine.alias.toLocaleLowerCase() == lc_keyword &&
|
||||
engine.name != aEngine.name
|
||||
) {
|
||||
eduplicate = true;
|
||||
dupName = engine.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the user if they have chosen an existing engine/bookmark keyword
|
||||
if (eduplicate || bduplicate) {
|
||||
let msgids = [{ id: "search-keyword-warning-title" }];
|
||||
if (eduplicate) {
|
||||
msgids.push({
|
||||
id: "search-keyword-warning-engine",
|
||||
args: { name: dupName },
|
||||
});
|
||||
} else {
|
||||
msgids.push({ id: "search-keyword-warning-bookmark" });
|
||||
}
|
||||
|
||||
let [dtitle, msg] = await document.l10n.formatValues(msgids);
|
||||
|
||||
Services.prompt.alert(window, dtitle, msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gEngineView._engineStore.changeEngine(aEngine, "alias", keyword);
|
||||
gEngineView.invalidate();
|
||||
return true;
|
||||
},
|
||||
|
||||
async setDefaultEngine() {
|
||||
await Services.search.setDefault(
|
||||
document.getElementById("defaultEngine").selectedItem.engine,
|
||||
@@ -839,23 +611,6 @@ var gSearchPane = {
|
||||
},
|
||||
};
|
||||
|
||||
function onDragEngineStart(event) {
|
||||
var selectedIndex = gEngineView.selectedIndex;
|
||||
|
||||
// Local shortcut rows can't be dragged or re-ordered.
|
||||
if (gEngineView._getLocalShortcut(selectedIndex)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var tree = document.getElementById("engineList");
|
||||
let cell = tree.getCellAt(event.clientX, event.clientY);
|
||||
if (selectedIndex >= 0 && !gEngineView.isCheckBox(cell.row, cell.col)) {
|
||||
event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
}
|
||||
}
|
||||
|
||||
class EngineStore {
|
||||
_engines = [];
|
||||
/**
|
||||
@@ -964,8 +719,47 @@ class EngineStore {
|
||||
if (aEngine.isAppProvided) {
|
||||
gSearchPane.showRestoreDefaults(true);
|
||||
}
|
||||
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
return index;
|
||||
|
||||
if (!gEngineView.tree) {
|
||||
// Only update the selection if it's visible in the UI.
|
||||
return;
|
||||
}
|
||||
|
||||
gEngineView.rowCountChanged(index, -1);
|
||||
gEngineView.invalidate();
|
||||
|
||||
gEngineView.selection.select(Math.min(index, gEngineView.rowCount - 1));
|
||||
gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
|
||||
|
||||
document.getElementById("engineList").focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the default engine UI and engine tree view as appropriate when engine changes
|
||||
* or locale changes occur.
|
||||
*
|
||||
* @param {Object} engine
|
||||
* @param {string} data
|
||||
*/
|
||||
browserSearchEngineModified(engine, data) {
|
||||
engine.QueryInterface(Ci.nsISearchEngine);
|
||||
switch (data) {
|
||||
case "engine-added":
|
||||
this.addEngine(engine);
|
||||
gEngineView.rowCountChanged(gEngineView.lastEngineIndex, 1);
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
break;
|
||||
case "engine-changed":
|
||||
gSearchPane.buildDefaultEngineDropDowns();
|
||||
this.updateEngine(engine);
|
||||
gEngineView.invalidate();
|
||||
break;
|
||||
case "engine-removed":
|
||||
this.removeEngine(engine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async restoreDefaultEngines() {
|
||||
@@ -1025,18 +819,22 @@ class EngineStore {
|
||||
}
|
||||
|
||||
class EngineView {
|
||||
_engineStore = null;
|
||||
_engineList = null;
|
||||
tree = null;
|
||||
|
||||
constructor(aEngineStore) {
|
||||
this._engineStore = aEngineStore;
|
||||
document.getElementById("engineList").view = this;
|
||||
this._engineList = document.getElementById("engineList");
|
||||
this._engineList.view = this;
|
||||
|
||||
UrlbarPrefs.addObserver(this);
|
||||
|
||||
this.loadL10nNames();
|
||||
this.#addListeners();
|
||||
this.#showAddEngineButton();
|
||||
}
|
||||
|
||||
_engineStore = null;
|
||||
tree = null;
|
||||
|
||||
loadL10nNames() {
|
||||
// This maps local shortcut sources to their l10n names. The names are needed
|
||||
// by getCellText. Getting the names is async but getCellText is not, so we
|
||||
@@ -1059,6 +857,28 @@ class EngineView {
|
||||
});
|
||||
}
|
||||
|
||||
#addListeners() {
|
||||
this._engineList.addEventListener("click", this);
|
||||
this._engineList.addEventListener("dragstart", this);
|
||||
this._engineList.addEventListener("keypress", this);
|
||||
this._engineList.addEventListener("select", this);
|
||||
this._engineList.addEventListener("dblclick", this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the "Add Search Engine" button if the pref is enabled.
|
||||
*/
|
||||
#showAddEngineButton() {
|
||||
let aliasRefresh = Services.prefs.getBoolPref(
|
||||
"browser.urlbar.update2.engineAliasRefresh",
|
||||
false
|
||||
);
|
||||
if (aliasRefresh) {
|
||||
let addButton = document.getElementById("addEngineButton");
|
||||
addButton.hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
get lastEngineIndex() {
|
||||
return this._engineStore.engines.length - 1;
|
||||
}
|
||||
@@ -1147,6 +967,163 @@ class EngineView {
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "dblclick":
|
||||
if (aEvent.target.id == "engineChildren") {
|
||||
let cell = aEvent.target.parentNode.getCellAt(
|
||||
aEvent.clientX,
|
||||
aEvent.clientY
|
||||
);
|
||||
if (cell.col?.id == "engineKeyword") {
|
||||
this.#startEditingAlias(this.selectedIndex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "click":
|
||||
if (
|
||||
aEvent.target.id != "engineChildren" &&
|
||||
!aEvent.target.classList.contains("searchEngineAction")
|
||||
) {
|
||||
// We don't want to toggle off selection while editing keyword
|
||||
// so proceed only when the input field is hidden.
|
||||
// We need to check that engineList.view is defined here
|
||||
// because the "click" event listener is on <window> and the
|
||||
// view might have been destroyed if the pane has been navigated
|
||||
// away from.
|
||||
if (this._engineList.inputField.hidden && this._engineList.view) {
|
||||
let selection = this._engineList.view.selection;
|
||||
if (selection?.count > 0) {
|
||||
selection.toggleSelect(selection.currentIndex);
|
||||
}
|
||||
this._engineList.blur();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "command":
|
||||
switch (aEvent.target.id) {
|
||||
case "restoreDefaultSearchEngines":
|
||||
this.#onRestoreDefaults();
|
||||
break;
|
||||
case "removeEngineButton":
|
||||
Services.search.removeEngine(this.selectedEngine.originalEngine);
|
||||
break;
|
||||
case "addEngineButton":
|
||||
gSubDialog.open(
|
||||
"chrome://browser/content/preferences/dialogs/addEngine.xhtml",
|
||||
{ features: "resizable=no, modal=yes" }
|
||||
);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "dragstart":
|
||||
if (aEvent.target.id == "engineChildren") {
|
||||
this.#onDragEngineStart(aEvent);
|
||||
}
|
||||
break;
|
||||
case "keypress":
|
||||
if (aEvent.target.id == "engineList") {
|
||||
this.#onTreeKeyPress(aEvent);
|
||||
}
|
||||
break;
|
||||
case "select":
|
||||
if (aEvent.target.id == "engineList") {
|
||||
this.#onTreeSelect();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the restore default engines button is clicked to reset the
|
||||
* list of engines to their defaults.
|
||||
*/
|
||||
async #onRestoreDefaults() {
|
||||
let num = await this._engineStore.restoreDefaultEngines();
|
||||
this.rowCountChanged(0, num);
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
#onDragEngineStart(event) {
|
||||
let selectedIndex = this.selectedIndex;
|
||||
|
||||
// Local shortcut rows can't be dragged or re-ordered.
|
||||
if (this._getLocalShortcut(selectedIndex)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
let tree = document.getElementById("engineList");
|
||||
let cell = tree.getCellAt(event.clientX, event.clientY);
|
||||
if (selectedIndex >= 0 && !this.isCheckBox(cell.row, cell.col)) {
|
||||
event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
}
|
||||
}
|
||||
|
||||
#onTreeSelect() {
|
||||
document.getElementById("removeEngineButton").disabled =
|
||||
!this.isEngineSelectedAndRemovable();
|
||||
}
|
||||
|
||||
#onTreeKeyPress(aEvent) {
|
||||
let index = this.selectedIndex;
|
||||
let tree = document.getElementById("engineList");
|
||||
if (tree.hasAttribute("editing")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
|
||||
// Space toggles the checkbox.
|
||||
let newValue = !this.getCellValue(
|
||||
index,
|
||||
tree.columns.getNamedColumn("engineShown")
|
||||
);
|
||||
this.setCellValue(
|
||||
index,
|
||||
tree.columns.getFirstColumn(),
|
||||
newValue.toString()
|
||||
);
|
||||
// Prevent page from scrolling on the space key.
|
||||
aEvent.preventDefault();
|
||||
} else {
|
||||
let isMac = Services.appinfo.OS == "Darwin";
|
||||
if (
|
||||
(isMac && aEvent.keyCode == KeyEvent.DOM_VK_RETURN) ||
|
||||
(!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2)
|
||||
) {
|
||||
this.#startEditingAlias(index);
|
||||
} else if (
|
||||
aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
|
||||
(isMac &&
|
||||
aEvent.shiftKey &&
|
||||
aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE &&
|
||||
this.isEngineSelectedAndRemovable())
|
||||
) {
|
||||
// Delete and Shift+Backspace (Mac) removes selected engine.
|
||||
Services.search.removeEngine(this.selectedEngine.originalEngine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers editing of an alias in the tree.
|
||||
*
|
||||
* @param {number} index
|
||||
*/
|
||||
#startEditingAlias(index) {
|
||||
// Local shortcut aliases can't be edited.
|
||||
if (this._getLocalShortcut(index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tree = document.getElementById("engineList");
|
||||
let engine = this._engineStore.engines[index];
|
||||
tree.startEditing(index, tree.columns.getLastColumn());
|
||||
tree.inputField.value = engine.alias || "";
|
||||
tree.inputField.select();
|
||||
}
|
||||
|
||||
// nsITreeView
|
||||
get rowCount() {
|
||||
return (
|
||||
@@ -1306,18 +1283,77 @@ class EngineView {
|
||||
}
|
||||
this._engineStore.engines[index].originalEngine.hideOneOffButton =
|
||||
value != "true";
|
||||
gEngineView.invalidate();
|
||||
this.invalidate();
|
||||
}
|
||||
}
|
||||
setCellText(index, column, value) {
|
||||
if (column.id == "engineKeyword") {
|
||||
gSearchPane
|
||||
.editKeyword(this._engineStore.engines[index], value)
|
||||
.then(valid => {
|
||||
this.#changeKeyword(this._engineStore.engines[index], value).then(
|
||||
valid => {
|
||||
if (!valid) {
|
||||
gSearchPane.startEditingAlias(index);
|
||||
this.#startEditingAlias(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changing the keyword for an engine. This will check for potentially
|
||||
* duplicate keywords and prompt the user if necessary.
|
||||
*
|
||||
* @param {object} aEngine
|
||||
* The engine to change.
|
||||
* @param {string} aNewKeyword
|
||||
* The new keyword.
|
||||
* @returns {Promise<boolean>}
|
||||
* Resolves to true if the keyword was changed.
|
||||
*/
|
||||
async #changeKeyword(aEngine, aNewKeyword) {
|
||||
let keyword = aNewKeyword.trim();
|
||||
if (keyword) {
|
||||
let eduplicate = false;
|
||||
let dupName = "";
|
||||
|
||||
// Check for duplicates in Places keywords.
|
||||
let bduplicate = !!(await PlacesUtils.keywords.fetch(keyword));
|
||||
|
||||
// Check for duplicates in changes we haven't committed yet
|
||||
let engines = this._engineStore.engines;
|
||||
let lc_keyword = keyword.toLocaleLowerCase();
|
||||
for (let engine of engines) {
|
||||
if (
|
||||
engine.alias &&
|
||||
engine.alias.toLocaleLowerCase() == lc_keyword &&
|
||||
engine.name != aEngine.name
|
||||
) {
|
||||
eduplicate = true;
|
||||
dupName = engine.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the user if they have chosen an existing engine/bookmark keyword
|
||||
if (eduplicate || bduplicate) {
|
||||
let msgids = [{ id: "search-keyword-warning-title" }];
|
||||
if (eduplicate) {
|
||||
msgids.push({
|
||||
id: "search-keyword-warning-engine",
|
||||
args: { name: dupName },
|
||||
});
|
||||
} else {
|
||||
msgids.push({ id: "search-keyword-warning-bookmark" });
|
||||
}
|
||||
|
||||
let [dtitle, msg] = await document.l10n.formatValues(msgids);
|
||||
|
||||
Services.prompt.alert(window, dtitle, msg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this._engineStore.changeEngine(aEngine, "alias", keyword);
|
||||
this.invalidate();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user