[gnome-shell] search: Allow searching for people in overview mode



commit 352fb7b8337a6d9a0fc954e9c123a7e1ac10d643
Author: Morten Mjelva <morten mjelva gmail com>
Date:   Fri Aug 26 16:15:38 2011 +0200

    search: Allow searching for people in overview mode
    
    This adds contacts search to shell, powered by libfolks.
    Changes:
    
    - Add Folks and Gee to the build system
    - ShellContactSystem, a backend in C
    - ContactDisplay, search frontend in JS
    
    https://bugzilla.gnome.org/show_bug.cgi?id=643018

 configure.ac                    |    2 +
 data/theme/gnome-shell.css      |   52 ++++++
 js/Makefile.am                  |    1 +
 js/ui/contactDisplay.js         |  179 ++++++++++++++++++++
 js/ui/overview.js               |    2 +
 src/Makefile.am                 |    4 +-
 src/shell-contact-system.c      |  350 +++++++++++++++++++++++++++++++++++++++
 src/shell-contact-system.h      |   50 ++++++
 tools/build/gnome-shell.modules |   34 ++++
 9 files changed, 673 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 87e1e90..8a335a4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,7 @@ CLUTTER_MIN_VERSION=1.7.5
 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
 GJS_MIN_VERSION=1.29.15
 MUTTER_MIN_VERSION=3.0.0
+FOLKS_MIN_VERSION=0.5.2
 GTK_MIN_VERSION=3.0.0
 GIO_MIN_VERSION=2.29.10
 LIBECAL_MIN_VERSION=2.32.0
@@ -82,6 +83,7 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
 PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
                                gio-unix-2.0 dbus-glib-1 libxml-2.0
                                gtk+-3.0 >= $GTK_MIN_VERSION
+                               folks >= $FOLKS_MIN_VERSION
                                libmutter >= $MUTTER_MIN_VERSION
                                gjs-internals-1.0 >= $GJS_MIN_VERSION
 			       libgnome-menu-3.0 $recorder_modules gconf-2.0
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 313d0b2..fbf9a58 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -673,6 +673,11 @@ StTooltip StLabel {
     -shell-grid-item-size: 118px;
 }
 
+.contact-grid {
+    spacing: 36px;
+    -shell-grid-item-size: 272px; /* 2 * -shell-grid-item-size + spacing */
+}
+
 .icon-grid .overview-icon {
     icon-size: 96px;
 }
@@ -739,11 +744,57 @@ StTooltip StLabel {
     text-align: center;
 }
 
+.contact {
+    width: 272px; /* Same width as two normal results + spacing */
+    height: 118px; /* Aspect ratio = 1.75. Normal US business card ratio */
+    border-radius: 4px;
+    padding: 3px;
+    border: 1px rgba(0,0,0,0);
+    transition-duration: 100;
+}
+
+.contact-content {
+    border-radius: 2px;
+    padding: 8px;
+    width: 232px;
+    height: 84px;
+    background-color: white;
+    color: black;
+    text-align: center;
+}
+
+.contact-icon {
+  border-radius: 4px;
+}
+
+.contact-details {
+  padding: 6px 8px 11px 8px;
+}
+
+.contact-details-alias {
+  font-size: 16px;
+  padding-bottom: 11px;
+}
+
+.contact-details-status {
+  font-size: 11pt;
+}
+
+.contact-details-status-icon {
+  padding-right: 2px;
+}
+
+.contact:hover {
+    background-color: rgba(255,255,255,0.1);
+    transition-duration: 100;
+}
+
 .app-well-app.running > .overview-icon {
     text-shadow: black 0px 2px 2px;
     background-image: url("running-indicator.svg");
 }
 
+.contact:selected,
 .app-well-app:selected > .overview-icon,
 .search-result-content:selected > .overview-icon {
     background-color: rgba(255,255,255,0.33);
@@ -757,6 +808,7 @@ StTooltip StLabel {
     transition-duration: 100;
 }
 
+.contact:focus,
 .app-well-app:focus > .overview-icon,
 .search-result-content:focus > .overview-icon {
     border: 1px solid #cccccc;
diff --git a/js/Makefile.am b/js/Makefile.am
index 25af725..0351f82 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -22,6 +22,7 @@ nobase_dist_js_DATA = 	\
 	ui/autorunManager.js    \
 	ui/boxpointer.js	\
 	ui/calendar.js		\
+	ui/contactDisplay.js \
 	ui/ctrlAltTab.js	\
 	ui/dash.js		\
 	ui/dateMenu.js		\
diff --git a/js/ui/contactDisplay.js b/js/ui/contactDisplay.js
new file mode 100644
index 0000000..10048af
--- /dev/null
+++ b/js/ui/contactDisplay.js
@@ -0,0 +1,179 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Folks = imports.gi.Folks
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+
+const Util = imports.misc.util;
+const IconGrid = imports.ui.iconGrid;
+const Search = imports.ui.search;
+const SearchDisplay = imports.ui.searchDisplay;
+
+const MAX_SEARCH_RESULTS_ROWS = 1;
+const ICON_SIZE = 81;
+
+function launchContact(id) {
+    Util.spawn(['gnome-contacts', '-i', id]);
+}
+
+
+/* This class represents a shown contact search result in the overview */
+function Contact(id) {
+    this._init(id);
+}
+
+Contact.prototype = {
+    _init: function(id) {
+        this.individual = Shell.ContactSystem.get_default().get_individual(id);
+
+        this.actor = new St.Bin({ style_class: 'contact',
+                                  reactive: true,
+                                  track_hover: true });
+
+        let content = new St.BoxLayout( { style_class: 'contact-content',
+                                          vertical: false });
+        this.actor.set_child(content);
+
+        let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
+                                 icon_size: ICON_SIZE,
+                                 style_class: 'contact-icon' });
+        if (this.individual.avatar != null)
+            icon.gicon = this.individual.avatar;
+        else
+            icon.icon_name = 'avatar-default';
+
+        content.add(icon, { x_fill: true,
+                            y_fill: false,
+                            x_align: St.Align.START,
+                            y_align: St.Align.MIDDLE });
+
+        let details = new St.BoxLayout({ style_class: 'contact-details',
+                                         vertical: true });
+        content.add(details, { x_fill: true,
+                               y_fill: false,
+                               x_align: St.Align.START,
+                               y_align: St.Align.MIDDLE });
+
+        let aliasText = this.individual.alias || _("Unknown");
+        let aliasLabel = new St.Label({ text: aliasText,
+                                        style_class: 'contact-details-alias' });
+        details.add(aliasLabel, { x_fill: true,
+                                  y_fill: false,
+                                  x_align: St.Align.START,
+                                  y_align: St.Align.START });
+
+        let presence = this._createPresence(this.individual.presence_type);
+        details.add(presence, { x_fill: false,
+                                y_fill: true,
+                                x_align: St.Align.START,
+                                y_align: St.Align.END });
+    },
+
+    _createPresence: function(presence) {
+        let text;
+        let iconName;
+
+        switch(presence) {
+          case Folks.PresenceType.AVAILABLE:
+            text = _("Available");
+            iconName = 'user-available';
+            break;
+          case Folks.PresenceType.AWAY:
+          case Folks.PresenceType.EXTENDED_AWAY:
+            text = _("Away");
+            iconName = 'user-away';
+            break;
+          case Folks.PresenceType.BUSY:
+            text = _("Busy");
+            iconName = 'user-busy';
+            break;
+          default:
+            text = _("Offline");
+            iconName = 'user-offline';
+          }
+
+        let icon = new St.Icon({ icon_name: iconName,
+                                 icon_type: St.IconType.FULLCOLOR,
+                                 icon_size: 16,
+                                 style_class: 'contact-details-status-icon' });
+        let label = new St.Label({ text: text });
+
+        let box = new St.BoxLayout({ vertical: false,
+                                     style_class: 'contact-details-status' });
+        box.add(icon, { x_fill: true,
+                        y_fill: false,
+                        x_align: St.Align.START,
+                        y_align: St.Align.START });
+
+        box.add(label, { x_fill: true,
+                         y_fill: false,
+                         x_align: St.Align.END,
+                         y_align: St.Align.START });
+
+        return box;
+    },
+
+    createIcon: function(size) {
+        let tc = St.TextureCache.get_default();
+        let icon = this.individual.avatar;
+
+        if (icon != null) {
+            return tc.load_gicon(null, icon, size);
+        } else {
+            return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
+        }
+    },
+};
+
+
+/* Searches for and returns contacts */
+function ContactSearchProvider() {
+    this._init();
+}
+
+ContactSearchProvider.prototype = {
+    __proto__: Search.SearchProvider.prototype,
+
+    _init: function() {
+        Search.SearchProvider.prototype._init.call(this, _("CONTACTS"));
+        this._contactSys = Shell.ContactSystem.get_default();
+    },
+
+    getResultMeta: function(id) {
+        let contact = new Contact(id);
+        return { 'id': id,
+                 'name': contact.alias,
+                 'createIcon': function(size) {
+                         return contact.createIcon(size);
+                 }
+        };
+    },
+
+    getInitialResultSet: function(terms) {
+        return this._contactSys.initial_search(terms);
+    },
+
+    getSubsearchResultSet: function(previousResults, terms) {
+        return this._contactSys.subsearch(previousResults, terms);
+    },
+
+    createResultActor: function(resultMeta, terms) {
+        let contact = new Contact(resultMeta.id);
+        return contact.actor;
+    },
+
+    createResultContainerActor: function() {
+        let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
+                                             xAlign: St.Align.START });
+        grid.actor.style_class = 'contact-grid';
+
+        let actor = new SearchDisplay.GridSearchResults(this, grid);
+        return actor;
+    },
+
+    activateResult: function(id, params) {
+        launchContact(id);
+    }
+};
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 9da431a..9555a71 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
 const Gdk = imports.gi.Gdk;
 
 const AppDisplay = imports.ui.appDisplay;
+const ContactDisplay = imports.ui.contactDisplay;
 const Dash = imports.ui.dash;
 const DND = imports.ui.dnd;
 const DocDisplay = imports.ui.docDisplay;
@@ -211,6 +212,7 @@ Overview.prototype = {
         this._viewSelector.addSearchProvider(new AppDisplay.SettingsSearchProvider());
         this._viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
         this._viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
+        this._viewSelector.addSearchProvider(new ContactDisplay.ContactSearchProvider());
 
         // TODO - recalculate everything when desktop size changes
         this._dash = new Dash.Dash();
diff --git a/src/Makefile.am b/src/Makefile.am
index 73e56df..3ff043f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -102,6 +102,7 @@ shell_public_headers_h =		\
 	shell-app-system.h		\
 	shell-app-usage.h		\
 	shell-arrow.h			\
+	shell-contact-system.h	\
 	shell-doc-system.h		\
 	shell-embedded-window.h		\
 	shell-generic-container.h	\
@@ -137,6 +138,7 @@ libgnome_shell_la_SOURCES =		\
 	shell-app-system.c		\
 	shell-app-usage.c		\
 	shell-arrow.c			\
+	shell-contact-system.c	\
 	shell-doc-system.c		\
 	shell-embedded-window.c		\
 	shell-generic-container.c	\
@@ -269,7 +271,7 @@ libgnome_shell_la_LIBADD =		\
 libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
 
 Shell-0.1.gir: libgnome-shell.la St-1.0.gir
-Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0
+Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0 Folks-0.6
 Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
 Shell_0_1_gir_LIBS = libgnome-shell.la
 Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)
diff --git a/src/shell-contact-system.c b/src/shell-contact-system.c
new file mode 100644
index 0000000..971a164
--- /dev/null
+++ b/src/shell-contact-system.c
@@ -0,0 +1,350 @@
+/* This implements a complete suite for caching and searching contacts in the
+ * Shell. We retrieve contacts from libfolks asynchronously and we search
+ * these for display to the user. */
+
+#include "shell-contact-system.h"
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gee.h>
+#include <clutter/clutter.h>
+#include <folks/folks.h>
+
+#include "shell-global.h"
+#include "shell-util.h"
+#include "st.h"
+
+G_DEFINE_TYPE (ShellContactSystem, shell_contact_system, G_TYPE_OBJECT);
+
+#define ALIAS_PREFIX_MATCH_WEIGHT 100
+#define ALIAS_SUBSTRING_MATCH_WEIGHT 90
+#define IM_PREFIX_MATCH_WEIGHT 10
+#define IM_SUBSTRING_MATCH_WEIGHT 5
+
+
+/* Callbacks */
+
+static void
+prepare_individual_aggregator_cb (GObject       *obj,
+                                  GAsyncResult  *res,
+                                  gpointer      user_data)
+{
+  FolksIndividualAggregator *aggregator = FOLKS_INDIVIDUAL_AGGREGATOR (obj);
+
+  folks_individual_aggregator_prepare_finish (aggregator, res, NULL);
+}
+
+
+/* Internal stuff */
+
+typedef struct {
+    gchar *key;
+    guint weight;
+} ContactSearchResult;
+
+struct _ShellContactSystemPrivate {
+    FolksIndividualAggregator *aggregator;
+};
+
+static void
+shell_contact_system_constructed (GObject *obj)
+{
+  ShellContactSystem *self = SHELL_CONTACT_SYSTEM (obj);
+
+  G_OBJECT_CLASS (shell_contact_system_parent_class)->constructed (obj);
+
+  /* We intentionally do not care about the "individuals-changed" signal, as
+   * we don't intend to update searches after they've been performed.
+   * Therefore, we will simply retrieve the "individuals" property which
+   * represents a snapshot of the individuals in the aggregator.
+   */
+  self->priv->aggregator = folks_individual_aggregator_new ();
+  folks_individual_aggregator_prepare (self->priv->aggregator, prepare_individual_aggregator_cb, NULL);
+}
+
+static void
+shell_contact_system_finalize (GObject *obj)
+{
+  ShellContactSystem *self = SHELL_CONTACT_SYSTEM (obj);
+
+  g_object_unref (self->priv->aggregator);
+
+  G_OBJECT_CLASS (shell_contact_system_parent_class)->finalize (obj);
+}
+
+static void
+shell_contact_system_init (ShellContactSystem *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemPrivate);
+}
+
+static void
+shell_contact_system_class_init (ShellContactSystemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = shell_contact_system_constructed;
+  object_class->finalize = shell_contact_system_finalize;
+
+  g_type_class_add_private (object_class, sizeof (ShellContactSystemPrivate));
+}
+
+/**
+ * normalize_terms:
+ * @terms: (element-type utf8): Input search terms
+ *
+ * Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
+ */
+static GSList *
+normalize_terms (GSList *terms)
+{
+  GSList *normalized_terms = NULL;
+  GSList *iter;
+  for (iter = terms; iter; iter = iter->next)
+    {
+      const char *term = iter->data;
+      normalized_terms = g_slist_prepend (normalized_terms, shell_util_normalize_and_casefold (term));
+    }
+  return normalized_terms;
+}
+
+static guint
+do_match (ShellContactSystem  *self,
+          FolksIndividual     *individual,
+          GSList              *terms)
+{
+  GSList *term_iter;
+  guint weight = 0;
+
+  char *alias = shell_util_normalize_and_casefold (folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)));
+
+  GeeMultiMap *im_addr_map = folks_im_details_get_im_addresses (FOLKS_IM_DETAILS (individual));
+  GeeCollection *im_addrs = gee_multi_map_get_values (im_addr_map);
+  GeeIterator *im_addrs_iter;
+
+  gboolean have_alias_prefix = FALSE;
+  gboolean have_alias_substring = FALSE;
+  
+  gboolean have_im_prefix = FALSE;
+  gboolean have_im_substring = FALSE;
+
+  for (term_iter = terms; term_iter; term_iter = term_iter->next)
+    {
+      const char *term = term_iter->data;
+      const char *p;
+
+      /* Match on alias */
+      p = strstr (alias, term);
+      if (p == alias)
+        have_alias_prefix = TRUE;
+      else if (p != NULL)
+        have_alias_substring = TRUE;
+
+      /* Match on one or more IM addresses */
+      im_addrs_iter = gee_iterable_iterator (GEE_ITERABLE (im_addrs));
+
+      while (gee_iterator_next (im_addrs_iter))
+        {
+          const gchar *addr = gee_iterator_get (im_addrs_iter);
+
+          p = strstr (addr, term);
+          if (p == addr)
+            have_im_prefix = TRUE;
+          else if (p != NULL)
+            have_im_substring = TRUE;
+        }
+
+      g_object_unref (im_addrs_iter);
+    }
+
+    if (have_alias_prefix)
+      weight += ALIAS_PREFIX_MATCH_WEIGHT;
+    else if (have_alias_substring)
+      weight += ALIAS_SUBSTRING_MATCH_WEIGHT;
+
+    if (have_im_prefix)
+      weight += IM_PREFIX_MATCH_WEIGHT;
+    else if (have_im_substring)
+      weight += IM_SUBSTRING_MATCH_WEIGHT;
+
+  g_free (alias);
+  g_object_unref (im_addrs);
+
+  return weight;
+}
+
+static gint
+compare_results (gconstpointer a,
+                 gconstpointer b)
+{
+  ContactSearchResult *first = (ContactSearchResult *) a;
+  ContactSearchResult *second = (ContactSearchResult *) b;
+
+  if (first->weight > second->weight)
+      return 1;
+  else if (first->weight < second->weight)
+      return -1;
+  else
+      return 0;
+}
+
+static void
+free_result (gpointer data,
+             gpointer user_data)
+{
+  g_slice_free (ContactSearchResult, data);
+}
+
+/* modifies and frees @results */
+static GSList *
+sort_and_prepare_results (GSList *results)
+{
+  GSList *iter;
+  GSList *sorted_results = NULL;
+
+  results = g_slist_sort (results, compare_results);
+
+  for (iter = results; iter; iter = iter->next)
+    {
+      ContactSearchResult *result = iter->data;
+      gchar *id = result->key;
+      sorted_results = g_slist_prepend (sorted_results, id);
+    }
+
+  g_slist_foreach (results, (GFunc) free_result, NULL);
+
+  return sorted_results;
+}
+
+
+/* Methods */
+
+/**
+ * shell_contact_system_get_default:
+ *
+ * Return Value: (transfer none): The global #ShellContactSystem singleton
+ */
+ShellContactSystem *
+shell_contact_system_get_default (void)
+{
+  static ShellContactSystem *instance = NULL;
+
+  if (instance == NULL)
+    instance = g_object_new (SHELL_TYPE_CONTACT_SYSTEM, NULL);
+
+  return instance;
+}
+
+/**
+ * shell_contact_system_get_all:
+ * @self: A #ShellContactSystem
+ *
+ * Returns: (transfer none): All individuals
+ */
+GeeMap *
+shell_contact_system_get_all (ShellContactSystem *self)
+{
+  GeeMap *individuals;
+
+  g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
+
+  individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
+
+  return individuals;
+}
+
+/**
+ * shell_contact_system_get_individual:
+ * @self: A #ShellContactSystem
+ * @id: A #gchar with the ID of the FolksIndividual to be returned.
+ *
+ * Returns: (transfer full): A #FolksIndividual or NULL if @id could not be found.
+ */
+FolksIndividual *
+shell_contact_system_get_individual (ShellContactSystem *self,
+                                     gchar              *id)
+{
+  GeeMap *individuals;
+  gpointer key, value;
+
+  key = (gpointer) id;
+
+  g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
+
+  individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
+
+  value = gee_map_get (individuals, key);
+
+  return FOLKS_INDIVIDUAL (value);
+}
+
+/**
+ * shell_contact_system_initial_search:
+ * @shell: A #ShellContactSystem
+ * @terms: (element-type utf8): List of terms, logical AND
+ *
+ * Search through contacts for the given search terms.
+ *
+ * Returns: (transfer container) (element-type utf8): List of contact
+ * identifiers
+ */
+GSList *
+shell_contact_system_initial_search (ShellContactSystem *self,
+                                     GSList             *terms)
+{
+  FolksIndividual *individual;
+  GSList *results = NULL;
+  GeeMap *individuals = NULL;
+  ContactSearchResult *result;
+  GeeMapIterator *iter;
+  gpointer key;
+  guint weight;
+  GSList *normalized_terms = normalize_terms (terms);
+
+  g_return_val_if_fail (SHELL_IS_CONTACT_SYSTEM (self), NULL);
+
+  individuals = folks_individual_aggregator_get_individuals (self->priv->aggregator);
+
+  iter = gee_map_map_iterator (individuals);
+
+  while (gee_map_iterator_next (iter))
+    {
+      individual = gee_map_iterator_get_value (iter);
+      weight = do_match (self, individual, normalized_terms);
+
+      if (weight != 0)
+        {
+          key = gee_map_iterator_get_key (iter);
+
+          result = g_slice_new (ContactSearchResult);
+          result->key = (gchar *) key;
+          result->weight = weight;
+
+          results = g_slist_append (results, result);
+        }
+
+      g_object_unref (individual);
+    }
+
+  return sort_and_prepare_results (results);
+}
+
+/**
+ * shell_contact_system_subsearch:
+ * @shell: A #ShellContactSystem
+ * @previous_results: (element-type utf8): List of previous results
+ * @terms: (element-type utf8): List of terms, logical AND
+ *
+ * Search through a previous result set; for more information see
+ * js/ui/search.js.
+ *
+ * Returns: (transfer container) (element-type utf8): List of contact
+ * identifiers
+ */
+GSList *
+shell_contact_system_subsearch (ShellContactSystem  *self,
+                                GSList              *previous_results,
+                                GSList              *terms)
+{
+  return shell_contact_system_initial_search (self, terms);
+}
diff --git a/src/shell-contact-system.h b/src/shell-contact-system.h
new file mode 100644
index 0000000..def382d
--- /dev/null
+++ b/src/shell-contact-system.h
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __SHELL_CONTACT_SYSTEM_H__
+#define __SHELL_CONTACT_SYSTEM_H__
+
+#include <clutter/clutter.h>
+#include <gio/gio.h>
+#include <folks/folks.h>
+
+#define SHELL_TYPE_CONTACT_SYSTEM             (shell_contact_system_get_type ())
+#define SHELL_CONTACT_SYSTEM(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystem))
+#define SHELL_CONTACT_SYSTEM_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemClass))
+#define SHELL_IS_CONTACT_SYSTEM(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_CONTACT_SYSTEM))
+#define SHELL_IS_CONTACT_SYSTEM_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_CONTACT_SYSTEM))
+#define SHELL_CONTACT_SYSTEM_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_CONTACT_SYSTEM, ShellContactSystemClass))
+
+typedef struct _ShellContactSystem ShellContactSystem;
+typedef struct _ShellContactSystemClass ShellContactSystemClass;
+typedef struct _ShellContactSystemPrivate ShellContactSystemPrivate;
+
+struct _ShellContactSystem
+{
+  GObject parent;
+
+  ShellContactSystemPrivate *priv;
+};
+
+struct _ShellContactSystemClass
+{
+  GObjectClass parent_class;
+};
+
+GType shell_contact_system_get_type (void) G_GNUC_CONST;
+
+/* Methods */
+
+ShellContactSystem * shell_contact_system_get_default (void);
+
+GeeMap *shell_contact_system_get_all (ShellContactSystem *self);
+
+FolksIndividual *shell_contact_system_get_individual (ShellContactSystem  *self,
+                                                      gchar               *id);
+
+GSList * shell_contact_system_initial_search (ShellContactSystem  *shell,
+                                              GSList              *terms);
+
+GSList * shell_contact_system_subsearch (ShellContactSystem *shell,
+                                         GSList             *previous_results,
+                                         GSList             *terms);
+
+#endif /* __SHELL_CONTACT_SYSTEM_H__ */
diff --git a/tools/build/gnome-shell.modules b/tools/build/gnome-shell.modules
index 91b79b2..51cef32 100644
--- a/tools/build/gnome-shell.modules
+++ b/tools/build/gnome-shell.modules
@@ -16,6 +16,38 @@
   <repository type="git" name="colord.gitorious.org"
       href="git://gitorious.org/colord/master.git"/>
 
+  <tarball id="libgee" version="0.6.0">
+    <pkg-config/>
+    <source hash="sha256:e586678d0a88637abeaaf850b62231000772e79ea6d9c4b45dc3cea99f778a7a" href="http://download.gnome.org/sources/libgee/0.6/libgee-0.6.0.tar.bz2"; md5sum="4eb513b23ab6ea78884989518a4acf6f" size="477609"/>
+    <dependencies>
+      <dep package="glib"/>
+    </dependencies>
+    <suggests>
+      <dep package="gobject-introspection"/>
+    </suggests>
+  </tarball>
+
+  <tarball id="folks" version="0.6.0">
+    <source hash="sha256:50539da0b42564887bbce67d3a4107f3a7dbd8c6ae3b5d3a2d422b4f75a2b519" href="http://ftp.gnome.org/pub/GNOME/sources/folks/0.6/folks-0.6.0.tar.bz2"; md5sum="4d5a3619c8b6e0714d02941c92826160" size="1261916"/>
+    <dependencies>
+      <dep package="libgee"/>
+      <dep package="vala"/>
+      <dep package="gobject-introspection"/>
+      <dep package="telepathy-glib"/>
+      <dep package="evolution-data-server"/>
+    </dependencies>
+  </tarball>
+
+  <tarball id="gnome-contacts">
+    <source hash="sha256:db52d4521e5d3335b5bf77ce5d1a7ec2cdea9ebf37ce12ddadda0503a2968cf9" href="http://ftp.gnome.org/pub/GNOME/sources/gnome-contacts/0.1/gnome-contacts-0.1.2.tar.bz2"; size="377930"/>
+    <dependencies>
+      <dep package="folks"/>
+      <dep package="glib"/>
+      <dep package="gtk3"/>
+      <dep package="vala"/>
+    </dependencies>
+  </tarball>
+
   <autotools id="gobject-introspection">
     <branch repo="git.gnome.org" module="gobject-introspection"/>
     <dependencies>
@@ -320,8 +352,10 @@
         <dep package="gjs"/>
         <dep package="caribou"/>
         <dep package="dconf"/>
+        <dep package="folks"/>
         <dep package="gconf"/>
         <dep package="glib"/>
+        <dep package="gnome-contacts"/>
         <dep package="gnome-menus"/>
         <dep package="gnome-desktop-3"/>
         <dep package="gsettings-desktop-schemas"/>



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