[gnome-clocks] Implement a Gnome Shell search provider



commit c875c737a90d5e2f15a22dd19426ec0d9c11bf17
Author: Paolo Borelli <pborelli gnome org>
Date:   Sat Aug 9 14:32:06 2014 +0200

    Implement a Gnome Shell search provider
    
    For now it just reads from the gsetting world clock list every time. We
    may want to make share the model with the actual World panel instead
    so that we do not rebuild the table every time, but given how small our
    search model is, it does not seem to be a performance problem.
    
    More generic questions are:
     * should we search also other locations beside the one added in the
       world clock?
     * should we also search alarm names?

 Makefile.am                               |    6 ++
 data/org.gnome.clocks.search-provider.ini |    5 ++
 data/org.gnome.clocks.service.in          |    2 +-
 po/POTFILES.in                            |    1 +
 src/application.vala                      |   36 +++++++++-
 src/search-provider.vala                  |  110 +++++++++++++++++++++++++++++
 src/window.vala                           |    5 ++
 src/world.vala                            |   13 +++-
 8 files changed, 173 insertions(+), 5 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 3053335..6adae26 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,10 @@ appdatadir = $(datadir)/appdata
 appdata_DATA = $(appdata_in_files:.xml.in=.xml)
 appdata_in_files = data/appdata/org.gnome.clocks.appdata.xml.in
 
+# gnome-shell search provider
+searchproviderdir = $(datadir)/gnome-shell/search-providers
+searchprovider_DATA = data/org.gnome.clocks.search-provider.ini
+
 # gsettings
 gsettings_SCHEMAS = data/org.gnome.clocks.gschema.xml
 @INTLTOOL_XML_RULE@
@@ -124,6 +128,7 @@ VALA_SOURCES = \
        src/utils.vala \
        src/widgets.vala \
        src/geocoding.vala \
+       src/search-provider.vala \
        src/main.vala
 
 gnome_clocks_SOURCES = \
@@ -146,6 +151,7 @@ gnome_clocks_LDADD = \
 
 EXTRA_DIST = \
        $(appdata_in_files) \
+       $(searchprovider_DATA) \
        $(icon_files) \
        $(hcicon_files) \
        $(images_DATA) \
diff --git a/data/org.gnome.clocks.search-provider.ini b/data/org.gnome.clocks.search-provider.ini
new file mode 100644
index 0000000..246dd55
--- /dev/null
+++ b/data/org.gnome.clocks.search-provider.ini
@@ -0,0 +1,5 @@
+[Shell Search Provider]
+DesktopId=org.gnome.clocks.desktop
+BusName=org.gnome.clocks
+ObjectPath=/org/gnome/clocks/SearchProvider
+Version=2
diff --git a/data/org.gnome.clocks.service.in b/data/org.gnome.clocks.service.in
index 8958e76..b24e7b6 100644
--- a/data/org.gnome.clocks.service.in
+++ b/data/org.gnome.clocks.service.in
@@ -1,3 +1,3 @@
 [D-BUS Service]
 Name=org.gnome.clocks
-Exec= bindir@/gnome-clocks
+Exec= bindir@/gnome-clocks --gapplication-service
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f3c63d7..41ff176 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,6 +4,7 @@ data/org.gnome.clocks.gschema.xml.in.in
 src/alarm.vala
 src/application.vala
 src/clock.vala
+src/search-provider.vala
 src/stopwatch.vala
 src/timer.vala
 src/utils.vala
diff --git a/src/application.vala b/src/application.vala
index bb18533..f769c11 100644
--- a/src/application.vala
+++ b/src/application.vala
@@ -30,19 +30,49 @@ public class Application : Gtk.Application {
         { "quit", on_quit_activate }
     };
 
+    private SearchProvider search_provider;
+    private uint search_provider_id = 0;
     private Window window;
 
+    private void ensure_window () {
+        if (window == null) {
+            window = new Window (this);
+        }
+    }
+
     public Application () {
         Object (application_id: "org.gnome.clocks");
 
         add_main_option_entries (option_entries);
         add_action_entries (action_entries, this);
+
+        search_provider = new SearchProvider ();
+        search_provider.activate.connect ((timestamp) => {
+            ensure_window ();
+            window.show_world ();
+            window.present_with_time (timestamp);
+        });
     }
 
-    protected override void activate () {
-        if (window == null) {
-            window = new Window (this);
+    public override bool dbus_register (DBusConnection connection, string object_path) {
+        try {
+            search_provider_id = connection.register_object (object_path + "/SearchProvider", 
search_provider);
+        } catch (IOError error) {
+            printerr ("Could not register search provider service: %s\n", error.message);
+        }
+
+        return true;
+    }
+
+    public override void dbus_unregister (DBusConnection connection, string object_path) {
+        if (search_provider_id != 0) {
+            connection.unregister_object (search_provider_id);
+            search_provider_id = 0;
         }
+    }
+
+    protected override void activate () {
+        ensure_window ();
         window.present ();
     }
 
diff --git a/src/search-provider.vala b/src/search-provider.vala
new file mode 100644
index 0000000..29c3af4
--- /dev/null
+++ b/src/search-provider.vala
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014  Paolo Borelli <pborelli gnome org>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+namespace Clocks {
+
+[DBus (name = "org.gnome.Shell.SearchProvider2")]
+public class SearchProvider : Object {
+
+    public signal void activate (uint32 timestamp);
+
+    private HashTable<string, World.Item> locations;
+
+    private void load () {
+        // FIXME: for now we just reread the list from settings every time a
+        // search is started. This is not very efficient and it also duplicates
+        // some code from World... Ideally we should just share the data
+        // with the World panel, but that requires refactoring since the
+        // window may have not been created yet.
+        locations = new HashTable<string, World.Item> (str_hash, str_equal);;
+        var settings = new GLib.Settings ("org.gnome.clocks");
+        int count = 0;
+        foreach (var l in settings.get_value ("world-clocks")) {
+            World.Item? location = World.Item.deserialize (l);
+            if (location != null) {
+                locations.insert (count.to_string (), location);
+            }
+            count++;
+        }
+    }
+
+    public string[] get_initial_result_set (string[] terms) {
+        load ();
+
+        List<string> normalized_terms = new List<string> ();
+        foreach (string t in terms) {
+            normalized_terms.prepend (t.normalize ().casefold ());
+        }
+
+        var result = new GenericArray<string> ();
+        locations.foreach ((id, item) => {
+            if (item.matches_search (normalized_terms)) {
+                result.add (id);
+            }
+        });
+
+        return result.data;
+    }
+
+    public string[] get_subsearch_result_set (string[] previous_results, string[] terms) {
+        List<string> normalized_terms = new List<string> ();
+        foreach (string t in terms) {
+            normalized_terms.prepend (t.normalize ().casefold ());
+        }
+
+        var result = new GenericArray<string> ();
+        foreach (var r in previous_results) {
+            var item = locations.get (r);
+            if (item != null && item.matches_search (normalized_terms)) {
+                result.add (r);
+            }
+        }
+
+        return result.data;
+    }
+
+    public HashTable<string, Variant>[] get_result_metas (string[] results) {
+        var result = new GenericArray<HashTable<string, Variant>> ();
+        foreach (var r in results) {
+            var meta = new HashTable<string, Variant> (str_hash, str_equal);;
+            var item = locations.get (r);
+            if (item != null) {
+                string time_label = item.time_label;
+                string day =  item.day_label;
+                if (day != null) {
+                    time_label += " " + day;
+                }
+                meta.insert ("id", r);
+                meta.insert ("name", time_label);
+                meta.insert ("description", item.name);
+            }
+            result.add (meta);
+        }
+
+        return result.data;
+    }
+
+    public void activate_result (string result, string[] terms, uint32 timestamp) {
+        activate (timestamp);
+    }
+
+    public void launch_search (string[] terms, uint32 timestamp) {
+    }
+}
+
+} // namespace Clocks
diff --git a/src/window.vala b/src/window.vala
index 2ba016b..fa72fde 100644
--- a/src/window.vala
+++ b/src/window.vala
@@ -132,6 +132,11 @@ public class Window : Gtk.ApplicationWindow {
         ((Clock) stack.visible_child).activate_select_none ();
     }
 
+    public void show_world () {
+        ((World.MainPanel) panels[PanelId.WORLD]).reset_view ();
+        stack.visible_child = panels[PanelId.WORLD];;
+    }
+
     public override bool key_press_event (Gdk.EventKey event) {
         uint keyval;
         if (((Gdk.Event*)(&event))->get_keyval (out keyval) && keyval == Gdk.Key.Escape) {
diff --git a/src/world.vala b/src/world.vala
index 16c373d..c229120 100644
--- a/src/world.vala
+++ b/src/world.vala
@@ -19,7 +19,7 @@
 namespace Clocks {
 namespace World {
 
-private class Item : Object, ContentItem {
+public class Item : Object, ContentItem {
     private static Gdk.Pixbuf? day_pixbuf = Utils.load_image ("day.png");
     private static Gdk.Pixbuf? night_pixbuf = Utils.load_image ("night.png");
 
@@ -177,6 +177,17 @@ private class Item : Object, ContentItem {
         }
         return location != null ? new Item (location) : null;
     }
+
+    // assumes terms have been normalized and casefolded by the caller
+    public bool matches_search (List<string> normalized_terms) {
+        string normalized_name = name.normalize ().casefold ();
+        foreach (string t in normalized_terms) {
+            if (!normalized_name.contains (t)) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
 
 [GtkTemplate (ui = "/org/gnome/clocks/ui/worldlocationdialog.ui")]


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