+/* The LRU cache is a short list of IconInfos that are kept
+ alive even though their IconInfo would otherwise have
+ been freed, so that we can avoid reloading these
+ constantly.
+ We put infos on the lru list when nothing otherwise
+ references the info. So, when we get a cache hit
+ we remove it from the list, and when the proxy
+ pixmap is released we put it on the list.
+*/
+
+static void
+ensure_lru_cache_space (GtkIconTheme *icon_theme)
+{
+ GtkIconThemePrivate *priv = icon_theme->priv;
+ GList *l;
+
+ /* Remove last item if LRU full */
+ l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
+ if (l)
+ {
+ GtkIconInfo *icon_info = l->data;
+
+ DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
+ icon_info,
+ g_strjoinv (",", icon_info->key.icon_names),
+ icon_info->key.size, icon_info->key.flags,
+ g_list_length (priv->info_cache_lru)));
+
+ priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
+ g_object_unref (icon_info);
+ }
+}
+
+static void
+add_to_lru_cache (GtkIconTheme *icon_theme,
+ GtkIconInfo *icon_info)
+{
+ GtkIconThemePrivate *priv = icon_theme->priv;
+
+ DEBUG_CACHE (("adding %p (%s %d 0x%x) to LRU cache (cache size %d)\n",
+ icon_info,
+ g_strjoinv (",", icon_info->key.icon_names),
+ icon_info->key.size, icon_info->key.flags,
+ g_list_length (priv->info_cache_lru)));
+
+ g_assert (g_list_find (priv->info_cache_lru, icon_info) == NULL);
+
+ ensure_lru_cache_space (icon_theme);
+ /* prepend new info to LRU */
+ priv->info_cache_lru = g_list_prepend (priv->info_cache_lru,
+ g_object_ref (icon_info));
+}
+
+static void
+ensure_in_lru_cache (GtkIconTheme *icon_theme,
+ GtkIconInfo *icon_info)
+{
+ GtkIconThemePrivate *priv = icon_theme->priv;
+ GList *l;
+
+ l = g_list_find (priv->info_cache_lru, icon_info);
+ if (l)
+ {
+ /* Move to front of LRU if already in it */
+ priv->info_cache_lru = g_list_remove_link (priv->info_cache_lru, l);
+ priv->info_cache_lru = g_list_concat (l, priv->info_cache_lru);
+ }
+ else
+ add_to_lru_cache (icon_theme, icon_info);
+}
+
+static void
+remove_from_lru_cache (GtkIconTheme *icon_theme,
+ GtkIconInfo *icon_info)
+{
+ GtkIconThemePrivate *priv = icon_theme->priv;
+ if (g_list_find (priv->info_cache_lru, icon_info))
+ {
+ DEBUG_CACHE (("removing %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
+ icon_info,
+ g_strjoinv (",", icon_info->key.icon_names),
+ icon_info->key.size, icon_info->key.flags,
+ g_list_length (priv->info_cache_lru)));
+
+ priv->info_cache_lru = g_list_remove (priv->info_cache_lru, icon_info);
+ g_object_unref (icon_info);
+ }
+}
+
+static SymbolicPixbufCache *
+symbolic_pixbuf_cache_new (GdkPixbuf *pixbuf,
+ const GdkRGBA *fg,
+ const GdkRGBA *success_color,
+ const GdkRGBA *warning_color,
+ const GdkRGBA *error_color,
+ SymbolicPixbufCache *next)
+{
+ SymbolicPixbufCache *cache;
+
+ cache = g_new0 (SymbolicPixbufCache, 1);
+ cache->pixbuf = g_object_ref (pixbuf);
+ if (fg)
+ cache->fg = *fg;
+ if (success_color)
+ cache->success_color = *success_color;
+ if (warning_color)
+ cache->warning_color = *warning_color;
+ if (error_color)
+ cache->error_color = *error_color;
+ cache->next = next;
+ return cache;
+}
+
+static gboolean
+rgba_matches (const GdkRGBA *a, const GdkRGBA *b)
+{
+ GdkRGBA transparent = { 0 };
+
+ /* For matching we treat unset colors as transparent rather
+ than default, which works as well, because transparent
+ will never be used for real symbolic icon colors */
+ if (a == NULL)
+ a = &transparent;
+
+ return
+ fabs(a->red - b->red) < 0.0001 &&
+ fabs(a->green - b->green) < 0.0001 &&
+ fabs(a->blue - b->blue) < 0.0001 &&
+ fabs(a->alpha - b->alpha) < 0.0001;
+}
+
+static SymbolicPixbufCache *
+symbolic_pixbuf_cache_matches (SymbolicPixbufCache *cache,
+ const GdkRGBA *fg,
+ const GdkRGBA *success_color,
+ const GdkRGBA *warning_color,
+ const GdkRGBA *error_color)
+{
+ while (cache != NULL)
+ {
+ if (rgba_matches (fg, &cache->fg) &&
+ rgba_matches (success_color, &cache->success_color) &&
+ rgba_matches (warning_color, &cache->warning_color) &&
+ rgba_matches (error_color, &cache->error_color))
+ return cache;
+
+ cache = cache->next;
+ }
+
+ return NULL;
+}
+
+static void
+symbolic_pixbuf_cache_free (SymbolicPixbufCache *cache)
+{
+ SymbolicPixbufCache *next;
+
+ while (cache != NULL)
+ {
+ next = cache->next;
+ g_object_unref (cache->pixbuf);
+ g_free (cache);
+
+ cache = next;
+ }
+}
+