[gnome-control-center] shell: Make search results sorting smarter



commit 13abc75273922e01b2987d483c1bb4a20de44d86
Author: Rui Matos <tiagomatos gmail com>
Date:   Sat Apr 26 23:11:47 2014 +0200

    shell: Make search results sorting smarter
    
    Instead of just sorting by the name the sort order will now be:
    
    1. Panels whose name match a search term
    2. Panels whose keywords match the most search terms
    3. Panels whose description match the most search terms
    4. The remaining panels by name
    
    https://bugzilla.gnome.org/show_bug.cgi?id=729027

 search-provider/cc-search-provider.c |    4 +
 shell/cc-shell-model.c               |  238 +++++++++++++++++++++++++++++++++-
 shell/cc-shell-model.h               |    6 +
 shell/cc-window.c                    |    2 +
 4 files changed, 248 insertions(+), 2 deletions(-)
---
diff --git a/search-provider/cc-search-provider.c b/search-provider/cc-search-provider.c
index 19ac9ee..ab081d0 100644
--- a/search-provider/cc-search-provider.c
+++ b/search-provider/cc-search-provider.c
@@ -113,6 +113,8 @@ handle_get_initial_result_set (CcShellSearchProvider2  *skeleton,
   casefolded_terms = get_casefolded_terms (terms);
   results = g_ptr_array_new ();
 
+  cc_shell_model_set_sort_terms (CC_SHELL_MODEL (model), casefolded_terms);
+
   ok = gtk_tree_model_get_iter_first (model, &iter);
   while (ok)
     {
@@ -154,6 +156,8 @@ handle_get_subsearch_result_set (CcShellSearchProvider2  *skeleton,
   casefolded_terms = get_casefolded_terms (terms);
   results = g_ptr_array_new ();
 
+  cc_shell_model_set_sort_terms (CC_SHELL_MODEL (model), casefolded_terms);
+
   for (i = 0; previous_results[i]; i++)
     {
       if (gtk_tree_model_get_iter_from_string (model, &iter,
diff --git a/shell/cc-shell-model.c b/shell/cc-shell-model.c
index 2add15e..55f3e3c 100644
--- a/shell/cc-shell-model.c
+++ b/shell/cc-shell-model.c
@@ -30,12 +30,225 @@
 #define GNOME_SETTINGS_PANEL_CATEGORY GNOME_SETTINGS_PANEL_ID_KEY
 #define GNOME_SETTINGS_PANEL_ID_KEYWORDS "Keywords"
 
+struct _CcShellModelPrivate
+{
+  gchar **sort_terms;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (CcShellModel, cc_shell_model, GTK_TYPE_LIST_STORE)
+
+static gint
+sort_by_name (GtkTreeModel *model,
+              GtkTreeIter  *a,
+              GtkTreeIter  *b)
+{
+  gchar *a_name = NULL;
+  gchar *b_name = NULL;
+  gint rval = 0;
+
+  gtk_tree_model_get (model, a, COL_CASEFOLDED_NAME, &a_name, -1);
+  gtk_tree_model_get (model, b, COL_CASEFOLDED_NAME, &b_name, -1);
+
+  rval = g_strcmp0 (a_name, b_name);
+
+  g_free (a_name);
+  g_free (b_name);
+
+  return rval;
+}
+
+static gint
+sort_by_name_with_terms (GtkTreeModel  *model,
+                         GtkTreeIter   *a,
+                         GtkTreeIter   *b,
+                         gchar        **terms)
+{
+  gboolean a_match, b_match;
+  gchar *a_name = NULL;
+  gchar *b_name = NULL;
+  gint rval = 0;
+  gint i;
+
+  gtk_tree_model_get (model, a, COL_CASEFOLDED_NAME, &a_name, -1);
+  gtk_tree_model_get (model, b, COL_CASEFOLDED_NAME, &b_name, -1);
+
+  for (i = 0; terms[i]; ++i)
+    {
+      a_match = strstr (a_name, terms[i]) != NULL;
+      b_match = strstr (b_name, terms[i]) != NULL;
+
+      if (a_match && !b_match)
+        {
+          rval = -1;
+          break;
+        }
+      else if (!a_match && b_match)
+        {
+          rval = 1;
+          break;
+        }
+    }
+
+  g_free (a_name);
+  g_free (b_name);
+
+  return rval;
+}
+
+static gint
+count_matches (gchar **keywords,
+               gchar **terms)
+{
+  gint i, j, c;
+
+  if (!keywords || !terms)
+    return 0;
+
+  c = 0;
+
+  for (i = 0; terms[i]; ++i)
+    for (j = 0; keywords[j]; ++j)
+      if (strstr (keywords[j], terms[i]))
+        c += 1;
+
+  return c;
+}
+
+static gint
+sort_by_keywords_with_terms (GtkTreeModel  *model,
+                             GtkTreeIter   *a,
+                             GtkTreeIter   *b,
+                             gchar        **terms)
+{
+  gint a_matches, b_matches;
+  gchar **a_keywords = NULL;
+  gchar **b_keywords = NULL;
+  gint rval = 0;
+
+  gtk_tree_model_get (model, a, COL_KEYWORDS, &a_keywords, -1);
+  gtk_tree_model_get (model, b, COL_KEYWORDS, &b_keywords, -1);
+
+  a_matches = count_matches (a_keywords, terms);
+  b_matches = count_matches (b_keywords, terms);
+
+  if (a_matches > b_matches)
+    rval = -1;
+  else if (a_matches < b_matches)
+    rval = 1;
+
+  g_strfreev (a_keywords);
+  g_strfreev (b_keywords);
+
+  return rval;
+}
+
+static gint
+sort_by_description_with_terms (GtkTreeModel  *model,
+                                GtkTreeIter   *a,
+                                GtkTreeIter   *b,
+                                gchar        **terms)
+{
+  gint a_matches, b_matches;
+  gchar *a_description = NULL;
+  gchar *b_description = NULL;
+  gchar **a_description_split = NULL;
+  gchar **b_description_split = NULL;
+  gint rval = 0;
+
+  gtk_tree_model_get (model, a, COL_DESCRIPTION, &a_description, -1);
+  gtk_tree_model_get (model, b, COL_DESCRIPTION, &b_description, -1);
 
-G_DEFINE_TYPE (CcShellModel, cc_shell_model, GTK_TYPE_LIST_STORE)
+  if (a_description && !b_description)
+    {
+      rval = -1;
+      goto out;
+    }
+  else if (!a_description && b_description)
+    {
+      rval = 1;
+      goto out;
+    }
+  else if (!a_description && !b_description)
+    {
+      rval = 0;
+      goto out;
+    }
+
+  a_description_split = g_strsplit (a_description, " ", -1);
+  b_description_split = g_strsplit (b_description, " ", -1);
+
+  a_matches = count_matches (a_description_split, terms);
+  b_matches = count_matches (b_description_split, terms);
+
+  if (a_matches > b_matches)
+    rval = -1;
+  else if (a_matches < b_matches)
+    rval = 1;
+
+ out:
+  g_free (a_description);
+  g_free (b_description);
+  g_strfreev (a_description_split);
+  g_strfreev (b_description_split);
+
+  return rval;
+}
+
+static gint
+sort_with_terms (GtkTreeModel  *model,
+                 GtkTreeIter   *a,
+                 GtkTreeIter   *b,
+                 gchar        **terms)
+{
+  gint rval;
+
+  rval = sort_by_name_with_terms (model, a, b, terms);
+  if (rval)
+    return rval;
+
+  rval = sort_by_keywords_with_terms (model, a, b, terms);
+  if (rval)
+    return rval;
+
+  rval = sort_by_description_with_terms (model, a, b, terms);
+  if (rval)
+    return rval;
+
+  return sort_by_name (model, a, b);
+}
+
+static gint
+cc_shell_model_sort_func (GtkTreeModel *model,
+                          GtkTreeIter  *a,
+                          GtkTreeIter  *b,
+                          gpointer      data)
+{
+  CcShellModel *self = data;
+  CcShellModelPrivate *priv = self->priv;
+
+  if (!priv->sort_terms || !priv->sort_terms[0])
+    return sort_by_name (model, a, b);
+  else
+    return sort_with_terms (model, a, b, priv->sort_terms);
+}
+
+static void
+cc_shell_model_finalize (GObject *object)
+{
+  CcShellModelPrivate *priv = CC_SHELL_MODEL (object)->priv;;
+
+  g_strfreev (priv->sort_terms);
+
+  G_OBJECT_CLASS (cc_shell_model_parent_class)->finalize (object);
+}
 
 static void
 cc_shell_model_class_init (CcShellModelClass *klass)
 {
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = cc_shell_model_finalize;
 }
 
 static void
@@ -44,10 +257,16 @@ cc_shell_model_init (CcShellModel *self)
   GType types[] = {G_TYPE_STRING, G_TYPE_STRING, G_TYPE_APP_INFO, G_TYPE_STRING,
                    G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_ICON, G_TYPE_STRV};
 
+  self->priv = cc_shell_model_get_instance_private (self);
+
   gtk_list_store_set_column_types (GTK_LIST_STORE (self),
                                    N_COLS, types);
 
-  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self), COL_NAME,
+  gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (self),
+                                           cc_shell_model_sort_func,
+                                           self, NULL);
+  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self),
+                                        GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
                                         GTK_SORT_ASCENDING);
 }
 
@@ -142,3 +361,18 @@ cc_shell_model_iter_matches_search (CcShellModel *model,
 
   return result;
 }
+
+void
+cc_shell_model_set_sort_terms (CcShellModel  *self,
+                               gchar        **terms)
+{
+  CcShellModelPrivate *priv = self->priv;
+
+  g_strfreev (priv->sort_terms);
+  priv->sort_terms = g_strdupv (terms);
+
+  /* trigger a re-sort */
+  gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (self),
+                                           cc_shell_model_sort_func,
+                                           self, NULL);
+}
diff --git a/shell/cc-shell-model.h b/shell/cc-shell-model.h
index f92c25f..bf24173 100644
--- a/shell/cc-shell-model.h
+++ b/shell/cc-shell-model.h
@@ -50,6 +50,7 @@ G_BEGIN_DECLS
 
 typedef struct _CcShellModel CcShellModel;
 typedef struct _CcShellModelClass CcShellModelClass;
+typedef struct _CcShellModelPrivate CcShellModelPrivate;
 
 typedef enum {
   CC_CATEGORY_PERSONAL,
@@ -76,6 +77,8 @@ enum
 struct _CcShellModel
 {
   GtkListStore parent;
+
+  CcShellModelPrivate *priv;
 };
 
 struct _CcShellModelClass
@@ -96,6 +99,9 @@ gboolean cc_shell_model_iter_matches_search (CcShellModel *model,
                                              GtkTreeIter  *iter,
                                              const char   *term);
 
+void cc_shell_model_set_sort_terms (CcShellModel  *model,
+                                    gchar        **terms);
+
 G_END_DECLS
 
 #endif /* _CC_SHELL_MODEL_H */
diff --git a/shell/cc-window.c b/shell/cc-window.c
index d091e79..8da4645 100644
--- a/shell/cc-window.c
+++ b/shell/cc-window.c
@@ -621,6 +621,8 @@ search_entry_changed_cb (GtkEntry *entry,
   g_strfreev (priv->filter_terms);
   priv->filter_terms = g_strsplit (priv->filter_string, " ", -1);
 
+  cc_shell_model_set_sort_terms (CC_SHELL_MODEL (priv->store), priv->filter_terms);
+
   if (!g_strcmp0 (priv->filter_string, ""))
     {
       shell_show_overview_page (center);


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