[gnome-characters] search-provider: Initial implementation



commit a9567c4e3b0b6709d78588bfa3cd95f38debd453
Author: Daiki Ueno <dueno src gnome org>
Date:   Sat Feb 21 09:01:09 2015 +0900

    search-provider: Initial implementation
    
    The implementation is largely based on gnome-weather.  Unlike other
    search-providers, this initiates a new search upon both
    GetInitialResultSet and GetSubsearchResultSet, because the initial
    result set can be too large and refinement doesn't work.
    
    Also, upon ActivateResult it doesn't launch the application, but copies
    the character into clipboard, as suggested in bug 744855.

 data/Makefile.am                                   |   25 ++++-
 data/ShellSearchProvider2.xml                      |   84 +++++++++++++
 ...Characters.BackgroundService.data.gresource.xml |    8 ++
 ...g.gnome.Characters.BackgroundService.service.in |    3 +
 data/org.gnome.Characters.search-provider.ini      |    6 +
 src/Makefile.am                                    |   22 +++-
 src/org.gnome.Characters.BackgroundService.in      |    6 +
 ....Characters.BackgroundService.src.gresource.xml |    9 ++
 src/searchProvider.js                              |  125 ++++++++++++++++++++
 src/service.js                                     |   98 +++++++++++++++
 10 files changed, 381 insertions(+), 5 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 1168dc7..ce11934 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -6,8 +6,12 @@ app_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --g
 org.gnome.Characters.data.gresource: org.gnome.Characters.data.gresource.xml $(app_resource_files)
        $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $<
 
+service_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies 
$(srcdir)/org.gnome.Characters.BackgroundService.data.gresource.xml)
+org.gnome.Characters.BackgroundService.data.gresource: 
org.gnome.Characters.BackgroundService.data.gresource.xml $(service_resource_files)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $<
+
 resourcedir = $(pkgdatadir)
-resource_DATA = org.gnome.Characters.data.gresource 
+resource_DATA = org.gnome.Characters.data.gresource org.gnome.Characters.BackgroundService.data.gresource
 
 appsdir = $(datadir)/applications
 apps_DATA = org.gnome.Characters.desktop
@@ -25,26 +29,41 @@ appstream_XML = org.gnome.Characters.appdata.xml
 
 org.gnome.Characters.service: org.gnome.Characters.service.in
        $(AM_V_GEN) sed \
-               -e "s|[ ]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
+               -e "s|[ ]PACKAGE_NAME@|org.gnome.Characters|g" \
+               -e "s|[ ]pkgdatadir@|$(pkgdatadir)|g" \
+               -e "s|[ ]pkglibdir@|$(pkglibdir)|g" \
+               $< > $@
+
+org.gnome.Characters.BackgroundService.service: org.gnome.Characters.BackgroundService.service.in
+       $(AM_V_GEN) sed \
+               -e "s|[ ]PACKAGE_NAME@|org.gnome.Characters|g" \
                -e "s|[ ]pkgdatadir@|$(pkgdatadir)|g" \
                -e "s|[ ]pkglibdir@|$(pkglibdir)|g" \
                $< > $@
 
 servicedir = $(datadir)/dbus-1/services
-service_DATA = org.gnome.Characters.service
+service_DATA = org.gnome.Characters.service org.gnome.Characters.BackgroundService.service
+
+searchproviderdir = $(datadir)/gnome-shell/search-providers
+dist_searchprovider_DATA = org.gnome.Characters.search-provider.ini
 
 EXTRA_DIST = \
        org.gnome.Characters.desktop.in \
        org.gnome.Characters.service.in \
+       org.gnome.Characters.BackgroundService.service.in \
        org.gnome.Characters.data.gresource.xml \
+       org.gnome.Characters.BackgroundService.data.gresource.xml \
        $(gsettings_SCHEMAS) \
        org.gnome.Characters.appdata.xml.in \
        $(app_resource_files) \
+       ShellSearchProvider2.xml \
        $(NULL)
 
 CLEANFILES = \
        org.gnome.Characters.service \
+       org.gnome.Characters.BackgroundService.service \
        org.gnome.Characters.data.gresource \
+       org.gnome.Characters.BackgroundService.data.gresource \
        org.gnome.Characters.appdata.xml \
        org.gnome.Characters.appdata.valid \
        $(apps_DATA) \
diff --git a/data/ShellSearchProvider2.xml b/data/ShellSearchProvider2.xml
new file mode 100644
index 0000000..f7ae7ce
--- /dev/null
+++ b/data/ShellSearchProvider2.xml
@@ -0,0 +1,84 @@
+<node>
+
+  <!--
+      org.gnome.Shell.SearchProvider2:
+      @short_description: Search provider interface
+
+      The interface used for integrating into GNOME Shell's search
+      interface (version 2).
+  -->
+  <interface name="org.gnome.Shell.SearchProvider2">
+
+    <!--
+        GetInitialResultSet:
+        @terms: Array of search terms, which the provider should treat as logical AND.
+        @results: An array of result identifier strings representing items which match the given search 
terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by 
the provider.
+
+        Called when the user first begins a search.
+    -->
+    <method name="GetInitialResultSet">
+      <arg type="as" name="terms" direction="in" />
+      <arg type="as" name="results" direction="out" />
+    </method>
+
+    <!--
+        GetSubsearchResultSet:
+        @previous_results: Array of results previously returned by GetInitialResultSet().
+        @terms: Array of updated search terms, which the provider should treat as logical AND.
+        @results: An array of result identifier strings representing items which match the given search 
terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by 
the provider.
+
+        Called when a search is performed which is a "subsearch" of
+        the previous search, e.g. the method may return less results, but
+        not more or different results.
+
+        This allows search providers to only search through the previous
+        result set, rather than possibly performing a full re-query.
+    -->
+    <method name="GetSubsearchResultSet">
+      <arg type="as" name="previous_results" direction="in" />
+      <arg type="as" name="terms" direction="in" />
+      <arg type="as" name="results" direction="out" />
+    </method>
+
+    <!--
+        GetResultMetas:
+        @identifiers: An array of result identifiers as returned by GetInitialResultSet() or 
GetSubsearchResultSet()
+        @metas: A dictionary describing the given search result, containing a human-readable 'name' 
(string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized 
GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail 
of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 
'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, 
data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also 
be specified if more context would help the user find the desired result.
+
+        Return an array of meta data used to display each given result
+    -->
+    <method name="GetResultMetas">
+      <arg type="as" name="identifiers" direction="in" />
+      <arg type="aa{sv}" name="metas" direction="out" />
+    </method>
+
+    <!--
+        ActivateResult:
+        @identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
+        @terms: Array of search terms, which the provider should treat as logical AND.
+        @timestamp: A timestamp of the user interaction that triggered this call
+
+        Called when the users chooses a given result. The result should
+        be displayed in the application associated with the corresponding
+        provider. The provided search terms can be used to allow launching a full search in
+        the application.
+    -->
+    <method name="ActivateResult">
+      <arg type="s" name="identifier" direction="in" />
+      <arg type="as" name="terms" direction="in" />
+      <arg type="u" name="timestamp" direction="in" />
+    </method>
+
+    <!--
+        LaunchSearch:
+        @terms: Array of search terms, which the provider should treat as logical AND.
+        @timestamp: A timestamp of the user interaction that triggered this call
+
+        Asks the search provider to launch a full search in the application for the provided terms.
+    -->
+    <method name="LaunchSearch">
+      <arg type="as" name="terms" direction="in" />
+      <arg type="u" name="timestamp" direction="in" />
+    </method>
+  </interface>
+</node>
diff --git a/data/org.gnome.Characters.BackgroundService.data.gresource.xml 
b/data/org.gnome.Characters.BackgroundService.data.gresource.xml
new file mode 100644
index 0000000..69abe6a
--- /dev/null
+++ b/data/org.gnome.Characters.BackgroundService.data.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/Characters/BackgroundService">
+  </gresource>
+  <gresource prefix="/org/gnome/shell">
+    <file>ShellSearchProvider2.xml</file>
+  </gresource>
+</gresources>
diff --git a/data/org.gnome.Characters.BackgroundService.service.in 
b/data/org.gnome.Characters.BackgroundService.service.in
new file mode 100644
index 0000000..484a690
--- /dev/null
+++ b/data/org.gnome.Characters.BackgroundService.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name= PACKAGE_NAME@.BackgroundService
+Exec= pkgdatadir@/@PACKAGE_NAME  BackgroundService
diff --git a/data/org.gnome.Characters.search-provider.ini b/data/org.gnome.Characters.search-provider.ini
new file mode 100644
index 0000000..0c132f9
--- /dev/null
+++ b/data/org.gnome.Characters.search-provider.ini
@@ -0,0 +1,6 @@
+[Shell Search Provider]
+DesktopId=org.gnome.Characters.desktop
+BusName=org.gnome.Characters.BackgroundService
+ObjectPath=/org/gnome/Characters/BackgroundService
+Version=2
+DefaultDisabled=true
diff --git a/src/Makefile.am b/src/Makefile.am
index dbe42bd..2eb23f9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,14 +1,18 @@
 NULL =
 
 appdir = $(pkgdatadir)
-nodist_app_SCRIPTS = org.gnome.Characters
+nodist_app_SCRIPTS = org.gnome.Characters org.gnome.Characters.BackgroundService
 
 app_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies 
$(srcdir)/org.gnome.Characters.src.gresource.xml)
 org.gnome.Characters.src.gresource: org.gnome.Characters.src.gresource.xml $(app_resource_files)
        $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $<
 
+service_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies 
$(srcdir)/org.gnome.Characters.BackgroundService.src.gresource.xml)
+org.gnome.Characters.BackgroundService.src.gresource: 
org.gnome.Characters.BackgroundService.src.gresource.xml $(service_resource_files)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $<
+
 resourcedir = $(pkgdatadir)
-resource_DATA = org.gnome.Characters.src.gresource
+resource_DATA = org.gnome.Characters.src.gresource org.gnome.Characters.BackgroundService.src.gresource
 
 org.gnome.Characters: org.gnome.Characters.in
        $(AM_V_GEN) sed \
@@ -20,9 +24,21 @@ org.gnome.Characters: org.gnome.Characters.in
                $< > $@
        @chmod +x $@
 
+org.gnome.Characters.BackgroundService: org.gnome.Characters.BackgroundService.in
+       $(AM_V_GEN) sed \
+               -e "s|[ ]GJS@|$(GJS)|g" \
+               -e "s|[ ]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
+               -e "s|[ ]prefix@|$(prefix)|g" \
+               -e "s|[ ]libdir@|$(libdir)|g" \
+               -e "s|[ ]pkgdatadir@|$(pkgdatadir)|g" \
+               $< > $@
+       @chmod +x $@
+
 EXTRA_DIST = \
        org.gnome.Characters.in \
        org.gnome.Characters.src.gresource.xml \
+       org.gnome.Characters.BackgroundService.in \
+       org.gnome.Characters.BackgroundService.src.gresource.xml \
        $(app_resource_files) \
        $(service_resource_files) \
        $(NULL)
@@ -30,6 +46,8 @@ EXTRA_DIST = \
 CLEANFILES = \
        org.gnome.Characters \
        org.gnome.Characters.src.gresource \
+       org.gnome.Characters.BackgroundService \
+       org.gnome.Characters.BackgroundService.src.gresource \
        $(NULL)
 
 install-exec-hook:
diff --git a/src/org.gnome.Characters.BackgroundService.in b/src/org.gnome.Characters.BackgroundService.in
new file mode 100644
index 0000000..bf43031
--- /dev/null
+++ b/src/org.gnome.Characters.BackgroundService.in
@@ -0,0 +1,6 @@
+#! GJS@
+imports.package.init({ name: "org.gnome.Characters",
+                       version: "@PACKAGE_VERSION@",
+                       prefix: "@prefix@",
+                       libdir: "@libdir@" });
+imports.package.run(imports.service);
diff --git a/src/org.gnome.Characters.BackgroundService.src.gresource.xml 
b/src/org.gnome.Characters.BackgroundService.src.gresource.xml
new file mode 100644
index 0000000..c9fd00d
--- /dev/null
+++ b/src/org.gnome.Characters.BackgroundService.src.gresource.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/Characters/BackgroundService/js">
+    <file>service.js</file>
+    <file>searchProvider.js</file>
+    <file>params.js</file>
+    <file>util.js</file>
+  </gresource>
+</gresources>
diff --git a/src/searchProvider.js b/src/searchProvider.js
new file mode 100644
index 0000000..76bc4b4
--- /dev/null
+++ b/src/searchProvider.js
@@ -0,0 +1,125 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2013 Giovanni Campagna <scampa giovanni gmail com>
+// Copyright (C) 2015  Daiki Ueno <dueno src gnome org>
+//
+// Gnome Weather is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2 of the License, or (at your
+// option) any later version.
+//
+// Gnome Weather is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with Gnome Weather; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Gc = imports.gi.Gc;
+const Util = imports.util;
+
+const MAX_SEARCH_RESULTS = 100
+
+const SearchProviderInterface = Gio.resources_lookup_data('/org/gnome/shell/ShellSearchProvider2.xml', 
0).toArray().toString();
+
+const SearchProvider = new Lang.Class({
+    Name: 'WeatherSearchProvider',
+
+    _init: function(application) {
+        this._app = application;
+
+        this._impl = Gio.DBusExportedObject.wrapJSObject(SearchProviderInterface, this);
+        this._cancellable = new Gio.Cancellable();
+    },
+
+    export: function(connection, path) {
+        return this._impl.export(connection, path);
+    },
+
+    unexport: function(connection) {
+        return this._impl.unexport_from_connection(connection);
+    },
+
+    _runQuery: function(keywords, invocation) {
+        this._cancellable.cancel();
+        this._cancellable.reset();
+
+        Gc.search_by_keywords(
+            keywords.map(String.toUpperCase),
+            MAX_SEARCH_RESULTS,
+            this._cancellable,
+            Lang.bind(this, function(source_object, res, user_data) {
+                let characters = [];
+                try {
+                    let result = Gc.search_finish(res);
+                    for (let index = 0; index < result.len; index++) {
+                        characters.push(Gc.search_result_get(result, index));
+                    }
+                } catch (e) {
+                    log("Failed to search by keywords: " + e);
+                }
+                invocation.return_value(new GLib.Variant('(as)', [characters]));
+            }));
+    },
+
+    GetInitialResultSetAsync: function(params, invocation) {
+        this._app.hold();
+        this._runQuery(params[0], invocation);
+    },
+
+    GetSubsearchResultSetAsync: function(params, invocation) {
+        this._app.hold();
+        this._runQuery(params[1], invocation);
+    },
+
+    GetResultMetas: function(identifiers) {
+        this._app.hold();
+
+        let ret = [];
+
+        for (let i = 0; i < identifiers.length; i++) {
+            let character = identifiers[i];
+            let codePoint = character.charCodeAt(0);
+            let codePointHex = codePoint.toString(16).toUpperCase();
+            let name = Gc.character_name(character);
+            if (name == null)
+                name = _("Unknown character name");
+            else
+                name = Util.capitalize(name);
+            let summary = _("U+%s, %s: %s").format(codePointHex,
+                                                   character,
+                                                   name);
+            ret.push({ name: new GLib.Variant('s', name),
+                       id: new GLib.Variant('s', identifiers[i]),
+                       description: new GLib.Variant('s', summary),
+                       icon: (new Gio.ThemedIcon({ name: 'gnome-characters' })).serialize()
+                     });
+        }
+
+        this._app.release();
+
+        return ret;
+    },
+
+    _getPlatformData: function(timestamp) {
+        return {'desktop-startup-id': new GLib.Variant('s', '_TIME' + timestamp) };
+    },
+
+    ActivateResult: function(id, terms, timestamp) {
+        let clipboard = Gc.gtk_clipboard_get();
+        // FIXME: GLib.unichar_to_utf8() has missing (nullable)
+        // annotation to the outbuf argument.
+        let outbuf = '      ';
+        let length = GLib.unichar_to_utf8(id, outbuf);
+        clipboard.set_text(id, length);
+    },
+
+    LaunchSearch: function(terms, timestamp) {
+        // not implemented
+    },
+});
diff --git a/src/service.js b/src/service.js
new file mode 100644
index 0000000..b260bff
--- /dev/null
+++ b/src/service.js
@@ -0,0 +1,98 @@
+// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
+//
+// Copyright (c) 2012 Giovanni Campagna <scampa giovanni gmail com>
+// Copyright (C) 2015  Daiki Ueno <dueno src gnome org>
+//
+// Gnome Weather is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2 of the License, or (at your
+// option) any later version.
+//
+// Gnome Weather is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with Gnome Weather; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+pkg.initGettext();
+pkg.initFormat();
+pkg.require({ 'Gio': '2.0',
+              'GLib': '2.0',
+              'GObject': '2.0',
+              'Gtk': '3.0' });
+
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+
+const Util = imports.util;
+const SearchProvider = imports.searchProvider;
+
+function initEnvironment() {
+    window.getApp = function() {
+        return Gio.Application.get_default();
+    };
+}
+
+const BackgroundService = new Lang.Class({
+    Name: 'CharactersBackgroundService',
+    // This needs to be a Gtk.Application instead of Gio.Application,
+    // to get Gtk.Clipboard working.
+    Extends: Gtk.Application,
+
+    _init: function() {
+        this.parent({ application_id: pkg.name,
+                      flags: Gio.ApplicationFlags.IS_SERVICE,
+                      inactivity_timeout: 60000 });
+        GLib.set_application_name(_("Characters"));
+
+        this._searchProvider = new SearchProvider.SearchProvider(this);
+    },
+
+    _onQuit: function() {
+        this.quit();
+    },
+
+    vfunc_dbus_register: function(connection, path) {
+        this.parent(connection, path);
+
+        this._searchProvider.export(connection, path);
+        return true;
+    },
+
+/*
+  Can't do until GApplication is fixed.
+
+    vfunc_dbus_unregister: function(connection, path) {
+        this._searchProvider.unexport(connection);
+
+        this.parent(connection, path);
+    },
+*/
+
+    vfunc_startup: function() {
+        this.parent();
+
+        Util.initActions(this,
+                         [{ name: 'quit',
+                            activate: this._onQuit }]);
+    },
+
+    vfunc_activate: function() {
+        // do nothing, this is a background service
+    },
+
+    vfunc_shutdown: function() {
+        // do nothing, this is a background service
+    }
+});
+
+function main(argv) {
+    initEnvironment();
+
+    return (new BackgroundService()).run(argv);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]