[Xfce4-commits] <exo:nick/icon-model-without-dups> Do not insert symlink icons in the model.
Nick Schermer
noreply at xfce.org
Sun Feb 27 20:10:02 CET 2011
Updating branch refs/heads/nick/icon-model-without-dups
to 64337d5176fe01644b2e9bc3dd3611433709ab4a (commit)
from 71e3a1e65f1550b54f9820a101552b5fe6620fac (commit)
commit 64337d5176fe01644b2e9bc3dd3611433709ab4a
Author: Nick Schermer <nick at xfce.org>
Date: Sun Feb 27 20:04:41 2011 +0100
Do not insert symlink icons in the model.
When loading the icons, check if the filename of the icon
is a symlink to another icon in the theme. If so merge the
"alternative" name in the real icon. This saves a lot of
icon duplicates in the icon chooser, while they are still
selected because the alternative names are still known.
This add some complexity, but might be a lot faster in the
end, because on normal themes around 50% of the icons are
merged, so a lot less pixbuf loading/rendering.
exo/exo-icon-chooser-dialog.c | 6 +-
exo/exo-icon-chooser-model.c | 250 +++++++++++++++++++++++++++++------------
exo/exo-icon-chooser-model.h | 2 -
3 files changed, 180 insertions(+), 78 deletions(-)
diff --git a/exo/exo-icon-chooser-dialog.c b/exo/exo-icon-chooser-dialog.c
index 984a3a9..ba2c6eb 100644
--- a/exo/exo-icon-chooser-dialog.c
+++ b/exo/exo-icon-chooser-dialog.c
@@ -269,7 +269,7 @@ exo_icon_chooser_dialog_init (ExoIconChooserDialog *icon_chooser_dialog)
"yalign", 0.0f,
NULL);
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->icon_chooser), renderer, FALSE);
- gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->icon_chooser), renderer, "text", EXO_ICON_CHOOSER_MODEL_COLUMN_DISPLAY_NAME, NULL);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->icon_chooser), renderer, "text", EXO_ICON_CHOOSER_MODEL_COLUMN_ICON_NAME, NULL);
/* setup the file chooser (hidden by default) */
priv->file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
@@ -379,7 +379,7 @@ exo_icon_chooser_dialog_set_model (ExoIconChooserDialog *dialog)
g_object_unref (G_OBJECT (filter));
/* enable search on the display name */
- exo_icon_view_set_search_column (EXO_ICON_VIEW (priv->icon_chooser), EXO_ICON_CHOOSER_MODEL_COLUMN_DISPLAY_NAME);
+ exo_icon_view_set_search_column (EXO_ICON_VIEW (priv->icon_chooser), EXO_ICON_CHOOSER_MODEL_COLUMN_ICON_NAME);
}
g_object_unref (G_OBJECT (model));
}
@@ -432,7 +432,7 @@ exo_icon_chooser_dialog_visible_func (GtkTreeModel *model,
/* filter by string */
if (priv->casefolded_text != NULL)
{
- gtk_tree_model_get (model, iter, EXO_ICON_CHOOSER_MODEL_COLUMN_DISPLAY_NAME, &name, -1);
+ gtk_tree_model_get (model, iter, EXO_ICON_CHOOSER_MODEL_COLUMN_ICON_NAME, &name, -1);
/* casefold the name */
normalized = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
diff --git a/exo/exo-icon-chooser-model.c b/exo/exo-icon-chooser-model.c
index 0ebb854..7b70bf8 100644
--- a/exo/exo-icon-chooser-model.c
+++ b/exo/exo-icon-chooser-model.c
@@ -72,9 +72,10 @@ static gboolean exo_icon_chooser_model_iter_parent (GtkTreeMode
GtkTreeIter *child);
static void exo_icon_chooser_model_icon_theme_changed (GtkIconTheme *icon_theme,
ExoIconChooserModel *model);
-static gint exo_icon_chooser_model_item_compare (gconstpointer item_a,
- gconstpointer item_b);
-static void exo_icon_chooser_model_item_free (ExoIconChooserModelItem *item);
+static void exo_icon_chooser_model_item_to_list (gpointer key,
+ gpointer value,
+ gpointer data);
+static void exo_icon_chooser_model_item_free (gpointer data);
@@ -93,9 +94,14 @@ struct _ExoIconChooserModel
struct _ExoIconChooserModelItem
{
- ExoIconChooserContext context;
- gchar *icon_name;
- gchar *display_name;
+ gchar *icon_name;
+ ExoIconChooserContext context;
+
+ /* storage for symlink icons merge */
+ GtkIconInfo *icon_info;
+
+ /* icon names of symlinks to this item */
+ GPtrArray *other_names;
};
@@ -209,7 +215,6 @@ exo_icon_chooser_model_get_column_type (GtkTreeModel *tree_model,
return G_TYPE_UINT;
case EXO_ICON_CHOOSER_MODEL_COLUMN_ICON_NAME:
- case EXO_ICON_CHOOSER_MODEL_COLUMN_DISPLAY_NAME:
return G_TYPE_STRING;
}
@@ -272,7 +277,6 @@ exo_icon_chooser_model_get_value (GtkTreeModel *tree_model,
{
ExoIconChooserModelItem *item;
ExoIconChooserModel *model = EXO_ICON_CHOOSER_MODEL (tree_model);
- GtkIconInfo *info;
_exo_return_if_fail (EXO_IS_ICON_CHOOSER_MODEL (model));
_exo_return_if_fail (iter->stamp == model->stamp);
@@ -292,27 +296,6 @@ exo_icon_chooser_model_get_value (GtkTreeModel *tree_model,
g_value_set_static_string (value, item->icon_name);
break;
- case EXO_ICON_CHOOSER_MODEL_COLUMN_DISPLAY_NAME:
- /* load the display name on-demand */
- if (G_UNLIKELY (item->display_name == NULL))
- {
- /* determine the icon info */
- info = gtk_icon_theme_lookup_icon (model->icon_theme, item->icon_name, 48, 0);
- if (G_LIKELY (info != NULL))
- {
- /* determine the display name from the icon info */
- item->display_name = g_strdup (gtk_icon_info_get_display_name (info));
- gtk_icon_info_free (info);
- }
-
- /* fallback to the icon name */
- if (item->display_name == NULL)
- item->display_name = item->icon_name;
- }
- g_value_init (value, G_TYPE_STRING);
- g_value_set_static_string (value, item->display_name);
- break;
-
default:
_exo_assert_not_reached ();
break;
@@ -409,18 +392,84 @@ exo_icon_chooser_model_iter_parent (GtkTreeModel *tree_model,
+static gboolean
+exo_icon_chooser_model_merge_symlinks (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ GHashTable *items = data;
+ ExoIconChooserModelItem *sym_item = value;
+ ExoIconChooserModelItem *item;
+ gchar *target;
+ const gchar *filename;
+ gchar *p, *name;
+ gboolean merged = FALSE;
+
+ /* get the location the symlink points to */
+ filename = gtk_icon_info_get_filename (sym_item->icon_info);
+ target = g_file_read_link (filename, NULL);
+ if (G_UNLIKELY (target == NULL))
+ return merged;
+
+ /* we don't care about paths and relative names, so make sure we
+ * have the basename of the symlink target */
+ if (g_path_is_absolute (target)
+ || g_str_has_prefix (target, "../"))
+ {
+ p = g_path_get_basename (target);
+ g_free (target);
+ target = p;
+ }
+
+ /* the icon names all have an extension */
+ p = strrchr (target, '.');
+ if (G_LIKELY (p != NULL))
+ {
+ /* lookup the target from the items table */
+ name = g_strndup (target, p - target);
+ item = g_hash_table_lookup (items, name);
+ g_free (name);
+
+ if (G_LIKELY (item != NULL))
+ {
+ /* allocate the array on demand */
+ if (item->other_names == NULL)
+ item->other_names = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+
+ /* take the symlinks display name */
+ g_ptr_array_add (item->other_names, sym_item->icon_name);
+ sym_item->icon_name = NULL;
+
+ /* set the symlinks context if the item has none */
+ if (item->context == EXO_ICON_CHOOSER_CONTEXT_OTHER)
+ item->context = sym_item->context;
+
+ /* this item can be removed from the hash table,
+ * remaining data will be freed by the destroy func */
+ merged = TRUE;
+ }
+ }
+
+ g_free (target);
+
+ return merged;
+}
+
+
+
static void
exo_icon_chooser_model_icon_theme_changed (GtkIconTheme *icon_theme,
ExoIconChooserModel *model)
{
ExoIconChooserModelItem *item;
+ GHashTable *items;
+ GHashTable *symlink_items;
+ GList *icons, *lp;
+ const gchar *filename;
ExoIconChooserContext context;
GtkTreePath *path;
GtkTreeIter iter;
- GList *items;
- GList *icons;
- GList *icp;
- GList *itp;
+ GtkIconInfo *icon_info;
/* allocate a path to the first model item */
path = gtk_tree_path_new_from_indices (0, -1);
@@ -438,55 +487,75 @@ exo_icon_chooser_model_icon_theme_changed (GtkIconTheme *icon_theme,
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
}
- /* determine all icons available for the icon_theme */
- items = gtk_icon_theme_list_icons (icon_theme, NULL);
- for (itp = items; itp != NULL; itp = itp->next)
+ /* separate tables for the symlink and non-symlink icons */
+ items = g_hash_table_new (g_str_hash, g_str_equal);
+ symlink_items = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, exo_icon_chooser_model_item_free);
+
+ /* insert the theme icons in the correct hash table */
+ icons = gtk_icon_theme_list_icons (icon_theme, NULL);
+ for (lp = icons; lp != NULL; lp = lp->next)
{
- /* allocate a model item for the icon... */
- item = g_slice_new (ExoIconChooserModelItem);
+ item = g_slice_new0 (ExoIconChooserModelItem);
+ item->icon_name = lp->data;
item->context = EXO_ICON_CHOOSER_CONTEXT_OTHER;
- item->icon_name = itp->data;
- item->display_name = NULL;
- /* ...and replace the name with the item */
- itp->data = item;
- }
+ icon_info = gtk_icon_theme_lookup_icon (icon_theme, item->icon_name, 48, 0);
+ if (G_LIKELY (icon_info != NULL))
+ {
+ /* check if this icon points to a symlink */
+ filename = gtk_icon_info_get_filename (icon_info);
+ if (filename != NULL
+ && g_file_test (filename, G_FILE_TEST_IS_SYMLINK))
+ {
+ /* insert this item in the symlink table */
+ item->icon_info = icon_info;
+ g_hash_table_insert (symlink_items, item->icon_name, item);
+ continue;
+ }
- /* sort the items by their icon names */
- items = g_list_sort (items, (GCompareFunc) exo_icon_chooser_model_item_compare);
+ gtk_icon_info_free (icon_info);
+ }
+
+ /* real file or no info, store it in the hash table */
+ g_hash_table_insert (items, item->icon_name, item);
+ }
+ g_list_free (icons);
/* now determine the categories for all items in the model */
for (context = 0; context < G_N_ELEMENTS (CONTEXT_NAMES); ++context)
{
- /* determine the icons for the context */
icons = gtk_icon_theme_list_icons (icon_theme, CONTEXT_NAMES[context]);
- icons = g_list_sort (icons, (GCompareFunc) g_strcasecmp);
-
- /* update the contexts for the items */
- for (icp = icons, itp = items; icp != NULL && itp != NULL; itp = itp->next)
+ for (lp = icons; lp != NULL; lp = lp->next)
{
- /* check if we have a match here */
- item = (ExoIconChooserModelItem *) itp->data;
- if (strcmp (item->icon_name, icp->data) != 0)
- continue;
-
- /* update the items context */
- item->context = context;
- g_free (icp->data);
- icp = icp->next;
- }
+ /* lookup the item in one of the hash tables */
+ item = g_hash_table_lookup (items, lp->data);
+ if (item == NULL)
+ item = g_hash_table_lookup (symlink_items, lp->data);
+
+ /* set the categories */
+ if (item != NULL)
+ item->context = context;
- /* release the icons */
- g_list_foreach (icp, (GFunc) g_free, NULL);
+ g_free (lp->data);
+ }
g_list_free (icons);
}
- /* insert the items into the model */
+ /* merge the symlinks in the items */
+ g_hash_table_foreach_remove (symlink_items, exo_icon_chooser_model_merge_symlinks, items);
+ g_hash_table_destroy (symlink_items);
+
+ /* create a sorted list of the resulting table */
+ icons = NULL;
+ g_hash_table_foreach (items, exo_icon_chooser_model_item_to_list, &icons);
+ g_hash_table_destroy (items);
+
+ /* insert the items into the model */
iter.stamp = model->stamp;
- for (itp = g_list_last (items); itp != NULL; itp = itp->prev)
+ for (lp = g_list_last (icons); lp != NULL; lp = lp->prev)
{
/* prepend the item to the beginning of our list */
- model->items = g_list_prepend (model->items, itp->data);
+ model->items = g_list_prepend (model->items, lp->data);
/* setup the iterator for the item */
iter.user_data = model->items;
@@ -494,7 +563,7 @@ exo_icon_chooser_model_icon_theme_changed (GtkIconTheme *icon_theme,
/* tell the view about our new item */
gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
}
- g_list_free (items);
+ g_list_free (icons);
/* release the path */
gtk_tree_path_free (path);
@@ -513,14 +582,30 @@ exo_icon_chooser_model_item_compare (gconstpointer item_a,
static void
-exo_icon_chooser_model_item_free (ExoIconChooserModelItem *item)
+exo_icon_chooser_model_item_to_list (gpointer key,
+ gpointer value,
+ gpointer data)
{
- /* release the display/icon name */
- if (item->display_name != item->icon_name)
- g_free (item->display_name);
- g_free (item->icon_name);
+ GList **list = data;
+ ExoIconChooserModelItem *item = value;
+
+ *list = g_list_insert_sorted (*list, item, exo_icon_chooser_model_item_compare);
+}
- /* release the item */
+
+
+static void
+exo_icon_chooser_model_item_free (gpointer data)
+{
+ ExoIconChooserModelItem *item = data;
+
+ if (G_LIKELY (item->other_names != NULL))
+ g_ptr_array_free (item->other_names, TRUE);
+
+ if (G_LIKELY (item->icon_info != NULL))
+ gtk_icon_info_free (item->icon_info);
+
+ g_free (item->icon_name);
g_slice_free (ExoIconChooserModelItem, item);
}
@@ -617,6 +702,9 @@ _exo_icon_chooser_model_get_iter_for_icon_name (ExoIconChooserModel *model,
{
ExoIconChooserModelItem *item;
GList *lp;
+ guint i;
+ gboolean found;
+ const gchar *other_name;
_exo_return_val_if_fail (EXO_IS_ICON_CHOOSER_MODEL (model), FALSE);
_exo_return_val_if_fail (icon_name != NULL, FALSE);
@@ -625,9 +713,25 @@ _exo_icon_chooser_model_get_iter_for_icon_name (ExoIconChooserModel *model,
/* check all items in the model */
for (lp = model->items; lp != NULL; lp = lp->next)
{
+ found = FALSE;
+
/* compare this item's icon name */
item = (ExoIconChooserModelItem *) lp->data;
- if (strcmp (item->icon_name, icon_name) == 0)
+ if (strcmp (icon_name, item->icon_name) == 0)
+ found = TRUE;
+
+ /* look in the alternative names */
+ if (!found && item->other_names != NULL)
+ {
+ for (i = 0; !found && i < item->other_names->len; ++i)
+ {
+ other_name = g_ptr_array_index (item->other_names, i);
+ if (strcmp (icon_name, other_name) == 0)
+ found = TRUE;
+ }
+ }
+
+ if (found)
{
/* generate an iterator for this item */
iter->stamp = model->stamp;
diff --git a/exo/exo-icon-chooser-model.h b/exo/exo-icon-chooser-model.h
index 5e2c5d2..db7c42c 100644
--- a/exo/exo-icon-chooser-model.h
+++ b/exo/exo-icon-chooser-model.h
@@ -72,7 +72,6 @@ typedef enum
* ExoIconChooserModelColumns:
* @EXO_ICON_CHOOSER_MODEL_COLUMN_CONTEXT : the context of the icon.
* @EXO_ICON_CHOOSER_MODEL_COLUMN_ICON_NAME : the name of the icon.
- * @EXO_ICON_CHOOSER_MODEL_COLUMN_DISPLAY_NAME : the display name of the icon.
* @EXO_ICON_CHOOSER_MODEL_N_COLUMNS : the number of columns.
*
* The columns provided by the #ExoIconChooserModel.
@@ -81,7 +80,6 @@ typedef enum
{
EXO_ICON_CHOOSER_MODEL_COLUMN_CONTEXT,
EXO_ICON_CHOOSER_MODEL_COLUMN_ICON_NAME,
- EXO_ICON_CHOOSER_MODEL_COLUMN_DISPLAY_NAME,
EXO_ICON_CHOOSER_MODEL_N_COLUMNS,
} ExoIconChooserModelColumn;
More information about the Xfce4-commits
mailing list