diff options
Diffstat (limited to 'libcaja-private/caja-icon-info.c')
-rw-r--r-- | libcaja-private/caja-icon-info.c | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/libcaja-private/caja-icon-info.c b/libcaja-private/caja-icon-info.c new file mode 100644 index 00000000..63ed8792 --- /dev/null +++ b/libcaja-private/caja-icon-info.c @@ -0,0 +1,751 @@ +/* caja-icon-info.c + * Copyright (C) 2007 Red Hat, Inc., Alexander Larsson <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include "caja-icon-info.h" +#include "caja-default-file-icon.h" +#include <gtk/gtk.h> +#include <gio/gio.h> +#include <eel/eel-gdk-pixbuf-extensions.h> + +struct _CajaIconInfo +{ + GObject parent; + + gboolean sole_owner; + guint64 last_use_time; + GdkPixbuf *pixbuf; + + gboolean got_embedded_rect; + GdkRectangle embedded_rect; + gint n_attach_points; + GdkPoint *attach_points; + char *display_name; + char *icon_name; +}; + +struct _CajaIconInfoClass +{ + GObjectClass parent_class; +}; + +static void schedule_reap_cache (void); + +G_DEFINE_TYPE (CajaIconInfo, + caja_icon_info, + G_TYPE_OBJECT); + +static void +caja_icon_info_init (CajaIconInfo *icon) +{ + icon->last_use_time = g_thread_gettime (); + icon->sole_owner = TRUE; +} + +gboolean +caja_icon_info_is_fallback (CajaIconInfo *icon) +{ + return icon->pixbuf == NULL; +} + +static void +pixbuf_toggle_notify (gpointer info, + GObject *object, + gboolean is_last_ref) +{ + CajaIconInfo *icon = info; + + if (is_last_ref) + { + icon->sole_owner = TRUE; + g_object_remove_toggle_ref (object, + pixbuf_toggle_notify, + info); + icon->last_use_time = g_thread_gettime (); + schedule_reap_cache (); + } +} + +static void +caja_icon_info_finalize (GObject *object) +{ + CajaIconInfo *icon; + + icon = CAJA_ICON_INFO (object); + + if (!icon->sole_owner && icon->pixbuf) + { + g_object_remove_toggle_ref (G_OBJECT (icon->pixbuf), + pixbuf_toggle_notify, + icon); + } + + if (icon->pixbuf) + { + g_object_unref (icon->pixbuf); + } + g_free (icon->attach_points); + g_free (icon->display_name); + g_free (icon->icon_name); + + G_OBJECT_CLASS (caja_icon_info_parent_class)->finalize (object); +} + +static void +caja_icon_info_class_init (CajaIconInfoClass *icon_info_class) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) icon_info_class; + + gobject_class->finalize = caja_icon_info_finalize; + +} + +CajaIconInfo * +caja_icon_info_new_for_pixbuf (GdkPixbuf *pixbuf) +{ + CajaIconInfo *icon; + + icon = g_object_new (CAJA_TYPE_ICON_INFO, NULL); + + if (pixbuf) + { + icon->pixbuf = g_object_ref (pixbuf); + } + + return icon; +} + +static CajaIconInfo * +caja_icon_info_new_for_icon_info (GtkIconInfo *icon_info) +{ + CajaIconInfo *icon; + GdkPoint *points; + gint n_points; + const char *filename; + char *basename, *p; + + icon = g_object_new (CAJA_TYPE_ICON_INFO, NULL); + + icon->pixbuf = gtk_icon_info_load_icon (icon_info, NULL); + + icon->got_embedded_rect = gtk_icon_info_get_embedded_rect (icon_info, + &icon->embedded_rect); + + if (gtk_icon_info_get_attach_points (icon_info, &points, &n_points)) + { + icon->n_attach_points = n_points; + icon->attach_points = points; + } + + icon->display_name = g_strdup (gtk_icon_info_get_display_name (icon_info)); + + filename = gtk_icon_info_get_filename (icon_info); + if (filename != NULL) + { + basename = g_path_get_basename (filename); + p = strrchr (basename, '.'); + if (p) + { + *p = 0; + } + icon->icon_name = basename; + } + + return icon; +} + + +typedef struct +{ + GIcon *icon; + int size; +} LoadableIconKey; + +typedef struct +{ + char *filename; + int size; +} ThemedIconKey; + +static GHashTable *loadable_icon_cache = NULL; +static GHashTable *themed_icon_cache = NULL; +static guint reap_cache_timeout = 0; + +#define NSEC_PER_SEC ((guint64)1000000000L) + +static guint time_now; + +static gboolean +reap_old_icon (gpointer key, + gpointer value, + gpointer user_info) +{ + CajaIconInfo *icon = value; + gboolean *reapable_icons_left = user_info; + + if (icon->sole_owner) + { + if (time_now - icon->last_use_time > 30 * NSEC_PER_SEC) + { + /* This went unused 30 secs ago. reap */ + return TRUE; + } + else + { + /* We can reap this soon */ + *reapable_icons_left = TRUE; + } + } + + return FALSE; +} + +static gboolean +reap_cache (gpointer data) +{ + gboolean reapable_icons_left; + + reapable_icons_left = TRUE; + + time_now = g_thread_gettime (); + + if (loadable_icon_cache) + { + g_hash_table_foreach_remove (loadable_icon_cache, + reap_old_icon, + &reapable_icons_left); + } + + if (themed_icon_cache) + { + g_hash_table_foreach_remove (themed_icon_cache, + reap_old_icon, + &reapable_icons_left); + } + + if (reapable_icons_left) + { + return TRUE; + } + else + { + reap_cache_timeout = 0; + return FALSE; + } +} + +static void +schedule_reap_cache (void) +{ + if (reap_cache_timeout == 0) + { + reap_cache_timeout = g_timeout_add_seconds_full (0, 5, + reap_cache, + NULL, NULL); + } +} + +void +caja_icon_info_clear_caches (void) +{ + if (loadable_icon_cache) + { + g_hash_table_remove_all (loadable_icon_cache); + } + + if (themed_icon_cache) + { + g_hash_table_remove_all (themed_icon_cache); + } +} + +static guint +loadable_icon_key_hash (LoadableIconKey *key) +{ + return g_icon_hash (key->icon) ^ key->size; +} + +static gboolean +loadable_icon_key_equal (const LoadableIconKey *a, + const LoadableIconKey *b) +{ + return a->size == b->size && + g_icon_equal (a->icon, b->icon); +} + +static LoadableIconKey * +loadable_icon_key_new (GIcon *icon, int size) +{ + LoadableIconKey *key; + + key = g_slice_new (LoadableIconKey); + key->icon = g_object_ref (icon); + key->size = size; + + return key; +} + +static void +loadable_icon_key_free (LoadableIconKey *key) +{ + g_object_unref (key->icon); + g_slice_free (LoadableIconKey, key); +} + +static guint +themed_icon_key_hash (ThemedIconKey *key) +{ + return g_str_hash (key->filename) ^ key->size; +} + +static gboolean +themed_icon_key_equal (const ThemedIconKey *a, + const ThemedIconKey *b) +{ + return a->size == b->size && + g_str_equal (a->filename, b->filename); +} + +static ThemedIconKey * +themed_icon_key_new (const char *filename, int size) +{ + ThemedIconKey *key; + + key = g_slice_new (ThemedIconKey); + key->filename = g_strdup (filename); + key->size = size; + + return key; +} + +static void +themed_icon_key_free (ThemedIconKey *key) +{ + g_free (key->filename); + g_slice_free (ThemedIconKey, key); +} + +CajaIconInfo * +caja_icon_info_lookup (GIcon *icon, + int size) +{ + CajaIconInfo *icon_info; + GdkPixbuf *pixbuf; + + if (G_IS_LOADABLE_ICON (icon)) + { + LoadableIconKey lookup_key; + LoadableIconKey *key; + GInputStream *stream; + + if (loadable_icon_cache == NULL) + { + loadable_icon_cache = + g_hash_table_new_full ((GHashFunc)loadable_icon_key_hash, + (GEqualFunc)loadable_icon_key_equal, + (GDestroyNotify) loadable_icon_key_free, + (GDestroyNotify) g_object_unref); + } + + lookup_key.icon = icon; + lookup_key.size = size; + + icon_info = g_hash_table_lookup (loadable_icon_cache, &lookup_key); + if (icon_info) + { + return g_object_ref (icon_info); + } + + pixbuf = NULL; + stream = g_loadable_icon_load (G_LOADABLE_ICON (icon), + size, + NULL, NULL, NULL); + if (stream) + { + pixbuf = eel_gdk_pixbuf_load_from_stream_at_size (stream, size); + g_object_unref (stream); + } + + icon_info = caja_icon_info_new_for_pixbuf (pixbuf); + + key = loadable_icon_key_new (icon, size); + g_hash_table_insert (loadable_icon_cache, key, icon_info); + + return g_object_ref (icon_info); + } + else if (G_IS_THEMED_ICON (icon)) + { + const char * const *names; + ThemedIconKey lookup_key; + ThemedIconKey *key; + GtkIconTheme *icon_theme; + GtkIconInfo *gtkicon_info; + const char *filename; + + if (themed_icon_cache == NULL) + { + themed_icon_cache = + g_hash_table_new_full ((GHashFunc)themed_icon_key_hash, + (GEqualFunc)themed_icon_key_equal, + (GDestroyNotify) themed_icon_key_free, + (GDestroyNotify) g_object_unref); + } + + names = g_themed_icon_get_names (G_THEMED_ICON (icon)); + + icon_theme = gtk_icon_theme_get_default (); + gtkicon_info = gtk_icon_theme_choose_icon (icon_theme, (const char **)names, size, 0); + + if (gtkicon_info == NULL) + { + return caja_icon_info_new_for_pixbuf (NULL); + } + + filename = gtk_icon_info_get_filename (gtkicon_info); + + lookup_key.filename = (char *)filename; + lookup_key.size = size; + + icon_info = g_hash_table_lookup (themed_icon_cache, &lookup_key); + if (icon_info) + { + gtk_icon_info_free (gtkicon_info); + return g_object_ref (icon_info); + } + + icon_info = caja_icon_info_new_for_icon_info (gtkicon_info); + + key = themed_icon_key_new (filename, size); + g_hash_table_insert (themed_icon_cache, key, icon_info); + + gtk_icon_info_free (gtkicon_info); + + return g_object_ref (icon_info); + } + else + { + GdkPixbuf *pixbuf; + GtkIconInfo *gtk_icon_info; + + gtk_icon_info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), + icon, + size, + GTK_ICON_LOOKUP_GENERIC_FALLBACK); + if (gtk_icon_info != NULL) + { + pixbuf = gtk_icon_info_load_icon (gtk_icon_info, NULL); + gtk_icon_info_free (gtk_icon_info); + } + else + { + pixbuf = NULL; + } + + return caja_icon_info_new_for_pixbuf (pixbuf); + } +} + +CajaIconInfo * +caja_icon_info_lookup_from_name (const char *name, + int size) +{ + GIcon *icon; + CajaIconInfo *info; + + icon = g_themed_icon_new (name); + info = caja_icon_info_lookup (icon, size); + g_object_unref (icon); + return info; +} + +CajaIconInfo * +caja_icon_info_lookup_from_path (const char *path, + int size) +{ + GFile *icon_file; + GIcon *icon; + CajaIconInfo *info; + + icon_file = g_file_new_for_path (path); + icon = g_file_icon_new (icon_file); + info = caja_icon_info_lookup (icon, size); + g_object_unref (icon); + g_object_unref (icon_file); + return info; +} + +GdkPixbuf * +caja_icon_info_get_pixbuf_nodefault (CajaIconInfo *icon) +{ + GdkPixbuf *res; + + if (icon->pixbuf == NULL) + { + res = NULL; + } + else + { + res = g_object_ref (icon->pixbuf); + + if (icon->sole_owner) + { + icon->sole_owner = FALSE; + g_object_add_toggle_ref (G_OBJECT (res), + pixbuf_toggle_notify, + icon); + } + } + + return res; +} + + +GdkPixbuf * +caja_icon_info_get_pixbuf (CajaIconInfo *icon) +{ + GdkPixbuf *res; + + res = caja_icon_info_get_pixbuf_nodefault (icon); + if (res == NULL) + { + res = gdk_pixbuf_new_from_data (caja_default_file_icon, + GDK_COLORSPACE_RGB, + TRUE, + 8, + caja_default_file_icon_width, + caja_default_file_icon_height, + caja_default_file_icon_width * 4, /* stride */ + NULL, /* don't destroy info */ + NULL); + } + + return res; +} + +GdkPixbuf * +caja_icon_info_get_pixbuf_nodefault_at_size (CajaIconInfo *icon, + gsize forced_size) +{ + GdkPixbuf *pixbuf, *scaled_pixbuf; + int w, h, s; + double scale; + + pixbuf = caja_icon_info_get_pixbuf_nodefault (icon); + + if (pixbuf == NULL) + return NULL; + + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + s = MAX (w, h); + if (s == forced_size) + { + return pixbuf; + } + + scale = (double)forced_size / s; + scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf, + w * scale, h * scale, + GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + return scaled_pixbuf; +} + + +GdkPixbuf * +caja_icon_info_get_pixbuf_at_size (CajaIconInfo *icon, + gsize forced_size) +{ + GdkPixbuf *pixbuf, *scaled_pixbuf; + int w, h, s; + double scale; + + pixbuf = caja_icon_info_get_pixbuf (icon); + + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + s = MAX (w, h); + if (s == forced_size) + { + return pixbuf; + } + + scale = (double)forced_size / s; + scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf, + w * scale, h * scale, + GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + return scaled_pixbuf; +} + +gboolean +caja_icon_info_get_embedded_rect (CajaIconInfo *icon, + GdkRectangle *rectangle) +{ + *rectangle = icon->embedded_rect; + return icon->got_embedded_rect; +} + +gboolean +caja_icon_info_get_attach_points (CajaIconInfo *icon, + GdkPoint **points, + gint *n_points) +{ + *n_points = icon->n_attach_points; + *points = icon->attach_points; + return icon->n_attach_points != 0; +} + +const char* caja_icon_info_get_display_name(CajaIconInfo* icon) +{ + return icon->display_name; +} + +const char* caja_icon_info_get_used_name(CajaIconInfo* icon) +{ + return icon->icon_name; +} + +/* Return nominal icon size for given zoom level. + * @zoom_level: zoom level for which to find matching icon size. + * + * Return value: icon size between CAJA_ICON_SIZE_SMALLEST and + * CAJA_ICON_SIZE_LARGEST, inclusive. + */ +guint +caja_get_icon_size_for_zoom_level (CajaZoomLevel zoom_level) +{ + switch (zoom_level) + { + case CAJA_ZOOM_LEVEL_SMALLEST: + return CAJA_ICON_SIZE_SMALLEST; + case CAJA_ZOOM_LEVEL_SMALLER: + return CAJA_ICON_SIZE_SMALLER; + case CAJA_ZOOM_LEVEL_SMALL: + return CAJA_ICON_SIZE_SMALL; + case CAJA_ZOOM_LEVEL_STANDARD: + return CAJA_ICON_SIZE_STANDARD; + case CAJA_ZOOM_LEVEL_LARGE: + return CAJA_ICON_SIZE_LARGE; + case CAJA_ZOOM_LEVEL_LARGER: + return CAJA_ICON_SIZE_LARGER; + case CAJA_ZOOM_LEVEL_LARGEST: + return CAJA_ICON_SIZE_LARGEST; + } + g_return_val_if_reached (CAJA_ICON_SIZE_STANDARD); +} + +float +caja_get_relative_icon_size_for_zoom_level (CajaZoomLevel zoom_level) +{ + return (float)caja_get_icon_size_for_zoom_level (zoom_level) / CAJA_ICON_SIZE_STANDARD; +} + +guint +caja_icon_get_larger_icon_size (guint size) +{ + if (size < CAJA_ICON_SIZE_SMALLEST) + { + return CAJA_ICON_SIZE_SMALLEST; + } + if (size < CAJA_ICON_SIZE_SMALLER) + { + return CAJA_ICON_SIZE_SMALLER; + } + if (size < CAJA_ICON_SIZE_SMALL) + { + return CAJA_ICON_SIZE_SMALL; + } + if (size < CAJA_ICON_SIZE_STANDARD) + { + return CAJA_ICON_SIZE_STANDARD; + } + if (size < CAJA_ICON_SIZE_LARGE) + { + return CAJA_ICON_SIZE_LARGE; + } + if (size < CAJA_ICON_SIZE_LARGER) + { + return CAJA_ICON_SIZE_LARGER; + } + return CAJA_ICON_SIZE_LARGEST; +} + +guint +caja_icon_get_smaller_icon_size (guint size) +{ + if (size > CAJA_ICON_SIZE_LARGEST) + { + return CAJA_ICON_SIZE_LARGEST; + } + if (size > CAJA_ICON_SIZE_LARGER) + { + return CAJA_ICON_SIZE_LARGER; + } + if (size > CAJA_ICON_SIZE_LARGE) + { + return CAJA_ICON_SIZE_LARGE; + } + if (size > CAJA_ICON_SIZE_STANDARD) + { + return CAJA_ICON_SIZE_STANDARD; + } + if (size > CAJA_ICON_SIZE_SMALL) + { + return CAJA_ICON_SIZE_SMALL; + } + if (size > CAJA_ICON_SIZE_SMALLER) + { + return CAJA_ICON_SIZE_SMALLER; + } + return CAJA_ICON_SIZE_SMALLEST; +} + +gint +caja_get_icon_size_for_stock_size (GtkIconSize size) +{ + gint w, h; + + if (gtk_icon_size_lookup (size, &w, &h)) + { + return MAX (w, h); + } + return CAJA_ZOOM_LEVEL_STANDARD; +} + + +guint +caja_icon_get_emblem_size_for_icon_size (guint size) +{ + if (size >= 96) + return 48; + if (size >= 64) + return 32; + if (size >= 48) + return 24; + if (size >= 24) + return 16; + if (size >= 16) + return 12; + + return 0; /* no emblems for smaller sizes */ +} |