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
This commit is contained in:
Rui Matos 2014-04-26 23:11:47 +02:00
parent 98a2ab2603
commit 13abc75273
4 changed files with 248 additions and 2 deletions

View file

@ -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,

View file

@ -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 (CcShellModel, cc_shell_model, GTK_TYPE_LIST_STORE)
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);
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);
}

View file

@ -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 */

View file

@ -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);