[gnome-contacts] Add search provider for GNOME Shell



commit 19751af1b8d810d0d1ca1772dff113a6766bcbeb
Author: Florian MÃllner <fmuellner gnome org>
Date:   Tue Jun 19 23:39:06 2012 +0200

    Add search provider for GNOME Shell
    
    GNOME Shell provides a DBus interface for external search provider
    implementations; add a small auto-activated service which implements
    that interface, to replace the Shell's built-in contact search with
    results provided directly by GNOME Contacts.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=679002

 configure.ac                                      |    4 +-
 data/Makefile.am                                  |   20 +++
 data/gnome-contacts-search-provider.ini.in        |    6 +
 data/org.gnome.Contacts.SearchProvider.service.in |    3 +
 po/POTFILES.in                                    |    1 +
 src/Makefile.am                                   |   13 ++
 src/contacts-contact.vala                         |   44 ++++++-
 src/contacts-shell-search-provider.vala           |  155 +++++++++++++++++++++
 8 files changed, 243 insertions(+), 3 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a9fc0b3..e88a8db 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,7 @@ AC_CONFIG_FILES([Makefile
 
 LT_INIT
 AC_PROG_CC
-AM_PROG_VALAC([0.14.0])
+AM_PROG_VALAC([0.17.2])
 AC_PROG_INSTALL
 
 GLIB_GSETTINGS
@@ -48,7 +48,7 @@ pkg_modules="gtk+-3.0 >= 3.4.0
 	     "
 PKG_CHECK_MODULES(CONTACTS, [$pkg_modules])
 
-CONTACTS_PACKAGES="--pkg gtk+-3.0 --pkg gio-2.0 --pkg folks --pkg folks-telepathy --pkg folks-eds --pkg libnotify"
+CONTACTS_PACKAGES="--pkg gtk+-3.0 --pkg gio-2.0 --pkg gio-unix-2.0 --pkg folks --pkg folks-telepathy --pkg folks-eds --pkg libnotify"
 AC_SUBST(CONTACTS_PACKAGES)
 
 gstreamers_modules="gdk-x11-3.0
diff --git a/data/Makefile.am b/data/Makefile.am
index 287c1b1..2576caf 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -3,12 +3,32 @@ NULL=
 desktopdir = $(datadir)/applications
 desktop_in_files = gnome-contacts.desktop.in
 desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+
+searchproviderdir = $(datadir)/gnome-shell/search-providers
+searchprovider_DATA = gnome-contacts-search-provider.ini
+searchprovider_in_files = gnome-contacts-search-provider.ini.in
+
+%.ini: %.ini.in
+	LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
+
 @INTLTOOL_DESKTOP_RULE@
 
+service_in_files = org.gnome.Contacts.SearchProvider.service.in
+
+servicedir = $(datadir)/dbus-1/services
+service_DATA = $(service_in_files:.service.in=.service)
+
+%.service: %.service.in Makefile
+	$(AM_V_GEN) [ -d $(@D) ] || $(mkdir_p) $(@D) ; \
+	            sed -e "s|\ libexecdir\@|$(libexecdir)|" $< > $  tmp && mv $  tmp $@
+
 EXTRA_DIST = \
 	gnome-contacts.desktop.in.in	\
+	$(searchprovider_DATA) 	\
+	$(service_in_files)  \
 	$(NULL)
 
+CLEANFILES = $(service_DATA)
 DISTCLEANFILES = \
 	gnome-contacts.desktop \
 	gnome-contacts.desktop.in
diff --git a/data/gnome-contacts-search-provider.ini.in b/data/gnome-contacts-search-provider.ini.in
new file mode 100644
index 0000000..9e8da46
--- /dev/null
+++ b/data/gnome-contacts-search-provider.ini.in
@@ -0,0 +1,6 @@
+[Shell Search Provider]
+_Title=Gnome Contacts
+Icon=x-office-address-book
+DesktopId=gnome-contacts.desktop
+BusName=org.gnome.Contacts.SearchProvider
+ObjectPath=/org/gnome/Contacts/SearchProvider
diff --git a/data/org.gnome.Contacts.SearchProvider.service.in b/data/org.gnome.Contacts.SearchProvider.service.in
new file mode 100644
index 0000000..6f45237
--- /dev/null
+++ b/data/org.gnome.Contacts.SearchProvider.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Contacts.SearchProvider
+Exec= libexecdir@/gnome-contacts-search-provider
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2ab8b5f..38f0a64 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,3 +1,4 @@
+data/gnome-contacts-search-provider.ini.in
 data/gnome-contacts.desktop.in.in
 [type: gettext/glade]src/app-menu.ui
 src/contacts-app.vala
diff --git a/src/Makefile.am b/src/Makefile.am
index 66c6e68..6ece706 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,6 +75,19 @@ gnome_contacts_SOURCES += \
 	$(NULL)
 endif
 
+libexec_PROGRAMS = gnome-contacts-search-provider
+
+gnome_contacts_search_provider_SOURCES = \
+	contacts-contact.vala \
+	contacts-esd-setup.c \
+	contacts-shell-search-provider.vala \
+	contacts-store.vala \
+	contacts-types.vala \
+	contacts-utils.vala \
+	$(NULL)
+
+gnome_contacts_search_provider_LDADD = $(CONTACTS_LIBS)
+
 CLEANFILES = \
 	$(vala_sources:.vala=.c) \
 	$(gsettings_SCHEMAS) \
diff --git a/src/contacts-contact.vala b/src/contacts-contact.vala
index 0bcfbae..9520f5d 100644
--- a/src/contacts-contact.vala
+++ b/src/contacts-contact.vala
@@ -142,6 +142,45 @@ public class Contacts.Contact : GLib.Object  {
     }
   }
 
+  public Icon? serializable_avatar_icon {
+    get {
+      if (individual.avatar != null && individual.avatar.to_string () != null)
+        return individual.avatar;
+
+      return null;
+    }
+  }
+
+  private Variant? _avatar_icon_data;
+  public Variant? avatar_icon_data {
+    get {
+      if (individual.avatar == null)
+        return null;
+
+      if (individual.avatar.to_string () != null)
+        return null;
+
+      if (_avatar_icon_data == null) {
+
+        if (small_avatar == null)
+          return null;
+
+        var pixel_data = Variant.new_from_data (VariantType.BYTESTRING,
+                                                small_avatar.get_pixels_with_length (),
+                                                true, small_avatar);
+        _avatar_icon_data = new Variant ("(iiibii ay)",
+                                         small_avatar.get_width (),
+                                         small_avatar.get_height (),
+                                         small_avatar.get_rowstride (),
+                                         small_avatar.get_has_alpha (),
+                                         small_avatar.get_bits_per_sample (),
+                                         small_avatar.get_n_channels (),
+                                         pixel_data);
+      }
+      return _avatar_icon_data;
+    }
+  }
+
   public string display_name {
     get {
       unowned string? name = individual.full_name;
@@ -436,6 +475,7 @@ public class Contacts.Contact : GLib.Object  {
       connect_persona (p);
     }
     _small_avatar = null;
+    _avatar_icon_data = null;
     individual.notify.connect(notify_cb);
     queue_changed (true);
   }
@@ -820,8 +860,10 @@ public class Contacts.Contact : GLib.Object  {
   }
 
   private void notify_cb (ParamSpec pspec) {
-    if (pspec.get_name () == "avatar")
+    if (pspec.get_name () == "avatar") {
       _small_avatar = null;
+      _avatar_icon_data = null;
+    }
     queue_changed (false);
   }
 
diff --git a/src/contacts-shell-search-provider.vala b/src/contacts-shell-search-provider.vala
new file mode 100644
index 0000000..0f829fc
--- /dev/null
+++ b/src/contacts-shell-search-provider.vala
@@ -0,0 +1,155 @@
+using Gee;
+
+[DBus (name = "org.gnome.Shell.SearchProvider")]
+public class Contacts.SearchProvider : Object {
+  SearchProviderApp app;
+  Store store;
+  Gee.HashMap<string, Contact> contacts_map;
+  private uint next_id;
+
+  public SearchProvider (SearchProviderApp app) {
+    this.app = app;
+    ensure_eds_accounts ();
+    store = new Store ();
+    contacts_map = new Gee.HashMap<string, Contact> ();
+    next_id = 0;
+
+    store.changed.connect ( (c) => {
+      contacts_map.set(c.get_data<string> ("search-id"), c);
+    });
+    store.added.connect ( (c) => {
+      var id = next_id++.to_string ();
+      c.set_data ("search-id", id);
+      contacts_map.set(id, c);
+    });
+    store.removed.connect ( (c) => {
+      contacts_map.unset(c.get_data<string> ("search-id"));
+    });
+  }
+
+  private static int compare_contacts (Contact a, Contact b) {
+    int a_prio = a.is_main ? 0 : -1;
+    int b_prio = b.is_main ? 0 : -1;
+
+    if (a_prio > b_prio)
+        return -1;
+    if (a_prio < b_prio)
+        return 1;
+
+    if (is_set (a.display_name) && is_set (b.display_name))
+      return a.display_name.collate (b.display_name);
+
+    // Sort empty names last
+    if (is_set (a.display_name))
+      return -1;
+    if (is_set (b.display_name))
+      return 1;
+
+    return 0;
+  }
+
+  private string[] do_search (string[] terms) {
+    app.hold ();
+    string[] normalized_terms =
+        Utils.canonicalize_for_search (string.joinv(" ", terms)).split(" ");
+
+    var matches = new ArrayList<Contact> ();
+    foreach (var c in store.get_contacts ()) {
+      if (c.is_hidden)
+          continue;
+
+      if (c.contains_strings (normalized_terms))
+          matches.add (c);
+    }
+
+    matches.sort((CompareFunc<Contact>) compare_contacts);
+
+    var results = new string[matches.size];
+    for (int i = 0; i < matches.size; i++)
+        results[i] = matches[i].get_data ("search-id");
+    app.release ();
+    return results;
+  }
+
+  public string[] GetInitialResultSet (string[] terms) {
+    return do_search (terms);
+  }
+
+  public string[] GetSubsearchResultSet (string[] previous_results,
+                                         string[] new_terms) {
+    return do_search (new_terms);
+  }
+
+  public HashTable<string, Variant>[] GetResultMetas (string[] ids) {
+    app.hold ();
+    var results = new ArrayList<HashTable> ();
+    foreach (var id in ids) {
+      var contact = contacts_map.get (id);
+
+      if (contact == null)
+        continue;
+
+      var meta = new HashTable<string, Variant> (str_hash, str_equal);
+      meta.insert ("id", new Variant.string (id));
+
+      meta.insert ("name", new Variant.string (contact.display_name));
+
+      if (contact.serializable_avatar_icon != null)
+        meta.insert ("gicon", new Variant.string (contact.serializable_avatar_icon.to_string ()));
+      else if (contact.avatar_icon_data != null)
+        meta.insert ("icon-data", contact.avatar_icon_data);
+      else
+        meta.insert ("gicon", new Variant.string (new ThemedIcon ("avatar-default").to_string ()));
+      results.add (meta);
+    }
+    app.release ();
+    return results.to_array ();
+  }
+
+  public void ActivateResult (string search_id) {
+    app.hold ();
+
+    var contact = contacts_map.get (search_id);
+
+    if (contact == null)
+      return;
+
+    string id = contact.individual.id;
+    try {
+      if (!Process.spawn_command_line_async ("gnome-contacts -i " + id))
+        stderr.printf ("Failed to launch contact with id '%s'\n", id);
+    } catch (SpawnError e) {
+      stderr.printf ("Failed to launch contact with id '%s'\n", id);
+    }
+
+    app.release ();
+  }
+}
+
+public class Contacts.SearchProviderApp : GLib.Application {
+  public SearchProviderApp () {
+    Object (application_id: "org.gnome.Contacts.SearchProvider",
+            flags: ApplicationFlags.IS_SERVICE,
+            inactivity_timeout: 10000);
+  }
+
+  public override bool dbus_register (GLib.DBusConnection connection, string object_path) {
+    try {
+      connection.register_object (object_path, new SearchProvider (this));
+    } catch (IOError error) {
+      stderr.printf ("Could not register service: %s", error.message);
+      quit ();
+    }
+    return true;
+  }
+
+  public override void startup () {
+    if (Environment.get_variable ("CONTACTS_SEARCH_PROVIDER_PERSIST") != null)
+      hold ();
+    base.startup ();
+  }
+}
+
+int main () {
+  return new Contacts.SearchProviderApp ().run ();
+}



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