add ability to search in web from search view

It use OpenSearch to define the search engines.
https://bugzilla.gnome.org/show_bug.cgi?id=623708
This commit is contained in:
Maxim Ermilov
2011-01-18 00:38:47 +03:00
parent 883f51be93
commit b0c6cf3fc5
12 changed files with 517 additions and 30 deletions

View File

@ -1,6 +1,18 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
const RESULT_ICON_SIZE = 48;
@ -211,6 +223,108 @@ SearchProvider.prototype = {
};
Signals.addSignalMethods(SearchProvider.prototype);
function OpenSearchSystem(title) {
this._init(title);
}
OpenSearchSystem.prototype = {
_init: function() {
this._providers = [];
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
this._refresh();
},
getProviders: function() {
let res = [];
for (let i = 0; i < this._providers.length; i++)
res.push({ id: i, name: this._providers[i].name });
return res;
},
setSearchTerms: function(terms) {
this._terms = terms;
},
_checkSupportedProviderLanguage: function(provider) {
if (provider.url.search(/{language}/) == -1)
return true;
let langs = GLib.get_language_names();
langs.push('en');
let lang = null;
for (let i = 0; i < langs.length; i++) {
for (let k = 0; k < provider.langs.length; k++) {
if (langs[i] == provider.langs[k])
lang = langs[i];
}
if (lang)
break;
}
provider.lang = lang;
return lang != null;
},
activateResult: function(id) {
let searchTerms = this._terms.join(' ');
let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
if (url.match('{language}'))
url = url.replace('{language}', this._providers[id].lang);
try {
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
} catch (e) {
// TODO: remove this after glib will be removed from moduleset
// In the default jhbuild, gio is in our prefix but gvfs is not
let p = new Shell.Process({ 'args' : ['gvfs-open', url] });
p.run();
}
Main.overview.hide();
},
_addProvider: function(fileName) {
let file = Gio.file_new_for_path(global.datadir + '/search_providers/' + fileName);
let source = '';
file.load_contents_async(null, Lang.bind(this, function (obj, res) {
let [success, source] = file.load_contents_finish(res);
if (source) {
let [success, name, url, langs, icon_uri] = global.parse_search_provider(source);
let provider ={ name: name,
url: url,
id: this._providers.length,
icon_uri: icon_uri,
langs: langs };
if (this._checkSupportedProviderLanguage(provider)) {
this._providers.push(provider);
this.emit('changed');
}
}
}));
},
_refresh: function() {
this._providers = [];
let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
let file = Gio.file_new_for_path(global.datadir + '/search_providers');
FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
for (let i = 0; i < files.length; i++) {
let enabled = true;
let name = files[i].get_name();
for (let k = 0; k < names.length; k++)
if (names[k] == name)
enabled = false;
if (enabled)
this._addProvider(name);
}
}));
}
}
Signals.addSignalMethods(OpenSearchSystem.prototype);
function SearchSystem() {
this._init();
}

View File

@ -149,18 +149,18 @@ GridSearchResults.prototype = {
};
function SearchResults(searchSystem) {
this._init(searchSystem);
function SearchResults(searchSystem, openSearchSystem) {
this._init(searchSystem, openSearchSystem);
}
SearchResults.prototype = {
_init: function(searchSystem) {
_init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem;
this._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
this.actor = new St.Bin({ name: 'searchResults',
y_align: St.Align.START,
x_align: St.Align.START,
x_fill: true });
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
@ -170,7 +170,11 @@ SearchResults.prototype = {
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
scrollView.add_actor(this._content);
this.actor.set_child(scrollView);
this.actor.add(scrollView, { x_fill: true,
y_fill: false,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._content.add(this._statusText);
@ -179,6 +183,51 @@ SearchResults.prototype = {
this._providerMeta = [];
for (let i = 0; i < this._providers.length; i++)
this.createProviderMeta(this._providers[i]);
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
this._openSearchProviders = [];
this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
this._updateOpenSearchProviderButtons();
},
_updateOpenSearchProviderButtons: function() {
this._selectedOpenSearchButton = -1;
for (let i = 0; i < this._openSearchProviders.length; i++)
this._openSearchProviders[i].actor.destroy();
this._openSearchProviders = this._openSearchSystem.getProviders();
for (let i = 0; i < this._openSearchProviders.length; i++)
this._createOpenSearchProviderButton(this._openSearchProviders[i]);
},
_updateOpenSearchButtonState: function() {
for (let i = 0; i < this._openSearchProviders.length; i++) {
if (i == this._selectedOpenSearchButton)
this._openSearchProviders[i].actor.add_style_pseudo_class('selected');
else
this._openSearchProviders[i].actor.remove_style_pseudo_class('selected');
}
},
_createOpenSearchProviderButton: function(provider) {
let clickable = new St.Clickable({ style_class: 'dash-search-button',
reactive: true,
x_fill: true,
y_align: St.Align.MIDDLE });
let bin = new St.Bin({ x_fill: false,
x_align:St.Align.MIDDLE });
clickable.connect('clicked', Lang.bind(this, function() {
this._openSearchSystem.activateResult(provider.id);
}));
let title = new St.Label({ text: provider.name,
style_class: 'dash-search-button-label' });
bin.set_child(title);
clickable.set_child(bin);
provider.actor = clickable;
this._searchProvidersBox.add(clickable);
},
createProviderMeta: function(provider) {
@ -227,6 +276,8 @@ SearchResults.prototype = {
this._searchSystem.reset();
this._statusText.hide();
this._clearDisplay();
this._selectedOpenSearchButton = -1;
this._updateOpenSearchButtonState();
},
startingSearch: function() {
@ -247,12 +298,12 @@ SearchResults.prototype = {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
return true;
} else {
this._statusText.hide();
}
let terms = this._searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
@ -262,7 +313,8 @@ SearchResults.prototype = {
meta.count.set_text('' + providerResults.length);
}
this.selectDown(false);
if (this._selectedOpenSearchButton == -1)
this.selectDown(false);
return true;
},
@ -284,16 +336,26 @@ SearchResults.prototype = {
},
selectUp: function(recursing) {
for (let i = this._selectedProvider; i >= 0; i--) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, true);
if (success) {
this._selectedProvider = i;
return;
if (this._selectedOpenSearchButton == -1) {
for (let i = this._selectedProvider; i >= 0; i--) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, true);
if (success) {
this._selectedProvider = i;
return;
}
}
}
if (this._selectedOpenSearchButton == -1)
this._selectedOpenSearchButton = this._openSearchProviders.length;
this._selectedOpenSearchButton--;
this._updateOpenSearchButtonState();
if (this._selectedOpenSearchButton >= 0)
return;
if (this._providerMeta.length > 0 && !recursing) {
this._selectedProvider = this._providerMeta.length - 1;
this.selectUp(true);
@ -302,18 +364,30 @@ SearchResults.prototype = {
selectDown: function(recursing) {
let current = this._selectedProvider;
if (current == -1)
current = 0;
for (let i = current; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, false);
if (success) {
this._selectedProvider = i;
return;
if (this._selectedOpenSearchButton == -1) {
if (current == -1)
current = 0;
for (let i = current; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (!meta.actor.visible)
continue;
let success = this._modifyActorSelection(meta.resultDisplay, false);
if (success) {
this._selectedProvider = i;
return;
}
}
}
this._selectedOpenSearchButton++;
if (this._selectedOpenSearchButton < this._openSearchProviders.length) {
this._updateOpenSearchButtonState();
return;
}
this._selectedOpenSearchButton = -1;
this._updateOpenSearchButtonState();
if (this._providerMeta.length > 0 && !recursing) {
this._selectedProvider = 0;
this.selectDown(true);
@ -321,6 +395,13 @@ SearchResults.prototype = {
},
activateSelected: function() {
if (this._selectedOpenSearchButton != -1) {
let provider = this._openSearchProviders[this._selectedOpenSearchButton];
this._openSearchSystem.activateResult(provider.id);
Main.overview.hide();
return;
}
let current = this._selectedProvider;
if (current < 0)
return;

View File

@ -247,9 +247,10 @@ SearchTab.prototype = {
this._focusBase = focusBase;
this._searchSystem = new Search.SearchSystem();
this._openSearchSystem = new Search.OpenSearchSystem();
this._searchEntry = new SearchEntry(focusBase);
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem, this._openSearchSystem);
BaseTab.prototype._init.call(this,
this._searchEntry.actor,
this._searchResults.actor);