diff --git a/shell/Makefile.am b/shell/Makefile.am index 75454e30f..f2eae952e 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -33,6 +33,8 @@ gnome_control_center_SOURCES = \ cc-panel.h \ cc-shell.c \ cc-shell.h \ + cc-util.c \ + cc-util.h \ hostname-helper.c \ hostname-helper.h \ cc-hostname-entry.c \ diff --git a/shell/cc-shell-category-view.c b/shell/cc-shell-category-view.c index edf87ef41..c924bd0b6 100644 --- a/shell/cc-shell-category-view.c +++ b/shell/cc-shell-category-view.c @@ -129,11 +129,12 @@ cc_shell_category_view_constructed (GObject *object) renderer = gtk_cell_renderer_pixbuf_new (); g_object_set (renderer, "follow-state", TRUE, + "stock-size", GTK_ICON_SIZE_DIALOG, NULL); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (iconview), renderer, FALSE); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (iconview), renderer, - "pixbuf", COL_PIXBUF); + "gicon", COL_GICON); gtk_icon_view_set_text_column (GTK_ICON_VIEW (iconview), COL_NAME); gtk_icon_view_set_item_width (GTK_ICON_VIEW (iconview), 100); diff --git a/shell/cc-shell-item-view.c b/shell/cc-shell-item-view.c index 4b41d4c3f..cc8ae988c 100644 --- a/shell/cc-shell-item-view.c +++ b/shell/cc-shell-item-view.c @@ -123,7 +123,7 @@ iconview_item_activated_cb (GtkIconView *icon_view, { GtkTreeModel *model; GtkTreeIter iter; - gchar *name, *desktop_file, *id; + gchar *name, *id; model = gtk_icon_view_get_model (icon_view); @@ -135,14 +135,12 @@ iconview_item_activated_cb (GtkIconView *icon_view, gtk_tree_model_get (model, &iter, COL_NAME, &name, - COL_DESKTOP_FILE, &desktop_file, COL_ID, &id, -1); g_signal_emit (cc_view, signals[DESKTOP_ITEM_ACTIVATED], 0, - name, id, desktop_file); + name, id); - g_free (desktop_file); g_free (name); g_free (id); } @@ -187,8 +185,7 @@ cc_shell_item_view_class_init (CcShellItemViewClass *klass) NULL, g_cclosure_marshal_generic, G_TYPE_NONE, - 3, - G_TYPE_STRING, + 2, G_TYPE_STRING, G_TYPE_STRING); } diff --git a/shell/cc-shell-model.c b/shell/cc-shell-model.c index fafd64830..2add15edc 100644 --- a/shell/cc-shell-model.c +++ b/shell/cc-shell-model.c @@ -19,11 +19,13 @@ * Author: Thomas Wood */ -#include "cc-shell-model.h" #include #include +#include "cc-shell-model.h" +#include "cc-util.h" + #define GNOME_SETTINGS_PANEL_ID_KEY "X-GNOME-Settings-Panel" #define GNOME_SETTINGS_PANEL_CATEGORY GNOME_SETTINGS_PANEL_ID_KEY #define GNOME_SETTINGS_PANEL_ID_KEYWORDS "Keywords" @@ -31,73 +33,6 @@ G_DEFINE_TYPE (CcShellModel, cc_shell_model, GTK_TYPE_LIST_STORE) -static GdkPixbuf * -load_pixbuf_for_gicon (GIcon *icon) -{ - GtkIconTheme *theme; - GtkIconInfo *icon_info; - GdkPixbuf *pixbuf = NULL; - GError *err = NULL; - - if (icon == NULL) - return NULL; - - theme = gtk_icon_theme_get_default (); - - icon_info = gtk_icon_theme_lookup_by_gicon (theme, icon, - 48, GTK_ICON_LOOKUP_FORCE_SIZE); - if (icon_info) - { - pixbuf = gtk_icon_info_load_icon (icon_info, &err); - if (err) - { - g_warning ("Could not load icon '%s': %s", - gtk_icon_info_get_filename (icon_info), err->message); - g_error_free (err); - } - - gtk_icon_info_free (icon_info); - } - else - { - char *name; - - name = g_icon_to_string (icon); - g_warning ("Could not find icon '%s'", name); - g_free (name); - } - - return pixbuf; -} - -static void -icon_theme_changed (GtkIconTheme *theme, - CcShellModel *self) -{ - GtkTreeIter iter; - GtkTreeModel *model; - gboolean cont; - - model = GTK_TREE_MODEL (self); - cont = gtk_tree_model_get_iter_first (model, &iter); - while (cont) - { - GdkPixbuf *pixbuf; - GIcon *icon; - - gtk_tree_model_get (model, &iter, - COL_GICON, &icon, - -1); - pixbuf = load_pixbuf_for_gicon (icon); - g_object_unref (icon); - gtk_list_store_set (GTK_LIST_STORE (model), &iter, - COL_PIXBUF, pixbuf, - -1); - - cont = gtk_tree_model_iter_next (model, &iter); - } -} - static void cc_shell_model_class_init (CcShellModelClass *klass) { @@ -106,17 +41,14 @@ cc_shell_model_class_init (CcShellModelClass *klass) static void cc_shell_model_init (CcShellModel *self) { - GType types[] = {G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - GDK_TYPE_PIXBUF, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_ICON, G_TYPE_STRV}; + 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}; 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_SORT_ASCENDING); - - g_signal_connect (G_OBJECT (gtk_icon_theme_get_default ()), "changed", - G_CALLBACK (icon_theme_changed), self); } CcShellModel * @@ -125,6 +57,24 @@ cc_shell_model_new (void) return g_object_new (CC_TYPE_SHELL_MODEL, NULL); } +static char ** +get_casefolded_keywords (GAppInfo *appinfo) +{ + const char * const * keywords; + char **casefolded_keywords; + int i, n; + + keywords = g_desktop_app_info_get_keywords (G_DESKTOP_APP_INFO (appinfo)); + n = keywords ? g_strv_length ((char**) keywords) : 0; + casefolded_keywords = g_new (char*, n+1); + + for (i = 0; i < n; i++) + casefolded_keywords[i] = cc_util_normalize_casefold_and_unaccent (keywords[i]); + casefolded_keywords[n] = NULL; + + return casefolded_keywords; +} + void cc_shell_model_add_item (CcShellModel *model, CcPanelCategory category, @@ -133,23 +83,62 @@ cc_shell_model_add_item (CcShellModel *model, { GIcon *icon = g_app_info_get_icon (appinfo); const gchar *name = g_app_info_get_name (appinfo); - const gchar *desktop = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (appinfo)); const gchar *comment = g_app_info_get_description (appinfo); - GdkPixbuf *pixbuf = NULL; - const char * const * keywords; + char **keywords; + char *casefolded_name, *casefolded_description; - keywords = g_desktop_app_info_get_keywords (G_DESKTOP_APP_INFO (appinfo)); - - pixbuf = load_pixbuf_for_gicon (icon); + casefolded_name = cc_util_normalize_casefold_and_unaccent (name); + casefolded_description = cc_util_normalize_casefold_and_unaccent (comment); + keywords = get_casefolded_keywords (appinfo); gtk_list_store_insert_with_values (GTK_LIST_STORE (model), NULL, 0, COL_NAME, name, - COL_DESKTOP_FILE, desktop, + COL_CASEFOLDED_NAME, casefolded_name, + COL_APP, appinfo, COL_ID, id, - COL_PIXBUF, pixbuf, COL_CATEGORY, category, COL_DESCRIPTION, comment, + COL_CASEFOLDED_DESCRIPTION, casefolded_description, COL_GICON, icon, COL_KEYWORDS, keywords, -1); + + g_free (casefolded_name); + g_free (casefolded_description); + g_strfreev (keywords); +} + +gboolean +cc_shell_model_iter_matches_search (CcShellModel *model, + GtkTreeIter *iter, + const char *term) +{ + gchar *name, *description; + gboolean result; + gchar **keywords; + + gtk_tree_model_get (GTK_TREE_MODEL (model), iter, + COL_CASEFOLDED_NAME, &name, + COL_CASEFOLDED_DESCRIPTION, &description, + COL_KEYWORDS, &keywords, + -1); + + result = (strstr (name, term) != NULL); + + if (!result && description) + result = (strstr (description, term) != NULL); + + if (!result && keywords) + { + gint i; + + for (i = 0; !result && keywords[i]; i++) + result = (strstr (keywords[i], term) == keywords[i]); + } + + g_free (name); + g_free (description); + g_strfreev (keywords); + + return result; } diff --git a/shell/cc-shell-model.h b/shell/cc-shell-model.h index b2e92fd2e..f92c25f2e 100644 --- a/shell/cc-shell-model.h +++ b/shell/cc-shell-model.h @@ -61,11 +61,12 @@ typedef enum { enum { COL_NAME, - COL_DESKTOP_FILE, + COL_CASEFOLDED_NAME, + COL_APP, COL_ID, - COL_PIXBUF, COL_CATEGORY, COL_DESCRIPTION, + COL_CASEFOLDED_DESCRIPTION, COL_GICON, COL_KEYWORDS, @@ -91,6 +92,10 @@ void cc_shell_model_add_item (CcShellModel *model, GAppInfo *appinfo, const char *id); +gboolean cc_shell_model_iter_matches_search (CcShellModel *model, + GtkTreeIter *iter, + const char *term); + G_END_DECLS #endif /* _CC_SHELL_MODEL_H */ diff --git a/shell/cc-util.c b/shell/cc-util.c new file mode 100644 index 000000000..e51a9d2c0 --- /dev/null +++ b/shell/cc-util.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012 Giovanni Campagna + * + * The Control Center is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * The Control Center is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with the Control Center; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" + +#include + + +#include "cc-util.h" + +/* Combining diacritical mark? + * Basic range: [0x0300,0x036F] + * Supplement: [0x1DC0,0x1DFF] + * For Symbols: [0x20D0,0x20FF] + * Half marks: [0xFE20,0xFE2F] + */ +#define IS_CDM_UCS4(c) (((c) >= 0x0300 && (c) <= 0x036F) || \ + ((c) >= 0x1DC0 && (c) <= 0x1DFF) || \ + ((c) >= 0x20D0 && (c) <= 0x20FF) || \ + ((c) >= 0xFE20 && (c) <= 0xFE2F)) + +/* Copied from tracker/src/libtracker-fts/tracker-parser-glib.c under the GPL + * And then from gnome-shell/src/shell-util.c + * + * Originally written by Aleksander Morgado + */ +char * +cc_util_normalize_casefold_and_unaccent (const char *str) +{ + char *normalized, *tmp; + int i = 0, j = 0, ilen; + + if (str == NULL) + return NULL; + + normalized = g_utf8_normalize (str, -1, G_NORMALIZE_NFKD); + tmp = g_utf8_casefold (normalized, -1); + g_free (normalized); + + ilen = strlen (tmp); + + while (i < ilen) + { + gunichar unichar; + gchar *next_utf8; + gint utf8_len; + + /* Get next character of the word as UCS4 */ + unichar = g_utf8_get_char_validated (&tmp[i], -1); + + /* Invalid UTF-8 character or end of original string. */ + if (unichar == (gunichar) -1 || + unichar == (gunichar) -2) + { + break; + } + + /* Find next UTF-8 character */ + next_utf8 = g_utf8_next_char (&tmp[i]); + utf8_len = next_utf8 - &tmp[i]; + + if (IS_CDM_UCS4 ((guint32) unichar)) + { + /* If the given unichar is a combining diacritical mark, + * just update the original index, not the output one */ + i += utf8_len; + continue; + } + + /* If already found a previous combining + * diacritical mark, indexes are different so + * need to copy characters. As output and input + * buffers may overlap, need to use memmove + * instead of memcpy */ + if (i != j) + { + memmove (&tmp[j], &tmp[i], utf8_len); + } + + /* Update both indexes */ + i += utf8_len; + j += utf8_len; + } + + /* Force proper string end */ + tmp[j] = '\0'; + + return tmp; +} diff --git a/shell/cc-util.h b/shell/cc-util.h new file mode 100644 index 000000000..42b09ff2b --- /dev/null +++ b/shell/cc-util.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 Giovanni Campagna + * + * The Control Center is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * The Control Center is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with the Control Center; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef _CC_UTIL_H +#define _CC_UTIL_H + +#include + +char *cc_util_normalize_casefold_and_unaccent (const char *str); + +#endif diff --git a/shell/gnome-control-center.c b/shell/gnome-control-center.c index 0f1a675e4..145b0b9ff 100644 --- a/shell/gnome-control-center.c +++ b/shell/gnome-control-center.c @@ -40,6 +40,7 @@ #include "cc-shell-category-view.h" #include "cc-shell-model.h" #include "cc-panel-loader.h" +#include "cc-util.h" G_DEFINE_TYPE (GnomeControlCenter, gnome_control_center, CC_TYPE_SHELL) @@ -176,7 +177,6 @@ static gboolean activate_panel (GnomeControlCenter *shell, const gchar *id, const gchar **argv, - const gchar *desktop_file, const gchar *name, GIcon *gicon) { @@ -184,8 +184,6 @@ activate_panel (GnomeControlCenter *shell, GtkWidget *box; const gchar *icon_name; - if (!desktop_file) - return FALSE; if (!id) return FALSE; @@ -580,47 +578,12 @@ model_filter_func (GtkTreeModel *model, GtkTreeIter *iter, GnomeControlCenterPrivate *priv) { - gchar *name; - gchar *needle, *haystack; - gboolean result; - gchar **keywords; + if (!priv->filter_string) + return FALSE; - gtk_tree_model_get (model, iter, - COL_NAME, &name, - COL_KEYWORDS, &keywords, - -1); - - if (!priv->filter_string || !name) - { - g_free (name); - g_strfreev (keywords); - return FALSE; - } - - needle = g_utf8_casefold (priv->filter_string, -1); - haystack = g_utf8_casefold (name, -1); - - result = (strstr (haystack, needle) != NULL); - - if (!result && keywords) - { - gint i; - gchar *keyword; - - for (i = 0; !result && keywords[i]; i++) - { - keyword = g_utf8_casefold (keywords[i], -1); - result = strstr (keyword, needle) == keyword; - g_free (keyword); - } - } - - g_free (name); - g_free (haystack); - g_free (needle); - g_strfreev (keywords); - - return result; + return cc_shell_model_iter_matches_search (CC_SHELL_MODEL (model), + iter, + priv->filter_string); } static gboolean @@ -647,7 +610,7 @@ search_entry_changed_cb (GtkEntry *entry, return; /* Don't re-filter for added trailing or leading spaces */ - str = g_strdup (gtk_entry_get_text (entry)); + str = cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (entry)); g_strstrip (str); if (!g_strcmp0 (str, priv->filter_string)) { @@ -991,7 +954,6 @@ _shell_set_active_panel_from_id (CcShell *shell, GtkTreeIter iter; gboolean iter_valid; gchar *name = NULL; - gchar *desktop = NULL; GIcon *gicon = NULL; GnomeControlCenterPrivate *priv = GNOME_CONTROL_CENTER (shell)->priv; GtkWidget *old_panel; @@ -1016,7 +978,6 @@ _shell_set_active_panel_from_id (CcShell *shell, gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, COL_NAME, &name, - COL_DESKTOP_FILE, &desktop, COL_GICON, &gicon, COL_ID, &id, -1); @@ -1030,13 +991,11 @@ _shell_set_active_panel_from_id (CcShell *shell, { g_free (id); g_free (name); - g_free (desktop); if (gicon) g_object_unref (gicon); name = NULL; id = NULL; - desktop = NULL; gicon = NULL; } @@ -1050,7 +1009,7 @@ _shell_set_active_panel_from_id (CcShell *shell, { g_warning ("Could not find settings panel \"%s\"", start_id); } - else if (activate_panel (GNOME_CONTROL_CENTER (shell), start_id, argv, desktop, + else if (activate_panel (GNOME_CONTROL_CENTER (shell), start_id, argv, name, gicon) == FALSE) { /* Failed to activate the panel for some reason, @@ -1067,7 +1026,6 @@ _shell_set_active_panel_from_id (CcShell *shell, } g_free (name); - g_free (desktop); if (gicon) g_object_unref (gicon);