From 4b015903bc8a957729344e804a7c02ed1aed76ec Mon Sep 17 00:00:00 2001 From: Seif Lotfy Date: Wed, 26 Jan 2011 20:43:44 +0100 Subject: [PATCH] Add support for asynchronous search providers https://bugzilla.gnome.org/show_bug.cgi?id=640659 --- js/ui/search.js | 88 +++++++++++++++++++++++++++++++++++------- js/ui/searchDisplay.js | 14 ++++--- js/ui/viewSelector.js | 2 +- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/js/ui/search.js b/js/ui/search.js index ca35341e9..e465a6a87 100644 --- a/js/ui/search.js +++ b/js/ui/search.js @@ -117,6 +117,43 @@ function SearchProvider(title) { SearchProvider.prototype = { _init: function(title) { this.title = title; + this.searchSystem = null; + this.searchAsync = false; + }, + + _asyncCancelled: function() { + }, + + startAsync: function() { + this.searchAsync = true; + }, + + tryCancelAsync: function() { + if (!this.searchAsync) + return; + this._asyncCancelled(); + this.searchAsync = false; + }, + + /** + * addItems: + * @items: an array of result identifier strings representing + * items which match the last given search terms. + * + * This should be used for something that requires a bit more + * logic; it's designed to be an asyncronous way to add a result + * to the current search. + */ + addItems: function( items) { + if (!this.searchSystem) + throw new Error('Search provider not registered'); + + if (!items.length) + return; + + this.tryCancelAsync(); + + this.searchSystem.addProviderItems(this, items); }, /** @@ -224,6 +261,7 @@ SearchProvider.prototype = { }; Signals.addSignalMethods(SearchProvider.prototype); + function OpenSearchSystem() { this._init(); } @@ -336,6 +374,7 @@ SearchSystem.prototype = { }, registerProvider: function (provider) { + provider.searchSystem = this; this._providers.push(provider); }, @@ -352,30 +391,50 @@ SearchSystem.prototype = { this._previousResults = []; }, + addProviderItems: function(provider, items) { + let index = this._providers.indexOf(provider); + let [provider2, results] = this._previousResults[index]; + if (provider !== provider2) + return; + + results.push.apply(results, items); + this.emit('results-updated', this._previousResults); + }, + updateSearch: function(searchString) { searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, ''); if (searchString == '') - return []; + return; let terms = searchString.split(/\s+/); - let isSubSearch = terms.length == this._previousTerms.length; - if (isSubSearch) { - for (let i = 0; i < terms.length; i++) { - if (terms[i].indexOf(this._previousTerms[i]) != 0) { - isSubSearch = false; - break; + this.updateSearchResults(terms); + }, + + updateSearchResults: function(terms) { + let isSubSearch = false; + + if (terms) { + isSubSearch = terms.length == this._previousTerms.length; + if (isSubSearch) { + for (let i = 0; i < terms.length; i++) { + if (terms[i].indexOf(this._previousTerms[i]) != 0) { + isSubSearch = false; + break; + } } } + } else { + terms = this._previousTerms; } let results = []; if (isSubSearch) { - for (let i = 0; i < this._previousResults.length; i++) { + for (let i = 0; i < this._providers.length; i++) { let [provider, previousResults] = this._previousResults[i]; + provider.tryCancelAsync(); try { let providerResults = provider.getSubsearchResultSet(previousResults, terms); - if (providerResults.length > 0) - results.push([provider, providerResults]); + results.push([provider, providerResults]); } catch (error) { global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); } @@ -383,10 +442,10 @@ SearchSystem.prototype = { } else { for (let i = 0; i < this._providers.length; i++) { let provider = this._providers[i]; + provider.tryCancelAsync(); try { let providerResults = provider.getInitialResultSet(terms); - if (providerResults.length > 0) - results.push([provider, providerResults]); + results.push([provider, providerResults]); } catch (error) { global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); } @@ -395,8 +454,7 @@ SearchSystem.prototype = { this._previousTerms = terms; this._previousResults = results; - - return results; - } + this.emit('results-updated', results); + }, }; Signals.addSignalMethods(SearchSystem.prototype); diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js index 7e9ba7a7d..b1a08d9dc 100644 --- a/js/ui/searchDisplay.js +++ b/js/ui/searchDisplay.js @@ -157,6 +157,7 @@ function SearchResults(searchSystem, openSearchSystem) { SearchResults.prototype = { _init: function(searchSystem, openSearchSystem) { this._searchSystem = searchSystem; + this._searchSystem.connect('results-updated', Lang.bind(this, this._updateResults)); this._openSearchSystem = openSearchSystem; this.actor = new St.BoxLayout({ name: 'searchResults', @@ -296,15 +297,16 @@ SearchResults.prototype = { this._statusText.show(); }, + doSearch: function (searchString) { + this._searchSystem.updateSearch(searchString); + }, + _metaForProvider: function(provider) { return this._providerMeta[this._providers.indexOf(provider)]; }, - updateSearch: function (searchString) { - let results = this._searchSystem.updateSearch(searchString); - + _updateResults: function(searchSystem, results) { this._clearDisplay(); - if (results.length == 0) { this._statusText.set_text(_("No matching results.")); this._statusText.show(); @@ -314,11 +316,13 @@ SearchResults.prototype = { this._statusText.hide(); } - let terms = this._searchSystem.getTerms(); + let terms = searchSystem.getTerms(); this._openSearchSystem.setSearchTerms(terms); for (let i = 0; i < results.length; i++) { let [provider, providerResults] = results[i]; + if (providerResults.length == 0) + continue; let meta = this._metaForProvider(provider); meta.actor.show(); meta.resultDisplay.renderResults(providerResults, terms); diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index 7e5fa2726..f569f9e7b 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -268,7 +268,7 @@ SearchTab.prototype = { _doSearch: function () { this._searchTimeoutId = 0; let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, ''); - this._searchResults.updateSearch(text); + this._searchResults.doSearch(text); return false; }