[gtk+] Rewrite search to be more similar to nautilus



commit 43e1eea1bb216985132f6193ae3bbf38ce533942
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat May 16 00:37:07 2015 -0400

    Rewrite search to be more similar to nautilus
    
    The main advantage here is that this code works for remote
    locations as well.

 gtk/gtkquery.c              |   58 ++++++++++-
 gtk/gtkquery.h              |    3 +
 gtk/gtksearchenginesimple.c |  239 ++++++++++++-------------------------------
 3 files changed, 124 insertions(+), 176 deletions(-)
---
diff --git a/gtk/gtkquery.c b/gtk/gtkquery.c
index 63830ca..7afcb0f 100644
--- a/gtk/gtkquery.c
+++ b/gtk/gtkquery.c
@@ -24,11 +24,12 @@
 
 #include "gtkquery.h"
 
-struct _GtkQueryPrivate 
+struct _GtkQueryPrivate
 {
   gchar *text;
   gchar *location_uri;
   GList *mime_types;
+  gchar **words;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkQuery, _gtk_query, G_TYPE_OBJECT)
@@ -37,10 +38,12 @@ static void
 finalize (GObject *object)
 {
   GtkQuery *query;
-  
+
   query = GTK_QUERY (object);
-  
+
   g_free (query->priv->text);
+  g_free (query->priv->location_uri);
+  g_strfreev (query->priv->words);
 
   G_OBJECT_CLASS (_gtk_query_parent_class)->finalize (object);
 }
@@ -79,6 +82,9 @@ _gtk_query_set_text (GtkQuery    *query,
 {
   g_free (query->priv->text);
   query->priv->text = g_strdup (text);
+
+  g_strfreev (query->priv->words);
+  query->priv->words = NULL;
 }
 
 gchar *
@@ -136,3 +142,49 @@ _gtk_query_add_mime_type (GtkQuery    *query,
                                            g_strdup (mime_type));
 }
 
+static gchar *
+prepare_string_for_compare (const gchar *string)
+{
+  gchar *normalized, *res;
+
+  normalized = g_utf8_normalize (string, -1, G_NORMALIZE_NFD);
+  res = g_utf8_strdown (normalized, -1);
+  g_free (normalized);
+
+  return res;
+}
+
+gboolean
+gtk_query_matches_string (GtkQuery    *query,
+                          const gchar *string)
+{
+  gchar *prepared;
+  gboolean found;
+  gint i;
+
+  if (!query->priv->text)
+    return FALSE;
+
+  if (!query->priv->words)
+    {
+      prepared = prepare_string_for_compare (query->priv->text);
+      query->priv->words = g_strsplit (prepared, " ", -1);
+      g_free (prepared);
+    }
+
+  prepared = prepare_string_for_compare (string);
+
+  found = TRUE;
+  for (i = 0; query->priv->words[i]; i++)
+    {
+      if (strstr (prepared, query->priv->words[i]) == NULL)
+        {
+          found = FALSE;
+          break;
+        }
+    }
+
+  g_free (prepared);
+
+  return found;
+}
diff --git a/gtk/gtkquery.h b/gtk/gtkquery.h
index f7b0012..8633373 100644
--- a/gtk/gtkquery.h
+++ b/gtk/gtkquery.h
@@ -67,6 +67,9 @@ void      _gtk_query_set_mime_types (GtkQuery    *query,
 void      _gtk_query_add_mime_type  (GtkQuery    *query, 
                                     const gchar *mime_type);
 
+gboolean  gtk_query_matches_string (GtkQuery    *query,
+                                    const gchar *string);
+
 G_END_DECLS
 
 #endif /* __GTK_QUERY_H__ */
diff --git a/gtk/gtksearchenginesimple.c b/gtk/gtksearchenginesimple.c
index 6df8027..613da2c 100644
--- a/gtk/gtksearchenginesimple.c
+++ b/gtk/gtksearchenginesimple.c
@@ -35,16 +35,14 @@
 typedef struct
 {
   GtkSearchEngineSimple *engine;
+  GCancellable *cancellable;
 
-  gchar *path;
-  gchar **words;
-  GList *found_list;
+  GQueue *directories;
 
   gint n_processed_files;
-  GList *uri_hits;
+  GList *hits;
 
-  /* accessed on both threads: */
-  volatile gboolean cancelled;
+  GtkQuery *query;
 } SearchThreadData;
 
 
@@ -77,7 +75,7 @@ gtk_search_engine_simple_dispose (GObject *object)
 
   if (priv->active_search)
     {
-      priv->active_search->cancelled = TRUE;
+      g_cancellable_cancel (priv->active_search->cancellable);
       priv->active_search = NULL;
     }
 
@@ -89,25 +87,22 @@ search_thread_data_new (GtkSearchEngineSimple *engine,
                        GtkQuery              *query)
 {
   SearchThreadData *data;
-  char *text, *lower, *uri;
+  const gchar *uri;
+  GFile *location;
 
   data = g_new0 (SearchThreadData, 1);
 
   data->engine = g_object_ref (engine);
+  data->directories = g_queue_new ();
+  data->query = g_object_ref (query);
   uri = _gtk_query_get_location (query);
   if (uri != NULL)
-    {
-      data->path = g_filename_from_uri (uri, NULL, NULL);
-      g_free (uri);
-    }
-  if (data->path == NULL)
-    data->path = g_strdup (g_get_home_dir ());
+    location = g_file_new_for_uri (uri);
+  else
+    location = g_file_new_for_path (g_get_home_dir ());
+  g_queue_push_tail (data->directories, location);
 
-  text = _gtk_query_get_text (query);
-  lower = g_utf8_casefold (text, -1);
-  data->words = g_strsplit (lower, " ", -1);
-  g_free (text);
-  g_free (lower);
+  data->cancellable = g_cancellable_new ();
 
   return data;
 }
@@ -115,9 +110,12 @@ search_thread_data_new (GtkSearchEngineSimple *engine,
 static void
 search_thread_data_free (SearchThreadData *data)
 {
+  g_queue_foreach (data->directories, (GFunc)g_object_unref, NULL);
+  g_queue_free (data->directories);
+  g_object_unref (data->cancellable);
+  g_object_unref (data->query);
   g_object_unref (data->engine);
-  g_free (data->path);
-  g_strfreev (data->words);
+
   g_free (data);
 }
 
@@ -128,7 +126,7 @@ search_thread_done_idle (gpointer user_data)
 
   data = user_data;
 
-  if (!data->cancelled)
+  if (!g_cancellable_is_cancelled (data->cancellable))
     _gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine));
 
   data->engine->priv->active_search = NULL;
@@ -146,15 +144,10 @@ typedef struct
 static gboolean
 search_thread_add_hits_idle (gpointer user_data)
 {
-  SearchHits *hits;
-
-  hits = user_data;
+  SearchHits *hits = user_data;
 
-  if (!hits->thread_data->cancelled)
-    {
-      _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (hits->thread_data->engine),
-                                   hits->uris);
-    }
+  if (!g_cancellable_is_cancelled (hits->thread_data->cancellable))
+    _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (hits->thread_data->engine), hits->uris);
 
   g_list_free_full (hits->uris, g_free);
   g_free (hits);
@@ -169,183 +162,83 @@ send_batch (SearchThreadData *data)
 
   data->n_processed_files = 0;
 
-  if (data->uri_hits)
+  if (data->hits)
     {
       guint id;
 
       hits = g_new (SearchHits, 1);
-      hits->uris = data->uri_hits;
+      hits->uris = data->hits;
       hits->thread_data = data;
 
       id = gdk_threads_add_idle (search_thread_add_hits_idle, hits);
       g_source_set_name_by_id (id, "[gtk+] search_thread_add_hits_idle");
     }
 
-  data->uri_hits = NULL;
+  data->hits = NULL;
 }
 
-typedef gboolean (VisitFunc) (const char *fpath, gpointer user_data);
-
-static gboolean process_dir    (GFile *dir, GList **new_root_dirs, VisitFunc func, gpointer user_data);
-static GList *  process_dirs   (GList *root_dirs, VisitFunc func, gpointer user_data);
-static void     breadth_search (gchar *root, VisitFunc func, gpointer user_data);
-
 static void
-breadth_search (gchar *root, VisitFunc func, gpointer user_data)
-{
-  GList *subdirs = NULL;
-
-  subdirs = g_list_prepend (subdirs, g_file_new_for_path (root));
-
-  while (subdirs)
-    subdirs = process_dirs (subdirs, func, user_data);
-}
-
-static GList *
-process_dirs (GList *root_dirs, VisitFunc func, gpointer user_data)
+visit_directory (GFile *dir, SearchThreadData *data)
 {
-  SearchThreadData *data = (SearchThreadData*) user_data;
-  GList *new_root_dirs = NULL;
-  GList *root;
-  gboolean keep_going = TRUE;
-
-  for (root = root_dirs; root; root = g_list_next (root))
-    {
-      GFile *dir = (GFile *) root->data;
-
-      /* Even if cancelled or stopped, we keep looping to unref the dirs */
-      if (keep_going && !data->cancelled)
-        keep_going = process_dir (dir, &new_root_dirs, func, user_data);
-
-      g_object_unref (dir);
-    }
-
-  if (!keep_going || data->cancelled)
-    {
-      g_list_free_full (new_root_dirs, g_object_unref);
-      new_root_dirs = NULL;
-    }
-
-  g_list_free (root_dirs);
-
-  return g_list_reverse (new_root_dirs);
-}
+  GFileEnumerator *enumerator;
+  GFileInfo *info;
+  GFile *child;
+  const gchar *display_name;
+
+  enumerator = g_file_enumerate_children (dir,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME ","
+                                          G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
+                                          G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+                                          G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          data->cancellable, NULL);
+  if (enumerator == NULL)
+    return;
 
-static gboolean
-process_dir (GFile *dir, GList **new_root_dirs, VisitFunc func, gpointer user_data)
-{
-  GFileEnumerator *direnum;
-  gchar *dirpath;
-  SearchThreadData *data = (SearchThreadData*) user_data;
-
-  direnum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME ","
-                                       G_FILE_ATTRIBUTE_STANDARD_TYPE ","
-                                       G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
-                                       G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
-                                       G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                       NULL, NULL);
-  if (direnum == NULL || data->cancelled)
-    return FALSE;
-
-  dirpath = g_file_get_path (dir);
-
-  while (TRUE)
+  while (g_file_enumerator_iterate (enumerator, &info, &child, data->cancellable, NULL))
     {
-      GFileInfo *info;
-      gchar *fullname;
-      const gchar *basename;
-      gboolean keep_going;
+      if (info == NULL)
+        break;
 
-      keep_going = g_file_enumerator_iterate (direnum, &info, NULL, NULL, NULL);
-
-      if (!keep_going || info == NULL || data->cancelled)
-        {
-          g_object_unref (direnum);
-          g_free (dirpath);
-          return keep_going;
-        }
+      display_name = g_file_info_get_display_name (info);
+      if (display_name == NULL)
+        continue;
 
       if (g_file_info_get_is_hidden (info))
         continue;
 
-      basename = g_file_info_get_name (info);
-      fullname = g_build_filename (dirpath, basename, NULL);
-
-      keep_going = func ((const char *) fullname, user_data);
-
-      g_free (fullname);
+      if (gtk_query_matches_string (data->query, display_name))
+        data->hits = g_list_prepend (data->hits, g_file_get_uri (child));
 
-      if (!keep_going)
-        {
-          g_object_unref (direnum);
-          g_free (dirpath);
-          return FALSE;
-        }
+      data->n_processed_files++;
+      if (data->n_processed_files > BATCH_SIZE)
+        send_batch (data);
 
-      if (g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
-        continue;
-
-      *new_root_dirs = g_list_prepend (*new_root_dirs,
-                                       g_file_get_child (dir, basename));
+      if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+        g_queue_push_tail (data->directories, g_object_ref (child));
     }
-}
 
-static int
-search_visit_func (const char *fpath, gpointer user_data)
-{
-  SearchThreadData *data;
-  gint i;
-  gchar *display_basename;
-  gchar *lower_name;
-  gchar *uri;
-  gboolean hit;
-
-  data = (SearchThreadData*) user_data;
-
-  if (data->cancelled)
-    return FALSE;
-
-  display_basename = g_filename_display_basename (fpath);
-  lower_name = g_utf8_casefold (display_basename, -1);
-  g_free (display_basename);
-
-  hit = TRUE;
-  for (i = 0; data->words[i] != NULL; i++)
-    {
-      if (strstr (lower_name, data->words[i]) == NULL)
-        {
-          hit = FALSE;
-          break;
-        }
-    }
-
-  g_free (lower_name);
-
-  if (hit)
-    {
-      uri = g_filename_to_uri (fpath, NULL, NULL);
-      data->uri_hits = g_list_prepend (data->uri_hits, uri);
-    }
-
-  data->n_processed_files++;
-
-  if (data->n_processed_files > BATCH_SIZE)
-    send_batch (data);
-
-  return TRUE;
+  g_object_unref (enumerator);
 }
 
 static gpointer
 search_thread_func (gpointer user_data)
 {
-  guint id;
   SearchThreadData *data;
+  GFile *dir;
+  guint id;
 
   data = user_data;
 
-  breadth_search (data->path, search_visit_func, data);
+  while (!g_cancellable_is_cancelled (data->cancellable) &&
+         (dir = g_queue_pop_head (data->directories)) != NULL)
+    {
+      visit_directory (dir, data);
+      g_object_unref (dir);
+    }
 
-  send_batch (data);
+  if (!g_cancellable_is_cancelled (data->cancellable))
+    send_batch (data);
 
   id = gdk_threads_add_idle (search_thread_done_idle, data);
   g_source_set_name_by_id (id, "[gtk+] search_thread_done_idle");
@@ -383,7 +276,7 @@ gtk_search_engine_simple_stop (GtkSearchEngine *engine)
 
   if (simple->priv->active_search != NULL)
     {
-      simple->priv->active_search->cancelled = TRUE;
+      g_cancellable_cancel (simple->priv->active_search->cancellable);
       simple->priv->active_search = NULL;
     }
 }


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