2011-11-16 18:11:05 -05:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
|
2013-01-02 20:48:22 -05:00
|
|
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
2011-11-16 18:11:05 -05:00
|
|
|
const Gio = imports.gi.Gio;
|
2011-12-15 08:46:12 -05:00
|
|
|
const GLib = imports.gi.GLib;
|
2011-11-16 18:11:05 -05:00
|
|
|
const Lang = imports.lang;
|
|
|
|
const St = imports.gi.St;
|
2013-01-02 09:31:50 -05:00
|
|
|
const Shell = imports.gi.Shell;
|
2011-11-16 18:11:05 -05:00
|
|
|
|
2013-11-04 10:07:44 -05:00
|
|
|
const FileUtils = imports.misc.fileUtils;
|
2011-11-16 18:11:05 -05:00
|
|
|
const Search = imports.ui.search;
|
|
|
|
|
2011-12-15 08:46:12 -05:00
|
|
|
const KEY_FILE_GROUP = 'Shell Search Provider';
|
|
|
|
|
2013-10-24 17:51:58 -04:00
|
|
|
const SearchProviderIface = '<node> \
|
|
|
|
<interface name="org.gnome.Shell.SearchProvider"> \
|
|
|
|
<method name="GetInitialResultSet"> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="as" direction="out" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="GetSubsearchResultSet"> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="as" direction="out" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="GetResultMetas"> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="aa{sv}" direction="out" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="ActivateResult"> \
|
|
|
|
<arg type="s" direction="in" /> \
|
|
|
|
</method> \
|
|
|
|
</interface> \
|
|
|
|
</node>';
|
2011-11-16 18:11:05 -05:00
|
|
|
|
2013-10-24 17:51:58 -04:00
|
|
|
const SearchProvider2Iface = '<node> \
|
|
|
|
<interface name="org.gnome.Shell.SearchProvider2"> \
|
|
|
|
<method name="GetInitialResultSet"> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="as" direction="out" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="GetSubsearchResultSet"> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="as" direction="out" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="GetResultMetas"> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="aa{sv}" direction="out" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="ActivateResult"> \
|
|
|
|
<arg type="s" direction="in" /> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="u" direction="in" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="LaunchSearch"> \
|
|
|
|
<arg type="as" direction="in" /> \
|
|
|
|
<arg type="u" direction="in" /> \
|
|
|
|
</method> \
|
|
|
|
</interface> \
|
|
|
|
</node>';
|
2012-12-05 17:20:22 -05:00
|
|
|
|
2013-09-26 07:22:22 -04:00
|
|
|
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
|
|
|
|
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
|
2011-11-16 18:11:05 -05:00
|
|
|
|
2013-11-02 18:03:52 -04:00
|
|
|
function loadRemoteSearchProviders(callback) {
|
2013-07-26 18:36:31 -04:00
|
|
|
let objectPaths = {};
|
|
|
|
let loadedProviders = [];
|
2012-06-22 11:44:15 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
function loadRemoteSearchProvider(file) {
|
|
|
|
let keyfile = new GLib.KeyFile();
|
|
|
|
let path = file.get_path();
|
2012-11-30 10:36:12 -05:00
|
|
|
|
|
|
|
try {
|
2013-07-26 18:36:31 -04:00
|
|
|
keyfile.load_from_file(path, 0);
|
|
|
|
} catch(e) {
|
2012-11-30 10:36:12 -05:00
|
|
|
return;
|
2011-12-15 08:46:12 -05:00
|
|
|
}
|
2012-11-01 15:55:41 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
if (!keyfile.has_group(KEY_FILE_GROUP))
|
|
|
|
return;
|
|
|
|
|
|
|
|
let remoteProvider;
|
2012-12-05 17:20:22 -05:00
|
|
|
try {
|
2013-07-26 18:36:31 -04:00
|
|
|
let group = KEY_FILE_GROUP;
|
|
|
|
let busName = keyfile.get_string(group, 'BusName');
|
|
|
|
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
|
|
|
|
|
|
|
if (objectPaths[objectPath])
|
|
|
|
return;
|
|
|
|
|
|
|
|
let appInfo = null;
|
|
|
|
try {
|
|
|
|
let desktopId = keyfile.get_string(group, 'DesktopId');
|
|
|
|
appInfo = Gio.DesktopAppInfo.new(desktopId);
|
|
|
|
} catch (e) {
|
|
|
|
log('Ignoring search provider ' + path + ': missing DesktopId');
|
|
|
|
return;
|
|
|
|
}
|
2012-12-05 17:20:22 -05:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
let version = '1';
|
|
|
|
try {
|
|
|
|
version = keyfile.get_string(group, 'Version');
|
|
|
|
} catch (e) {
|
|
|
|
// ignore error
|
|
|
|
}
|
2012-12-05 17:20:22 -05:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
if (version >= 2)
|
|
|
|
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
|
|
|
|
else
|
|
|
|
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
|
|
|
|
2014-08-01 07:35:31 -04:00
|
|
|
remoteProvider.defaultEnabled = true;
|
|
|
|
try {
|
|
|
|
remoteProvider.defaultEnabled = !keyfile.get_boolean(group, 'DefaultDisabled');
|
|
|
|
} catch(e) {
|
|
|
|
// ignore error
|
|
|
|
}
|
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
objectPaths[objectPath] = remoteProvider;
|
|
|
|
loadedProviders.push(remoteProvider);
|
|
|
|
} catch(e) {
|
|
|
|
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
|
|
|
}
|
2012-11-30 10:36:12 -05:00
|
|
|
}
|
2012-11-01 15:55:41 -04:00
|
|
|
|
2014-06-24 15:17:09 -04:00
|
|
|
let searchSettings = new Gio.Settings({ schema_id: Search.SEARCH_PROVIDERS_SCHEMA });
|
2013-11-02 18:03:52 -04:00
|
|
|
if (searchSettings.get_boolean('disable-external')) {
|
|
|
|
callback([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-04 10:07:44 -05:00
|
|
|
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
|
2013-07-26 18:36:31 -04:00
|
|
|
|
2012-11-01 15:57:18 -04:00
|
|
|
let sortOrder = searchSettings.get_strv('sort-order');
|
2012-12-28 12:03:13 -05:00
|
|
|
|
|
|
|
// Special case gnome-control-center to be always active and always first
|
|
|
|
sortOrder.unshift('gnome-control-center.desktop');
|
|
|
|
|
2013-11-02 18:03:52 -04:00
|
|
|
loadedProviders = loadedProviders.filter(function(provider) {
|
|
|
|
let appId = provider.appInfo.get_id();
|
2014-08-01 07:35:31 -04:00
|
|
|
|
|
|
|
if (provider.defaultEnabled) {
|
|
|
|
let disabled = searchSettings.get_strv('disabled');
|
|
|
|
return disabled.indexOf(appId) == -1;
|
|
|
|
} else {
|
|
|
|
let enabled = searchSettings.get_strv('enabled');
|
|
|
|
return enabled.indexOf(appId) != -1;
|
|
|
|
}
|
2013-11-02 18:03:52 -04:00
|
|
|
});
|
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
loadedProviders.sort(function(providerA, providerB) {
|
|
|
|
let idxA, idxB;
|
|
|
|
let appIdA, appIdB;
|
2012-11-01 15:57:18 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
appIdA = providerA.appInfo.get_id();
|
|
|
|
appIdB = providerB.appInfo.get_id();
|
2012-11-01 15:57:18 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
idxA = sortOrder.indexOf(appIdA);
|
|
|
|
idxB = sortOrder.indexOf(appIdB);
|
2012-11-01 15:57:18 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
// if no provider is found in the order, use alphabetical order
|
|
|
|
if ((idxA == -1) && (idxB == -1)) {
|
|
|
|
let nameA = providerA.appInfo.get_name();
|
|
|
|
let nameB = providerB.appInfo.get_name();
|
2013-01-28 12:04:59 -05:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
return GLib.utf8_collate(nameA, nameB);
|
|
|
|
}
|
2012-11-01 15:57:18 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
// if providerA isn't found, it's sorted after providerB
|
|
|
|
if (idxA == -1)
|
|
|
|
return 1;
|
2012-11-01 15:57:18 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
// if providerB isn't found, it's sorted after providerA
|
|
|
|
if (idxB == -1)
|
|
|
|
return -1;
|
2012-11-01 15:57:18 -04:00
|
|
|
|
2013-07-26 18:36:31 -04:00
|
|
|
// finally, if both providers are found, return their order in the list
|
|
|
|
return (idxA - idxB);
|
|
|
|
});
|
2012-11-01 15:57:18 -04:00
|
|
|
|
2013-11-02 18:03:52 -04:00
|
|
|
callback(loadedProviders);
|
2012-11-01 15:55:41 -04:00
|
|
|
}
|
|
|
|
|
2011-11-16 18:11:05 -05:00
|
|
|
const RemoteSearchProvider = new Lang.Class({
|
|
|
|
Name: 'RemoteSearchProvider',
|
|
|
|
|
2013-09-26 07:22:22 -04:00
|
|
|
_init: function(appInfo, dbusName, dbusPath, proxyInfo) {
|
|
|
|
if (!proxyInfo)
|
|
|
|
proxyInfo = SearchProviderProxyInfo;
|
2012-12-05 17:20:22 -05:00
|
|
|
|
2013-09-26 07:22:22 -04:00
|
|
|
this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION,
|
|
|
|
g_name: dbusName,
|
|
|
|
g_object_path: dbusPath,
|
|
|
|
g_interface_info: proxyInfo,
|
|
|
|
g_interface_name: proxyInfo.name,
|
|
|
|
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
|
|
|
|
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
|
|
|
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
|
2011-11-16 18:11:05 -05:00
|
|
|
|
2012-12-06 14:10:44 -05:00
|
|
|
this.appInfo = appInfo;
|
|
|
|
this.id = appInfo.get_id();
|
|
|
|
this.isRemoteProvider = true;
|
2011-11-16 18:11:05 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
createIcon: function(size, meta) {
|
2013-12-06 06:56:43 -05:00
|
|
|
let gicon = null;
|
|
|
|
let icon = null;
|
|
|
|
|
2013-04-24 12:05:22 -04:00
|
|
|
if (meta['icon']) {
|
|
|
|
gicon = Gio.icon_deserialize(meta['icon']);
|
|
|
|
} else if (meta['gicon']) {
|
2013-01-02 09:31:50 -05:00
|
|
|
gicon = Gio.icon_new_for_string(meta['gicon']);
|
2011-11-16 18:11:05 -05:00
|
|
|
} else if (meta['icon-data']) {
|
|
|
|
let [width, height, rowStride, hasAlpha,
|
|
|
|
bitsPerSample, nChannels, data] = meta['icon-data'];
|
2013-01-02 09:31:50 -05:00
|
|
|
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
|
|
|
bitsPerSample, width, height, rowStride);
|
2011-11-16 18:11:05 -05:00
|
|
|
}
|
|
|
|
|
2013-12-06 06:56:43 -05:00
|
|
|
if (gicon)
|
|
|
|
icon = new St.Icon({ gicon: gicon,
|
|
|
|
icon_size: size });
|
|
|
|
return icon;
|
2011-11-16 18:11:05 -05:00
|
|
|
},
|
|
|
|
|
2013-05-29 16:48:30 -04:00
|
|
|
filterResults: function(results, maxNumber) {
|
|
|
|
if (results.length <= maxNumber)
|
|
|
|
return results;
|
|
|
|
|
|
|
|
let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
|
|
|
|
let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
|
|
|
|
|
|
|
|
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
|
|
|
|
},
|
|
|
|
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
_getResultsFinished: function(results, error, callback) {
|
2013-09-26 07:22:22 -04:00
|
|
|
if (error) {
|
RemoteSearch: don't complete a search that was cancelled
This closes a race between setTerms and a slow GetInitialResultSet.
The bug manifests as follows:
- initial search for a short string
- previous results === undefined, call GetInitialResultSet
- user types more, cancel previous search in setTerms()
- mainloop, then _gotResults([])
- previous results === [], !!previous results === true
- therefore call GetSubsearchResultSet with an empty list of results
- _gotResults() from GetSubsearchResultSet is empty
- much later, return from GetInitialResultSet is discarded by
cancellable
- user unhappy because what he searched for is not there
After this fix, the flow is:
- initial search for a short string
- previous results === undefined, call GetInitialResultSet
- user types more, cancel previous search in setTerms()
- mainloop, but no _gotResults
- previous results === undefined, call GetInitialResultSet again with
longer string
- some time later, return from first GetInitialResultSet is discarded
by cancellable
- soon after, return from second GetInitialResultSet comes with good
results
- user happy
https://bugzilla.gnome.org/show_bug.cgi?id=745861
2015-03-08 18:32:22 -04:00
|
|
|
if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
return;
|
|
|
|
|
|
|
|
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
callback([]);
|
|
|
|
return;
|
2013-09-26 07:22:22 -04:00
|
|
|
}
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
|
|
|
|
callback(results[0]);
|
2011-11-16 18:11:05 -05:00
|
|
|
},
|
|
|
|
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
getInitialResultSet: function(terms, callback, cancellable) {
|
2013-09-26 07:22:22 -04:00
|
|
|
this.proxy.GetInitialResultSetRemote(terms,
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
Lang.bind(this, this._getResultsFinished, callback),
|
|
|
|
cancellable);
|
2011-11-16 18:11:05 -05:00
|
|
|
},
|
|
|
|
|
2013-11-02 20:09:14 -04:00
|
|
|
getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
|
2013-09-26 07:22:22 -04:00
|
|
|
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
Lang.bind(this, this._getResultsFinished, callback),
|
|
|
|
cancellable);
|
2011-11-16 18:11:05 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_getResultMetasFinished: function(results, error, callback) {
|
|
|
|
if (error) {
|
2013-09-26 07:22:22 -04:00
|
|
|
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
|
2011-11-16 18:11:05 -05:00
|
|
|
callback([]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let metas = results[0];
|
|
|
|
let resultMetas = [];
|
|
|
|
for (let i = 0; i < metas.length; i++) {
|
2013-04-24 12:05:22 -04:00
|
|
|
for (let prop in metas[i]) {
|
|
|
|
// we can use the serialized icon variant directly
|
|
|
|
if (prop != 'icon')
|
|
|
|
metas[i][prop] = metas[i][prop].deep_unpack();
|
|
|
|
}
|
|
|
|
|
2011-11-16 18:11:05 -05:00
|
|
|
resultMetas.push({ id: metas[i]['id'],
|
|
|
|
name: metas[i]['name'],
|
2013-02-27 11:54:09 -05:00
|
|
|
description: metas[i]['description'],
|
2011-11-16 18:11:05 -05:00
|
|
|
createIcon: Lang.bind(this,
|
|
|
|
this.createIcon, metas[i]) });
|
|
|
|
}
|
|
|
|
callback(resultMetas);
|
|
|
|
},
|
|
|
|
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
getResultMetas: function(ids, callback, cancellable) {
|
2013-09-26 07:22:22 -04:00
|
|
|
this.proxy.GetResultMetasRemote(ids,
|
|
|
|
Lang.bind(this, this._getResultMetasFinished, callback),
|
search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.
We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.
Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.
Finally, it's time to modernize. Promises^WCallbacks are the future.
Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 19:45:35 -04:00
|
|
|
cancellable);
|
2011-11-16 18:11:05 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
activateResult: function(id) {
|
2012-12-05 17:20:22 -05:00
|
|
|
this.proxy.ActivateResultRemote(id);
|
2012-11-26 16:55:25 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
launchSearch: function(terms) {
|
|
|
|
// the provider is not compatible with the new version of the interface, launch
|
|
|
|
// the app itself but warn so we can catch the error in logs
|
|
|
|
log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch');
|
2014-01-19 12:34:32 -05:00
|
|
|
this.appInfo.launch([], global.create_app_launch_context(0, -1));
|
2011-11-16 18:11:05 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2012-12-05 17:20:22 -05:00
|
|
|
const RemoteSearchProvider2 = new Lang.Class({
|
|
|
|
Name: 'RemoteSearchProvider2',
|
|
|
|
Extends: RemoteSearchProvider,
|
|
|
|
|
|
|
|
_init: function(appInfo, dbusName, dbusPath) {
|
2013-09-26 07:22:22 -04:00
|
|
|
this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo);
|
2012-11-26 16:55:25 -05:00
|
|
|
|
|
|
|
this.canLaunchSearch = true;
|
2012-12-05 17:20:22 -05:00
|
|
|
},
|
2011-11-16 18:11:05 -05:00
|
|
|
|
2012-12-05 11:48:22 -05:00
|
|
|
activateResult: function(id, terms) {
|
|
|
|
this.proxy.ActivateResultRemote(id, terms, global.get_current_time());
|
2012-11-26 16:55:25 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
launchSearch: function(terms) {
|
2012-12-10 15:47:25 -05:00
|
|
|
this.proxy.LaunchSearchRemote(terms, global.get_current_time());
|
2012-12-05 17:20:22 -05:00
|
|
|
}
|
|
|
|
});
|