From 01abacdc80cedd878e44eeb773a7338f3d972bd7 Mon Sep 17 00:00:00 2001
From: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
Date: Fri, 6 Mar 2009 15:01:15 -0500
Subject: [PATCH] Make the search asynchronous

---
 sflphone-gtk/src/calltree.c        |  66 +++---
 sflphone-gtk/src/contactlist/eds.c | 344 +++++++++++++++++++----------
 sflphone-gtk/src/contactlist/eds.h |   7 +
 3 files changed, 274 insertions(+), 143 deletions(-)

diff --git a/sflphone-gtk/src/calltree.c b/sflphone-gtk/src/calltree.c
index 1092acc8c2..61ce18b7d4 100644
--- a/sflphone-gtk/src/calltree.c
+++ b/sflphone-gtk/src/calltree.c
@@ -222,15 +222,46 @@ show_history_tab(GtkToggleToolButton *toggle_tool_button UNUSED,
 	toolbar_update_buttons();
 	gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(histfilter));
 
+}
+
+    static void 
+handler_async_search (GList *hits, gpointer user_data) {
+
+    GtkTreeSelection *sel;
+    GList *i;
+
+    for (i = hits; i != NULL; i = i->next)
+    {
+        Hit *entry;
+        entry = i->data;
+        if (entry)
+        {
+            /* Create entry for business phone information */
+            create_new_entry_in_contactlist (entry->name, entry->phone_business, CONTACT_PHONE_BUSINESS);
+            /* Create entry for home phone information */
+            create_new_entry_in_contactlist (entry->name, entry->phone_home, CONTACT_PHONE_HOME);
+            /* Create entry for mobile phone information */
+            create_new_entry_in_contactlist (entry->name, entry->phone_mobile, CONTACT_PHONE_MOBILE);
+        }
+        free_hit(entry);
+    }
+    g_list_free(hits);
+
+    active_calltree = contacts;
+    gtk_widget_hide(current_calls->tree);
+    gtk_widget_hide(history->tree);
+    gtk_widget_show(contacts->tree);
+
+    sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (active_calltree->view));
+    g_signal_emit_by_name(sel, "changed");
+    toolbar_update_buttons();
+ 
 }
 
   static void
 show_contacts_tab(GtkToggleToolButton *toggle_tool_button UNUSED,
     gpointer  user_data UNUSED)
 {
-  GtkTreeSelection *sel;
-  GList *results;
-  GList *i;
   call_t *j;
   gchar* msg;
 
@@ -250,33 +281,10 @@ show_contacts_tab(GtkToggleToolButton *toggle_tool_button UNUSED,
   call_list_reset(contacts);
 
   // do a synchronous search
-  results = search_sync (gtk_entry_get_text(GTK_ENTRY(filter_entry)), 50);
-
-  for (i = results; i != NULL; i = i->next)
-  {
-    Hit *entry;
-    entry = i->data;
-    if (entry)
-    {
-        /* Create entry for business phone information */
-        create_new_entry_in_contactlist (entry->name, entry->phone_business, CONTACT_PHONE_BUSINESS);
-        /* Create entry for home phone information */
-        create_new_entry_in_contactlist (entry->name, entry->phone_home, CONTACT_PHONE_HOME);
-        /* Create entry for mobile phone information */
-        create_new_entry_in_contactlist (entry->name, entry->phone_mobile, CONTACT_PHONE_MOBILE);
-    }
-    free_hit(entry);
-  }
-  g_list_free(results);
-
-  active_calltree = contacts;
-  gtk_widget_hide(current_calls->tree);
-  gtk_widget_hide(history->tree);
-  gtk_widget_show(contacts->tree);
+  //results = search_sync (gtk_entry_get_text(GTK_ENTRY(filter_entry)), 50);
 
-  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (active_calltree->view));
-  g_signal_emit_by_name(sel, "changed");
-  toolbar_update_buttons();
+  // do an asynchronous search
+   search_async (gtk_entry_get_text (GTK_ENTRY (filter_entry)), 50, &handler_async_search, NULL); 
 }
 
 void create_new_entry_in_contactlist (gchar *contact_name, gchar *contact_phone, contact_type_t type){
diff --git a/sflphone-gtk/src/contactlist/eds.c b/sflphone-gtk/src/contactlist/eds.c
index 6a9cfc8613..f9e64c0e00 100644
--- a/sflphone-gtk/src/contactlist/eds.c
+++ b/sflphone-gtk/src/contactlist/eds.c
@@ -30,12 +30,20 @@
 #include <pango/pango.h>
 #include "eds.h"
 
+typedef struct _Handler_And_Data {
+    SearchAsyncHandler  handler;
+    gpointer            user_data;
+    GList              *hits;
+    int                 max_results_remaining;
+    int                 book_views_remaining;
+} Handler_And_Data;
+
 static GSList *books = NULL;
 
 static EContactField search_fields[] = { E_CONTACT_FULL_NAME, E_CONTACT_PHONE_BUSINESS, E_CONTACT_NICKNAME, 0 };
 static int n_search_fields = G_N_ELEMENTS (search_fields) - 1;
 
-void
+    void
 free_hit (Hit *h)
 {
     g_free (h->name);
@@ -48,154 +56,262 @@ free_hit (Hit *h)
 /**
  * Split a string of tokens separated by whitespace into an array of tokens.
  */
-static GArray *
+    static GArray *
 split_query_string (const gchar *str)
 {
-  GArray *parts = g_array_sized_new (FALSE, FALSE, sizeof (char *), 2);
-  PangoLogAttr *attrs;
-  guint str_len = strlen (str), word_start = 0, i;
-
-  attrs = g_new0 (PangoLogAttr, str_len + 1);
-  /* TODO: do we need to specify a particular language or is NULL ok? */
-  pango_get_log_attrs (str, -1, -1, NULL, attrs, str_len + 1);
-
-  for (i = 0; i < str_len + 1; i++) {
-    char *start_word, *end_word, *word;
-    if (attrs[i].is_word_end) {
-      start_word = g_utf8_offset_to_pointer (str, word_start);
-      end_word = g_utf8_offset_to_pointer (str, i);
-      word  = g_strndup (start_word, end_word - start_word);
-      g_array_append_val (parts, word);
-    }
-    if (attrs[i].is_word_start) {
-      word_start = i;
+    GArray *parts = g_array_sized_new (FALSE, FALSE, sizeof (char *), 2);
+    PangoLogAttr *attrs;
+    guint str_len = strlen (str), word_start = 0, i;
+
+    attrs = g_new0 (PangoLogAttr, str_len + 1);
+    /* TODO: do we need to specify a particular language or is NULL ok? */
+    pango_get_log_attrs (str, -1, -1, NULL, attrs, str_len + 1);
+
+    for (i = 0; i < str_len + 1; i++) {
+        char *start_word, *end_word, *word;
+        if (attrs[i].is_word_end) {
+            start_word = g_utf8_offset_to_pointer (str, word_start);
+            end_word = g_utf8_offset_to_pointer (str, i);
+            word  = g_strndup (start_word, end_word - start_word);
+            g_array_append_val (parts, word);
+        }
+        if (attrs[i].is_word_start) {
+            word_start = i;
+        }
     }
-  }
-  g_free (attrs);
-  return parts;
+    g_free (attrs);
+    return parts;
 }
 
 /**
  * Create a query which looks for the specified string in a contact's full name, email addresses and
  * nick name.
  */
-static EBookQuery*
+    static EBookQuery*
 create_query (const char* s)
 {
-  EBookQuery *query;
-  GArray *parts = split_query_string (s);
-  EBookQuery ***field_queries;
-  EBookQuery **q;
-  guint j;
-  int i;
-
-  q = g_new0 (EBookQuery *, n_search_fields);
-  field_queries = g_new0 (EBookQuery **, n_search_fields);
-
-  for (i = 0; i < n_search_fields; i++) {
-    field_queries[i] = g_new0 (EBookQuery *, parts->len);
-    for (j = 0; j < parts->len; j++) {
-      field_queries[i][j] = e_book_query_field_test (search_fields[i], E_BOOK_QUERY_CONTAINS, g_array_index (parts, gchar *, j));
+    EBookQuery *query;
+    GArray *parts = split_query_string (s);
+    EBookQuery ***field_queries;
+    EBookQuery **q;
+    guint j;
+    int i;
+
+    q = g_new0 (EBookQuery *, n_search_fields);
+    field_queries = g_new0 (EBookQuery **, n_search_fields);
+
+    for (i = 0; i < n_search_fields; i++) {
+        field_queries[i] = g_new0 (EBookQuery *, parts->len);
+        for (j = 0; j < parts->len; j++) {
+            field_queries[i][j] = e_book_query_field_test (search_fields[i], E_BOOK_QUERY_CONTAINS, g_array_index (parts, gchar *, j));
+        }
+        q[i] = e_book_query_and (parts->len, field_queries[i], TRUE);
     }
-    q[i] = e_book_query_and (parts->len, field_queries[i], TRUE);
-  }
-  g_array_free (parts, TRUE);
+    g_array_free (parts, TRUE);
 
-  query = e_book_query_or (n_search_fields, q, TRUE);
+    query = e_book_query_or (n_search_fields, q, TRUE);
 
-  for (i = 0; i < n_search_fields; i++) {
-    g_free (field_queries[i]);
-  }
-  g_free (field_queries);
-  g_free (q);
+    for (i = 0; i < n_search_fields; i++) {
+        g_free (field_queries[i]);
+    }
+    g_free (field_queries);
+    g_free (q);
 
-  return query;
+    return query;
 }
 
 /**
  * Initialize address book
  */
-void
+    void
 init (void)
 {
-  GSList *list, *l;
-  ESourceList *source_list;
-  source_list = e_source_list_new_for_gconf_default ("/apps/evolution/addressbook/sources");
-
-  if (source_list == NULL) {
-    return;
-  }
-  list = e_source_list_peek_groups (source_list);
-
-  for (l = list; l != NULL; l = l->next) {
-    ESourceGroup *group = l->data;
-    GSList *sources = NULL, *m;
-    sources = e_source_group_peek_sources (group);
-    for (m = sources; m != NULL; m = m->next) {
-      ESource *source = m->data;
-      EBook *book = e_book_new (source, NULL);
-      if (book != NULL) {
-        books = g_slist_prepend (books, book);
-        e_book_open(book, TRUE, NULL);
-      }
+    GSList *list, *l;
+    ESourceList *source_list;
+    source_list = e_source_list_new_for_gconf_default ("/apps/evolution/addressbook/sources");
+
+    if (source_list == NULL) {
+        return;
     }
-  }
+    list = e_source_list_peek_groups (source_list);
 
-  g_object_unref (source_list);
+    for (l = list; l != NULL; l = l->next) {
+        ESourceGroup *group = l->data;
+        GSList *sources = NULL, *m;
+        sources = e_source_group_peek_sources (group);
+        for (m = sources; m != NULL; m = m->next) {
+            ESource *source = m->data;
+            EBook *book = e_book_new (source, NULL);
+            if (book != NULL) {
+                books = g_slist_prepend (books, book);
+                e_book_open(book, TRUE, NULL);
+            }
+        }
+    }
+
+    g_object_unref (source_list);
 }
 
 /**
  * Do a synchronized search in EDS address book
  */
-GList *
+    GList *
 search_sync (const char *query,
-             int         max_results)
+        int         max_results)
+{
+    GSList *iter = NULL;
+    GList *contacts = NULL;
+    GList *hits = NULL;
+
+    EBookQuery* book_query = create_query (query);
+    for (iter = books; iter != NULL; iter = iter->next) {
+        if (max_results <= 0) {
+            break;
+        }
+        EBook *book = (EBook *) iter->data;
+        e_book_get_contacts (book, book_query, &contacts, NULL);
+        for (; contacts != NULL; contacts = g_list_next (contacts)) {
+            EContact *contact;
+            Hit *hit;
+            gchar *number;
+
+            contact = E_CONTACT (contacts->data);
+            hit = g_new0 (Hit, 1);
+
+            /* Get business phone information */
+            fetch_information_from_contact (contact, E_CONTACT_PHONE_BUSINESS, &number);
+            hit->phone_business = g_strdup (number);
+
+            /* Get home phone information */
+            fetch_information_from_contact (contact, E_CONTACT_PHONE_HOME, &number);
+            hit->phone_home = g_strdup (number);
+
+            /* Get mobile phone information */
+            fetch_information_from_contact (contact, E_CONTACT_PHONE_MOBILE, &number);
+            hit->phone_mobile = g_strdup (number);
+
+            hit->name = g_strdup ((char*) e_contact_get_const (contact, E_CONTACT_NAME_OR_ORG));
+            if(! hit->name)
+                hit->name = "";
+
+            hits = g_list_append (hits, hit);
+            max_results--;
+            if (max_results <= 0) {
+                break;
+            }
+        }
+    }
+    e_book_query_unref (book_query);
+
+    return hits;
+}
+
+    static void
+view_finish (EBookView *book_view, Handler_And_Data *had)
 {
-  GSList *iter = NULL;
-  GList *contacts = NULL;
-  GList *hits = NULL;
-
-  EBookQuery* book_query = create_query (query);
-  for (iter = books; iter != NULL; iter = iter->next) {
-    if (max_results <= 0) {
-      break;
+    SearchAsyncHandler had_handler = had->handler;
+    GList *had_hits = had->hits;
+    gpointer had_user_data = had->user_data;
+    g_free (had);
+
+    g_return_if_fail (book_view != NULL);
+    g_object_unref (book_view);
+
+    had_handler (had_hits, had_user_data);
+}
+
+    static void
+view_contacts_added_cb (EBookView *book_view, GList *contacts, gpointer user_data)
+{
+    Handler_And_Data *had = (Handler_And_Data *) user_data;
+    if (had->max_results_remaining <= 0) {
+        e_book_view_stop (book_view);
+        had->book_views_remaining--;
+        if (had->book_views_remaining == 0) {
+            view_finish (book_view, had);
+            return;
+        }
     }
-    EBook *book = (EBook *) iter->data;
-    e_book_get_contacts (book, book_query, &contacts, NULL);
     for (; contacts != NULL; contacts = g_list_next (contacts)) {
-      EContact *contact;
-      Hit *hit;
-      gchar *number;
-
-      contact = E_CONTACT (contacts->data);
-      hit = g_new0 (Hit, 1);
-        
-      /* Get business phone information */
-      fetch_information_from_contact (contact, E_CONTACT_PHONE_BUSINESS, &number);
-      hit->phone_business = g_strdup (number);
-    
-      /* Get home phone information */
-      fetch_information_from_contact (contact, E_CONTACT_PHONE_HOME, &number);
-      hit->phone_home = g_strdup (number);
-
-      /* Get mobile phone information */
-      fetch_information_from_contact (contact, E_CONTACT_PHONE_MOBILE, &number);
-      hit->phone_mobile = g_strdup (number);
-
-      hit->name = g_strdup ((char*) e_contact_get_const (contact, E_CONTACT_NAME_OR_ORG));
-      if(! hit->name)
-        hit->name = "";
-
-      hits = g_list_append (hits, hit);
-      max_results--;
-      if (max_results <= 0) {
-        break;
-      }
+        EContact *contact;
+        Hit *hit;
+        gchar *number;
+
+        contact = E_CONTACT (contacts->data);
+        hit = g_new (Hit, 1);
+
+        //hit->pixbuf = pixbuf_from_contact (contact);
+        /* Get business phone information */
+        fetch_information_from_contact (contact, E_CONTACT_PHONE_BUSINESS, &number);
+        hit->phone_business = g_strdup (number);
+
+        /* Get home phone information */
+        fetch_information_from_contact (contact, E_CONTACT_PHONE_HOME, &number);
+        hit->phone_home = g_strdup (number);
+
+        /* Get mobile phone information */
+        fetch_information_from_contact (contact, E_CONTACT_PHONE_MOBILE, &number);
+        hit->phone_mobile = g_strdup (number);
+
+        hit->name = g_strdup ((char*) e_contact_get_const (contact, E_CONTACT_NAME_OR_ORG));
+        if(! hit->name)
+            hit->name = "";
+
+        had->hits = g_list_append (had->hits, hit);
+        had->max_results_remaining--;
+        if (had->max_results_remaining <= 0) {
+            e_book_view_stop (book_view);
+            had->book_views_remaining--;
+            if (had->book_views_remaining == 0) {
+                view_finish (book_view, had);
+            }
+            break;
+        }
+    }
+}
+
+    static void
+view_completed_cb (EBookView *book_view, EBookViewStatus status, gpointer user_data)
+{
+    Handler_And_Data *had = (Handler_And_Data *) user_data;
+    had->book_views_remaining--;
+    if (had->book_views_remaining == 0) {
+        view_finish (book_view, had);
+    }
+}
+
+    void
+search_async (const char         *query,
+        int                 max_results,
+        SearchAsyncHandler  handler,
+        gpointer            user_data)
+{
+    GSList *iter;
+
+    EBookQuery* book_query = create_query (query);
+
+    Handler_And_Data *had = g_new (Handler_And_Data, 1);
+    had->handler = handler;
+    had->user_data = user_data;
+    had->hits = NULL;
+    had->max_results_remaining = max_results;
+    had->book_views_remaining = 0;
+    for (iter = books; iter != NULL; iter = iter->next) {
+        EBook *book = (EBook *) iter->data;
+        EBookView *book_view = NULL;
+        e_book_get_book_view (book, book_query, NULL, max_results, &book_view, NULL);
+        if (book_view != NULL) {
+            had->book_views_remaining++;
+            g_signal_connect (book_view, "contacts_added", (GCallback) view_contacts_added_cb, had);
+            g_signal_connect (book_view, "sequence_complete", (GCallback) view_completed_cb, had);
+            e_book_view_start (book_view);
+        }
+    }
+    if (had->book_views_remaining == 0) {
+        g_free (had);
     }
-  }
-  e_book_query_unref (book_query);
 
-  return hits;
+    e_book_query_unref (book_query);
 }
 
 void fetch_information_from_contact (EContact *contact, EContactField field, gchar **info){
diff --git a/sflphone-gtk/src/contactlist/eds.h b/sflphone-gtk/src/contactlist/eds.h
index 55cbbe8ae9..84a14bb149 100644
--- a/sflphone-gtk/src/contactlist/eds.h
+++ b/sflphone-gtk/src/contactlist/eds.h
@@ -44,8 +44,15 @@ typedef struct _Hit
 
 void free_hit (Hit *h);
 
+typedef void (* SearchAsyncHandler) (GList *hits, gpointer user_data);
+
 void init (void);
 
+void search_async (const char         *query,
+                   int                 max_results,
+                   SearchAsyncHandler  handler,
+                   gpointer            user_data);
+
 GList * search_sync (const char *query, int max_results);
 
 void fetch_information_from_contact (EContact *contact, EContactField field, gchar **info);
-- 
GitLab