1 /* GtkIconTheme - a loader for icon themes
2 * gtk-icon-theme.c Copyright (C) 2002, 2003 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 #include <string.h>
26 #include <stdlib.h>
27 #include <math.h>
28 #include <glib.h>
29 #include <glib/gstdio.h>
30
31 #ifdef G_OS_WIN32
32 #ifndef S_ISDIR
33 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
34 #endif
35 #define WIN32_LEAN_AND_MEAN
36 #include <windows.h>
37 #include <shellapi.h>
38 #include "win32/gdkwin32.h"
39 #endif /* G_OS_WIN32 */
40
41 #include "gtkiconthemeprivate.h"
42 #include "gtkcsspalettevalueprivate.h"
43 #include "gtkcssrgbavalueprivate.h"
44 #include "gtkdebug.h"
45 #include "deprecated/gtkiconfactory.h"
46 #include "gtkiconcache.h"
47 #include "gtkintl.h"
48 #include "gtkmain.h"
49 #include "deprecated/gtknumerableiconprivate.h"
50 #include "gtksettingsprivate.h"
51 #include "gtkstylecontextprivate.h"
52 #include "gtkprivate.h"
53 #include "gdkpixbufutilsprivate.h"
54
55 #undef GDK_DEPRECATED
56 #undef GDK_DEPRECATED_FOR
57 #define GDK_DEPRECATED
58 #define GDK_DEPRECATED_FOR(f)
59
60 #include "deprecated/gtkstyle.h"
61
62 /* this is in case round() is not provided by the compiler,
63 * such as in the case of C89 compilers, like MSVC
64 */
65 #include "fallback-c89.c"
66
67 /**
68 * SECTION:gtkicontheme
69 * @Short_description: Looking up icons by name
70 * @Title: GtkIconTheme
71 *
72 * #GtkIconTheme provides a facility for looking up icons by name
73 * and size. The main reason for using a name rather than simply
74 * providing a filename is to allow different icons to be used
75 * depending on what “icon theme” is selected
76 * by the user. The operation of icon themes on Linux and Unix
77 * follows the [Icon Theme Specification](http://www.freedesktop.org/Standards/icon-theme-spec)
78 * There is a fallback icon theme, named `hicolor`, where applications
79 * should install their icons, but additional icon themes can be installed
80 * as operating system vendors and users choose.
81 *
82 * Named icons are similar to the deprecated [Stock Items][gtkstock],
83 * and the distinction between the two may be a bit confusing.
84 * A few things to keep in mind:
85 *
86 * - Stock images usually are used in conjunction with
87 * [Stock Items][gtkstock], such as %GTK_STOCK_OK or
88 * %GTK_STOCK_OPEN. Named icons are easier to set up and therefore
89 * are more useful for new icons that an application wants to
90 * add, such as application icons or window icons.
91 *
92 * - Stock images can only be loaded at the symbolic sizes defined
93 * by the #GtkIconSize enumeration, or by custom sizes defined
94 * by gtk_icon_size_register(), while named icons are more flexible
95 * and any pixel size can be specified.
96 *
97 * - Because stock images are closely tied to stock items, and thus
98 * to actions in the user interface, stock images may come in
99 * multiple variants for different widget states or writing
100 * directions.
101 *
102 * A good rule of thumb is that if there is a stock image for what
103 * you want to use, use it, otherwise use a named icon. It turns
104 * out that internally stock images are generally defined in
105 * terms of one or more named icons. (An example of the
106 * more than one case is icons that depend on writing direction;
107 * %GTK_STOCK_GO_FORWARD uses the two themed icons
108 * “gtk-stock-go-forward-ltr” and “gtk-stock-go-forward-rtl”.)
109 *
110 * In many cases, named themes are used indirectly, via #GtkImage
111 * or stock items, rather than directly, but looking up icons
112 * directly is also simple. The #GtkIconTheme object acts
113 * as a database of all the icons in the current theme. You
114 * can create new #GtkIconTheme objects, but it’s much more
115 * efficient to use the standard icon theme for the #GdkScreen
116 * so that the icon information is shared with other people
117 * looking up icons.
118 * |[<!-- language="C" -->
119 * GError *error = NULL;
120 * GtkIconTheme *icon_theme;
121 * GdkPixbuf *pixbuf;
122 *
123 * icon_theme = gtk_icon_theme_get_default ();
124 * pixbuf = gtk_icon_theme_load_icon (icon_theme,
125 * "my-icon-name", // icon name
126 * 48, // icon size
127 * 0, // flags
128 * &error);
129 * if (!pixbuf)
130 * {
131 * g_warning ("Couldn’t load icon: %s", error->message);
132 * g_error_free (error);
133 * }
134 * else
135 * {
136 * // Use the pixbuf
137 * g_object_unref (pixbuf);
138 * }
139 * ]|
140 */
141
142 #define FALLBACK_ICON_THEME "hicolor"
143
144 typedef enum
145 {
146 ICON_THEME_DIR_FIXED,
147 ICON_THEME_DIR_SCALABLE,
148 ICON_THEME_DIR_THRESHOLD,
149 ICON_THEME_DIR_UNTHEMED
150 } IconThemeDirType;
151
152 /* In reverse search order: */
153 typedef enum
154 {
155 ICON_SUFFIX_NONE = 0,
156 ICON_SUFFIX_XPM = 1 << 0,
157 ICON_SUFFIX_SVG = 1 << 1,
158 ICON_SUFFIX_PNG = 1 << 2,
159 HAS_ICON_FILE = 1 << 3,
160 ICON_SUFFIX_SYMBOLIC_PNG = 1 << 4
161 } IconSuffix;
162
163 #define INFO_CACHE_LRU_SIZE 32
164 #if 0
165 #define DEBUG_CACHE(args) g_print args
166 #else
167 #define DEBUG_CACHE(args)
168 #endif
169
170 struct _GtkIconThemePrivate
171 {
172 GHashTable *info_cache;
173 GList *info_cache_lru;
174
175 gchar *current_theme;
176 gchar **search_path;
177 gint search_path_len;
178 GList *resource_paths;
179
180 guint custom_theme : 1;
181 guint is_screen_singleton : 1;
182 guint pixbuf_supports_svg : 1;
183 guint themes_valid : 1;
184 guint loading_themes : 1;
185
186 /* A list of all the themes needed to look up icons.
187 * In search order, without duplicates
188 */
189 GList *themes;
190 GHashTable *unthemed_icons;
191
192 /* GdkScreen for the icon theme (may be NULL) */
193 GdkScreen *screen;
194
195 /* time when we last stat:ed for theme changes */
196 glong last_stat_time;
197 GList *dir_mtimes;
198
199 gulong theme_changed_idle;
200 };
201
202 typedef struct {
203 gchar **icon_names;
204 gint size;
205 gint scale;
206 GtkIconLookupFlags flags;
207 } IconInfoKey;
208
209 typedef struct _SymbolicPixbufCache SymbolicPixbufCache;
210
211 struct _SymbolicPixbufCache {
212 GdkPixbuf *pixbuf;
213 GdkPixbuf *proxy_pixbuf;
214 GdkRGBA fg;
215 GdkRGBA success_color;
216 GdkRGBA warning_color;
217 GdkRGBA error_color;
218 SymbolicPixbufCache *next;
219 };
220
221 struct _GtkIconInfoClass
222 {
223 GObjectClass parent_class;
224 };
225
226 struct _GtkIconInfo
227 {
228 GObject parent_instance;
229
230 /* Information about the source
231 */
232 IconInfoKey key;
233 GtkIconTheme *in_cache;
234
235 gchar *filename;
236 GFile *icon_file;
237 GLoadableIcon *loadable;
238 GSList *emblem_infos;
239
240 /* Cache pixbuf (if there is any) */
241 GdkPixbuf *cache_pixbuf;
242
243 /* Information about the directory where
244 * the source was found
245 */
246 IconThemeDirType dir_type;
247 gint dir_size;
248 gint dir_scale;
249 gint min_size;
250 gint max_size;
251
252 /* Parameters influencing the scaled icon
253 */
254 gint desired_size;
255 gint desired_scale;
256 guint forced_size : 1;
257 guint emblems_applied : 1;
258 guint is_svg : 1;
259 guint is_resource : 1;
260
261 /* Cached information if we go ahead and try to load
262 * the icon.
263 */
264 GdkPixbuf *pixbuf;
265 GdkPixbuf *proxy_pixbuf;
266 GError *load_error;
267 gdouble unscaled_scale;
268 gdouble scale;
269
270 SymbolicPixbufCache *symbolic_pixbuf_cache;
271
272 gint symbolic_width;
273 gint symbolic_height;
274 };
275
276 typedef struct
277 {
278 gchar *name;
279 gchar *display_name;
280 gchar *comment;
281 gchar *example;
282
283 /* In search order */
284 GList *dirs;
285 } IconTheme;
286
287 typedef struct
288 {
289 IconThemeDirType type;
290 GQuark context;
291
292 gint size;
293 gint min_size;
294 gint max_size;
295 gint threshold;
296 gint scale;
297 gboolean is_resource;
298
299 gchar *dir;
300 gchar *subdir;
301 gint subdir_index;
302
303 GtkIconCache *cache;
304
305 GHashTable *icons;
306 } IconThemeDir;
307
308 typedef struct
309 {
310 gchar *svg_filename;
311 gchar *no_svg_filename;
312 gboolean is_resource;
313 } UnthemedIcon;
314
315 typedef struct
316 {
317 gint size;
318 GdkPixbuf *pixbuf;
319 } BuiltinIcon;
320
321 typedef struct
322 {
323 gchar *dir;
324 time_t mtime;
325 GtkIconCache *cache;
326 gboolean exists;
327 } IconThemeDirMtime;
328
329 static void gtk_icon_theme_finalize (GObject *object);
330 static void theme_dir_destroy (IconThemeDir *dir);
331 static void theme_destroy (IconTheme *theme);
332 static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
333 const gchar *icon_name,
334 gint size,
335 gint scale,
336 gboolean allow_svg,
337 gboolean use_default_icons);
338 static void theme_list_icons (IconTheme *theme,
339 GHashTable *icons,
340 GQuark context);
341 static gboolean theme_has_icon (IconTheme *theme,
342 const gchar *icon_name);
343 static void theme_list_contexts (IconTheme *theme,
344 GHashTable *contexts);
345 static void theme_subdir_load (GtkIconTheme *icon_theme,
346 IconTheme *theme,
347 GKeyFile *theme_file,
348 gchar *subdir);
349 static void do_theme_change (GtkIconTheme *icon_theme);
350 static void blow_themes (GtkIconTheme *icon_themes);
351 static gboolean rescan_themes (GtkIconTheme *icon_themes);
352 static IconSuffix theme_dir_get_icon_suffix (IconThemeDir *dir,
353 const gchar *icon_name,
354 gboolean *has_icon_file);
355 static GtkIconInfo *icon_info_new (IconThemeDirType type,
356 gint dir_size,
357 gint dir_scale);
358 static GtkIconInfo *icon_info_new_builtin (BuiltinIcon *icon);
359 static IconSuffix suffix_from_name (const gchar *name);
360 static BuiltinIcon *find_builtin_icon (const gchar *icon_name,
361 gint size,
362 gint scale,
363 gint *min_difference_p);
364 static void remove_from_lru_cache (GtkIconTheme *icon_theme,
365 GtkIconInfo *icon_info);
366 static gboolean icon_info_ensure_scale_and_pixbuf (GtkIconInfo* icon_info);
367
368 static guint signal_changed = 0;
369
370 static GHashTable *icon_theme_builtin_icons;
371
372 static guint
icon_info_key_hash(gconstpointer _key)373 icon_info_key_hash (gconstpointer _key)
374 {
375 const IconInfoKey *key = _key;
376 guint h = 0;
377 int i;
378 for (i = 0; key->icon_names[i] != NULL; i++)
379 h ^= g_str_hash (key->icon_names[i]);
380
381 h ^= key->size * 0x10001;
382 h ^= key->scale * 0x1000010;
383 h ^= key->flags * 0x100000100;
384
385 return h;
386 }
387
388 static gboolean
icon_info_key_equal(gconstpointer _a,gconstpointer _b)389 icon_info_key_equal (gconstpointer _a,
390 gconstpointer _b)
391 {
392 const IconInfoKey *a = _a;
393 const IconInfoKey *b = _b;
394 int i;
395
396 if (a->size != b->size)
397 return FALSE;
398
399 if (a->scale != b->scale)
400 return FALSE;
401
402 if (a->flags != b->flags)
403 return FALSE;
404
405 for (i = 0;
406 a->icon_names[i] != NULL &&
407 b->icon_names[i] != NULL; i++)
408 {
409 if (strcmp (a->icon_names[i], b->icon_names[i]) != 0)
410 return FALSE;
411 }
412
413 return a->icon_names[i] == NULL && b->icon_names[i] == NULL;
414 }
415
G_DEFINE_TYPE_WITH_PRIVATE(GtkIconTheme,gtk_icon_theme,G_TYPE_OBJECT)416 G_DEFINE_TYPE_WITH_PRIVATE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
417
418 /**
419 * gtk_icon_theme_new:
420 *
421 * Creates a new icon theme object. Icon theme objects are used
422 * to lookup up an icon by name in a particular icon theme.
423 * Usually, you’ll want to use gtk_icon_theme_get_default()
424 * or gtk_icon_theme_get_for_screen() rather than creating
425 * a new icon theme object for scratch.
426 *
427 * Returns: the newly created #GtkIconTheme object.
428 *
429 * Since: 2.4
430 */
431 GtkIconTheme *
432 gtk_icon_theme_new (void)
433 {
434 return g_object_new (GTK_TYPE_ICON_THEME, NULL);
435 }
436
437 /**
438 * gtk_icon_theme_get_default:
439 *
440 * Gets the icon theme for the default screen. See
441 * gtk_icon_theme_get_for_screen().
442 *
443 * Returns: (transfer none): A unique #GtkIconTheme associated with
444 * the default screen. This icon theme is associated with
445 * the screen and can be used as long as the screen
446 * is open. Do not ref or unref it.
447 *
448 * Since: 2.4
449 */
450 GtkIconTheme *
gtk_icon_theme_get_default(void)451 gtk_icon_theme_get_default (void)
452 {
453 return gtk_icon_theme_get_for_screen (gdk_screen_get_default ());
454 }
455
456 /**
457 * gtk_icon_theme_get_for_screen:
458 * @screen: a #GdkScreen
459 *
460 * Gets the icon theme object associated with @screen; if this
461 * function has not previously been called for the given
462 * screen, a new icon theme object will be created and
463 * associated with the screen. Icon theme objects are
464 * fairly expensive to create, so using this function
465 * is usually a better choice than calling than gtk_icon_theme_new()
466 * and setting the screen yourself; by using this function
467 * a single icon theme object will be shared between users.
468 *
469 * Returns: (transfer none): A unique #GtkIconTheme associated with
470 * the given screen. This icon theme is associated with
471 * the screen and can be used as long as the screen
472 * is open. Do not ref or unref it.
473 *
474 * Since: 2.4
475 */
476 GtkIconTheme *
gtk_icon_theme_get_for_screen(GdkScreen * screen)477 gtk_icon_theme_get_for_screen (GdkScreen *screen)
478 {
479 GtkIconTheme *icon_theme;
480
481 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
482
483 icon_theme = g_object_get_data (G_OBJECT (screen), "gtk-icon-theme");
484 if (!icon_theme)
485 {
486 GtkIconThemePrivate *priv;
487
488 icon_theme = gtk_icon_theme_new ();
489 gtk_icon_theme_set_screen (icon_theme, screen);
490
491 priv = icon_theme->priv;
492 priv->is_screen_singleton = TRUE;
493
494 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), icon_theme);
495 }
496
497 return icon_theme;
498 }
499
500 static void
gtk_icon_theme_class_init(GtkIconThemeClass * klass)501 gtk_icon_theme_class_init (GtkIconThemeClass *klass)
502 {
503 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
504
505 gobject_class->finalize = gtk_icon_theme_finalize;
506
507 /**
508 * GtkIconTheme::changed:
509 * @icon_theme: the icon theme
510 *
511 * Emitted when the current icon theme is switched or GTK+ detects
512 * that a change has occurred in the contents of the current
513 * icon theme.
514 */
515 signal_changed = g_signal_new (I_("changed"),
516 G_TYPE_FROM_CLASS (klass),
517 G_SIGNAL_RUN_LAST,
518 G_STRUCT_OFFSET (GtkIconThemeClass, changed),
519 NULL, NULL,
520 NULL,
521 G_TYPE_NONE, 0);
522 }
523
524
525 /* Callback when the display that the icon theme is attached
526 * to is closed; unset the screen, and if it’s the unique theme
527 * for the screen, drop the reference
528 */
529 static void
display_closed(GdkDisplay * display,gboolean is_error,GtkIconTheme * icon_theme)530 display_closed (GdkDisplay *display,
531 gboolean is_error,
532 GtkIconTheme *icon_theme)
533 {
534 GtkIconThemePrivate *priv = icon_theme->priv;
535 GdkScreen *screen = priv->screen;
536 gboolean was_screen_singleton = priv->is_screen_singleton;
537
538 if (was_screen_singleton)
539 {
540 g_object_set_data (G_OBJECT (screen), I_("gtk-icon-theme"), NULL);
541 priv->is_screen_singleton = FALSE;
542 }
543
544 gtk_icon_theme_set_screen (icon_theme, NULL);
545
546 if (was_screen_singleton)
547 {
548 g_object_unref (icon_theme);
549 }
550 }
551
552 static void
update_current_theme(GtkIconTheme * icon_theme)553 update_current_theme (GtkIconTheme *icon_theme)
554 {
555 #define theme_changed(_old, _new) \
556 ((_old && !_new) || (!_old && _new) || \
557 (_old && _new && strcmp (_old, _new) != 0))
558 GtkIconThemePrivate *priv = icon_theme->priv;
559
560 if (!priv->custom_theme)
561 {
562 gchar *theme = NULL;
563 gboolean changed = FALSE;
564
565 if (priv->screen)
566 {
567 GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
568 g_object_get (settings, "gtk-icon-theme-name", &theme, NULL);
569 }
570
571 if (theme_changed (priv->current_theme, theme))
572 {
573 g_free (priv->current_theme);
574 priv->current_theme = theme;
575 changed = TRUE;
576 }
577 else
578 g_free (theme);
579
580 if (changed)
581 do_theme_change (icon_theme);
582 }
583 #undef theme_changed
584 }
585
586 /* Callback when the icon theme GtkSetting changes
587 */
588 static void
theme_changed(GtkSettings * settings,GParamSpec * pspec,GtkIconTheme * icon_theme)589 theme_changed (GtkSettings *settings,
590 GParamSpec *pspec,
591 GtkIconTheme *icon_theme)
592 {
593 update_current_theme (icon_theme);
594 }
595
596 static void
unset_screen(GtkIconTheme * icon_theme)597 unset_screen (GtkIconTheme *icon_theme)
598 {
599 GtkIconThemePrivate *priv = icon_theme->priv;
600 GtkSettings *settings;
601 GdkDisplay *display;
602
603 if (priv->screen)
604 {
605 settings = gtk_settings_get_for_screen (priv->screen);
606 display = gdk_screen_get_display (priv->screen);
607
608 g_signal_handlers_disconnect_by_func (display,
609 (gpointer) display_closed,
610 icon_theme);
611 if (settings)
612 g_signal_handlers_disconnect_by_func (settings,
613 (gpointer) theme_changed,
614 icon_theme);
615
616 priv->screen = NULL;
617 }
618 }
619
620 /**
621 * gtk_icon_theme_set_screen:
622 * @icon_theme: a #GtkIconTheme
623 * @screen: a #GdkScreen
624 *
625 * Sets the screen for an icon theme; the screen is used
626 * to track the user’s currently configured icon theme,
627 * which might be different for different screens.
628 *
629 * Since: 2.4
630 */
631 void
gtk_icon_theme_set_screen(GtkIconTheme * icon_theme,GdkScreen * screen)632 gtk_icon_theme_set_screen (GtkIconTheme *icon_theme,
633 GdkScreen *screen)
634 {
635 GtkIconThemePrivate *priv;
636 GtkSettings *settings;
637 GdkDisplay *display;
638
639 g_return_if_fail (GTK_ICON_THEME (icon_theme));
640 g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
641
642 priv = icon_theme->priv;
643
644 unset_screen (icon_theme);
645
646 if (screen)
647 {
648 display = gdk_screen_get_display (screen);
649 settings = gtk_settings_get_for_screen (screen);
650
651 priv->screen = screen;
652
653 g_signal_connect (display, "closed",
654 G_CALLBACK (display_closed), icon_theme);
655 g_signal_connect (settings, "notify::gtk-icon-theme-name",
656 G_CALLBACK (theme_changed), icon_theme);
657 }
658
659 update_current_theme (icon_theme);
660 }
661
662 /* Checks whether a loader for SVG files has been registered
663 * with GdkPixbuf.
664 */
665 static gboolean
pixbuf_supports_svg(void)666 pixbuf_supports_svg (void)
667 {
668 GSList *formats;
669 GSList *tmp_list;
670 static gint found_svg = -1;
671
672 if (found_svg != -1)
673 return found_svg;
674
675 formats = gdk_pixbuf_get_formats ();
676
677 found_svg = FALSE;
678 for (tmp_list = formats; tmp_list && !found_svg; tmp_list = tmp_list->next)
679 {
680 gchar **mime_types = gdk_pixbuf_format_get_mime_types (tmp_list->data);
681 gchar **mime_type;
682
683 for (mime_type = mime_types; *mime_type && !found_svg; mime_type++)
684 {
685 if (strcmp (*mime_type, "image/svg") == 0)
686 found_svg = TRUE;
687 }
688
689 g_strfreev (mime_types);
690 }
691
692 g_slist_free (formats);
693
694 return found_svg;
695 }
696
697 /* The icon info was removed from the icon_info_hash hash table */
698 static void
icon_info_uncached(GtkIconInfo * icon_info)699 icon_info_uncached (GtkIconInfo *icon_info)
700 {
701 GtkIconTheme *icon_theme = icon_info->in_cache;
702
703 DEBUG_CACHE (("removing %p (%s %d 0x%x) from cache (icon_them: %p) (cache size %d)\n",
704 icon_info,
705 g_strjoinv (",", icon_info->key.icon_names),
706 icon_info->key.size, icon_info->key.flags,
707 icon_theme,
708 icon_theme != NULL ? g_hash_table_size (icon_theme->priv->info_cache) : 0));
709
710 icon_info->in_cache = NULL;
711
712 if (icon_theme != NULL)
713 remove_from_lru_cache (icon_theme, icon_info);
714 }
715
716 static void
gtk_icon_theme_init(GtkIconTheme * icon_theme)717 gtk_icon_theme_init (GtkIconTheme *icon_theme)
718 {
719 GtkIconThemePrivate *priv;
720 const gchar * const *xdg_data_dirs;
721 int i, j;
722
723 priv = gtk_icon_theme_get_instance_private (icon_theme);
724 icon_theme->priv = priv;
725
726 priv->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
727 (GDestroyNotify)icon_info_uncached);
728
729 priv->custom_theme = FALSE;
730
731 xdg_data_dirs = g_get_system_data_dirs ();
732 for (i = 0; xdg_data_dirs[i]; i++) ;
733
734 priv->search_path_len = 2 * i + 2;
735
736 priv->search_path = g_new (char *, priv->search_path_len);
737
738 i = 0;
739 priv->search_path[i++] = g_build_filename (g_get_user_data_dir (), "icons", NULL);
740 priv->search_path[i++] = g_build_filename (g_get_home_dir (), ".icons", NULL);
741
742 for (j = 0; xdg_data_dirs[j]; j++)
743 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "icons", NULL);
744
745 for (j = 0; xdg_data_dirs[j]; j++)
746 priv->search_path[i++] = g_build_filename (xdg_data_dirs[j], "pixmaps", NULL);
747
748 priv->resource_paths = g_list_append (NULL, g_strdup ("/org/gtk/libgtk/icons/"));
749
750 priv->themes_valid = FALSE;
751 priv->themes = NULL;
752 priv->unthemed_icons = NULL;
753
754 priv->pixbuf_supports_svg = pixbuf_supports_svg ();
755 }
756
757 static void
free_dir_mtime(IconThemeDirMtime * dir_mtime)758 free_dir_mtime (IconThemeDirMtime *dir_mtime)
759 {
760 if (dir_mtime->cache)
761 _gtk_icon_cache_unref (dir_mtime->cache);
762
763 g_free (dir_mtime->dir);
764 g_slice_free (IconThemeDirMtime, dir_mtime);
765 }
766
767 static gboolean
theme_changed_idle(gpointer user_data)768 theme_changed_idle (gpointer user_data)
769 {
770 GtkIconTheme *icon_theme;
771 GtkIconThemePrivate *priv;
772
773 icon_theme = GTK_ICON_THEME (user_data);
774 priv = icon_theme->priv;
775
776 g_signal_emit (icon_theme, signal_changed, 0);
777
778 if (priv->screen && priv->is_screen_singleton)
779 gtk_style_context_reset_widgets (priv->screen);
780
781 priv->theme_changed_idle = 0;
782
783 return FALSE;
784 }
785
786 static void
queue_theme_changed(GtkIconTheme * icon_theme)787 queue_theme_changed (GtkIconTheme *icon_theme)
788 {
789 GtkIconThemePrivate *priv = icon_theme->priv;
790
791 if (!priv->theme_changed_idle)
792 {
793 priv->theme_changed_idle =
794 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2,
795 theme_changed_idle, icon_theme, NULL);
796 g_source_set_name_by_id (priv->theme_changed_idle, "[gtk+] theme_changed_idle");
797 }
798 }
799
800 static void
do_theme_change(GtkIconTheme * icon_theme)801 do_theme_change (GtkIconTheme *icon_theme)
802 {
803 GtkIconThemePrivate *priv = icon_theme->priv;
804
805 g_hash_table_remove_all (priv->info_cache);
806
807 if (!priv->themes_valid)
808 return;
809
810 GTK_NOTE (ICONTHEME,
811 g_message ("change to icon theme \"%s\"", priv->current_theme));
812 blow_themes (icon_theme);
813
814 queue_theme_changed (icon_theme);
815
816 }
817
818 static void
blow_themes(GtkIconTheme * icon_theme)819 blow_themes (GtkIconTheme *icon_theme)
820 {
821 GtkIconThemePrivate *priv = icon_theme->priv;
822
823 if (priv->themes_valid)
824 {
825 g_list_free_full (priv->themes, (GDestroyNotify) theme_destroy);
826 g_list_free_full (priv->dir_mtimes, (GDestroyNotify) free_dir_mtime);
827 g_hash_table_destroy (priv->unthemed_icons);
828 }
829 priv->themes = NULL;
830 priv->unthemed_icons = NULL;
831 priv->dir_mtimes = NULL;
832 priv->themes_valid = FALSE;
833 }
834
835 static void
gtk_icon_theme_finalize(GObject * object)836 gtk_icon_theme_finalize (GObject *object)
837 {
838 GtkIconTheme *icon_theme;
839 GtkIconThemePrivate *priv;
840 int i;
841
842 icon_theme = GTK_ICON_THEME (object);
843 priv = icon_theme->priv;
844
845 g_hash_table_destroy (priv->info_cache);
846 g_assert (priv->info_cache_lru == NULL);
847
848 if (priv->theme_changed_idle)
849 g_source_remove (priv->theme_changed_idle);
850
851 unset_screen (icon_theme);
852
853 g_free (priv->current_theme);
854
855 for (i = 0; i < priv->search_path_len; i++)
856 g_free (priv->search_path[i]);
857 g_free (priv->search_path);
858
859 g_list_free_full (priv->resource_paths, g_free);
860
861 blow_themes (icon_theme);
862
863 G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);
864 }
865
866 /**
867 * gtk_icon_theme_set_search_path:
868 * @icon_theme: a #GtkIconTheme
869 * @path: (array length=n_elements) (element-type filename): array of
870 * directories that are searched for icon themes
871 * @n_elements: number of elements in @path.
872 *
873 * Sets the search path for the icon theme object. When looking
874 * for an icon theme, GTK+ will search for a subdirectory of
875 * one or more of the directories in @path with the same name
876 * as the icon theme containing an index.theme file. (Themes from
877 * multiple of the path elements are combined to allow themes to be
878 * extended by adding icons in the user’s home directory.)
879 *
880 * In addition if an icon found isn’t found either in the current
881 * icon theme or the default icon theme, and an image file with
882 * the right name is found directly in one of the elements of
883 * @path, then that image will be used for the icon name.
884 * (This is legacy feature, and new icons should be put
885 * into the fallback icon theme, which is called hicolor,
886 * rather than directly on the icon path.)
887 *
888 * Since: 2.4
889 */
890 void
gtk_icon_theme_set_search_path(GtkIconTheme * icon_theme,const gchar * path[],gint n_elements)891 gtk_icon_theme_set_search_path (GtkIconTheme *icon_theme,
892 const gchar *path[],
893 gint n_elements)
894 {
895 GtkIconThemePrivate *priv;
896 gint i;
897
898 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
899
900 priv = icon_theme->priv;
901 for (i = 0; i < priv->search_path_len; i++)
902 g_free (priv->search_path[i]);
903
904 g_free (priv->search_path);
905
906 priv->search_path = g_new (gchar *, n_elements);
907 priv->search_path_len = n_elements;
908
909 for (i = 0; i < priv->search_path_len; i++)
910 priv->search_path[i] = g_strdup (path[i]);
911
912 do_theme_change (icon_theme);
913 }
914
915 /**
916 * gtk_icon_theme_get_search_path:
917 * @icon_theme: a #GtkIconTheme
918 * @path: (allow-none) (array length=n_elements) (element-type filename) (out):
919 * location to store a list of icon theme path directories or %NULL.
920 * The stored value should be freed with g_strfreev().
921 * @n_elements: location to store number of elements in @path, or %NULL
922 *
923 * Gets the current search path. See gtk_icon_theme_set_search_path().
924 *
925 * Since: 2.4
926 */
927 void
gtk_icon_theme_get_search_path(GtkIconTheme * icon_theme,gchar ** path[],gint * n_elements)928 gtk_icon_theme_get_search_path (GtkIconTheme *icon_theme,
929 gchar **path[],
930 gint *n_elements)
931 {
932 GtkIconThemePrivate *priv;
933 gint i;
934
935 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
936
937 priv = icon_theme->priv;
938
939 if (n_elements)
940 *n_elements = priv->search_path_len;
941
942 if (path)
943 {
944 *path = g_new (gchar *, priv->search_path_len + 1);
945 for (i = 0; i < priv->search_path_len; i++)
946 (*path)[i] = g_strdup (priv->search_path[i]);
947 (*path)[i] = NULL;
948 }
949 }
950
951 /**
952 * gtk_icon_theme_append_search_path:
953 * @icon_theme: a #GtkIconTheme
954 * @path: (type filename): directory name to append to the icon path
955 *
956 * Appends a directory to the search path.
957 * See gtk_icon_theme_set_search_path().
958 *
959 * Since: 2.4
960 */
961 void
gtk_icon_theme_append_search_path(GtkIconTheme * icon_theme,const gchar * path)962 gtk_icon_theme_append_search_path (GtkIconTheme *icon_theme,
963 const gchar *path)
964 {
965 GtkIconThemePrivate *priv;
966
967 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
968 g_return_if_fail (path != NULL);
969
970 priv = icon_theme->priv;
971
972 priv->search_path_len++;
973
974 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
975 priv->search_path[priv->search_path_len-1] = g_strdup (path);
976
977 do_theme_change (icon_theme);
978 }
979
980 /**
981 * gtk_icon_theme_prepend_search_path:
982 * @icon_theme: a #GtkIconTheme
983 * @path: (type filename): directory name to prepend to the icon path
984 *
985 * Prepends a directory to the search path.
986 * See gtk_icon_theme_set_search_path().
987 *
988 * Since: 2.4
989 */
990 void
gtk_icon_theme_prepend_search_path(GtkIconTheme * icon_theme,const gchar * path)991 gtk_icon_theme_prepend_search_path (GtkIconTheme *icon_theme,
992 const gchar *path)
993 {
994 GtkIconThemePrivate *priv;
995 gint i;
996
997 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
998 g_return_if_fail (path != NULL);
999
1000 priv = icon_theme->priv;
1001
1002 priv->search_path_len++;
1003 priv->search_path = g_renew (gchar *, priv->search_path, priv->search_path_len);
1004
1005 for (i = priv->search_path_len - 1; i > 0; i--)
1006 priv->search_path[i] = priv->search_path[i - 1];
1007
1008 priv->search_path[0] = g_strdup (path);
1009
1010 do_theme_change (icon_theme);
1011 }
1012
1013 /**
1014 * gtk_icon_theme_add_resource_path:
1015 * @icon_theme: a #GtkIconTheme
1016 * @path: a resource path
1017 *
1018 * Adds a resource path that will be looked at when looking
1019 * for icons, similar to search paths.
1020 *
1021 * This function should be used to make application-specific icons
1022 * available as part of the icon theme.
1023 *
1024 * The resources are considered as part of the hicolor icon theme
1025 * and must be located in subdirectories that are defined in the
1026 * hicolor icon theme, such as `@path/16x16/actions/run.png`.
1027 * Icons that are directly placed in the resource path instead
1028 * of a subdirectory are also considered as ultimate fallback.
1029 *
1030 * Since: 3.14
1031 */
1032 void
gtk_icon_theme_add_resource_path(GtkIconTheme * icon_theme,const gchar * path)1033 gtk_icon_theme_add_resource_path (GtkIconTheme *icon_theme,
1034 const gchar *path)
1035 {
1036 GtkIconThemePrivate *priv = NULL;
1037
1038 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1039 g_return_if_fail (path != NULL);
1040
1041 priv = icon_theme->priv;
1042 priv->resource_paths = g_list_append (priv->resource_paths, g_strdup (path));
1043
1044 do_theme_change (icon_theme);
1045 }
1046
1047 /**
1048 * gtk_icon_theme_set_custom_theme:
1049 * @icon_theme: a #GtkIconTheme
1050 * @theme_name: (allow-none): name of icon theme to use instead of
1051 * configured theme, or %NULL to unset a previously set custom theme
1052 *
1053 * Sets the name of the icon theme that the #GtkIconTheme object uses
1054 * overriding system configuration. This function cannot be called
1055 * on the icon theme objects returned from gtk_icon_theme_get_default()
1056 * and gtk_icon_theme_get_for_screen().
1057 *
1058 * Since: 2.4
1059 */
1060 void
gtk_icon_theme_set_custom_theme(GtkIconTheme * icon_theme,const gchar * theme_name)1061 gtk_icon_theme_set_custom_theme (GtkIconTheme *icon_theme,
1062 const gchar *theme_name)
1063 {
1064 GtkIconThemePrivate *priv;
1065
1066 g_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1067
1068 priv = icon_theme->priv;
1069
1070 g_return_if_fail (!priv->is_screen_singleton);
1071
1072 if (theme_name != NULL)
1073 {
1074 priv->custom_theme = TRUE;
1075 if (!priv->current_theme || strcmp (theme_name, priv->current_theme) != 0)
1076 {
1077 g_free (priv->current_theme);
1078 priv->current_theme = g_strdup (theme_name);
1079
1080 do_theme_change (icon_theme);
1081 }
1082 }
1083 else
1084 {
1085 if (priv->custom_theme)
1086 {
1087 priv->custom_theme = FALSE;
1088 update_current_theme (icon_theme);
1089 }
1090 }
1091 }
1092
1093 static const gchar builtin_hicolor_index[] =
1094 "[Icon Theme]\n"
1095 "Name=Hicolor\n"
1096 "Hidden=True\n"
1097 "Directories=16x16/actions,16x16/status,22x22/actions,24x24/actions,24x24/status,32x32/actions,32x32/status,48x48/status,64x64/actions\n"
1098 "[16x16/actions]\n"
1099 "Size=16\n"
1100 "Type=Threshold\n"
1101 "[16x16/status]\n"
1102 "Size=16\n"
1103 "Type=Threshold\n"
1104 "[22x22/actions]\n"
1105 "Size=22\n"
1106 "Type=Threshold\n"
1107 "[24x24/actions]\n"
1108 "Size=24\n"
1109 "Type=Threshold\n"
1110 "[24x24/status]\n"
1111 "Size=24\n"
1112 "Type=Threshold\n"
1113 "[32x32/actions]\n"
1114 "Size=32\n"
1115 "Type=Threshold\n"
1116 "[32x32/status]\n"
1117 "Size=32\n"
1118 "Type=Threshold\n"
1119 "[48x48/status]\n"
1120 "Size=48\n"
1121 "Type=Threshold\n"
1122 "[64x64/actions]\n"
1123 "Size=64\n"
1124 "Type=Threshold\n";
1125
1126 static void
insert_theme(GtkIconTheme * icon_theme,const gchar * theme_name)1127 insert_theme (GtkIconTheme *icon_theme,
1128 const gchar *theme_name)
1129 {
1130 gint i;
1131 GList *l;
1132 gchar **dirs;
1133 gchar **scaled_dirs;
1134 gchar **themes;
1135 GtkIconThemePrivate *priv;
1136 IconTheme *theme = NULL;
1137 gchar *path;
1138 GKeyFile *theme_file;
1139 GError *error = NULL;
1140 IconThemeDirMtime *dir_mtime;
1141 GStatBuf stat_buf;
1142
1143 priv = icon_theme->priv;
1144
1145 for (l = priv->themes; l != NULL; l = l->next)
1146 {
1147 theme = l->data;
1148 if (strcmp (theme->name, theme_name) == 0)
1149 return;
1150 }
1151
1152 for (i = 0; i < priv->search_path_len; i++)
1153 {
1154 path = g_build_filename (priv->search_path[i],
1155 theme_name,
1156 NULL);
1157 dir_mtime = g_slice_new (IconThemeDirMtime);
1158 dir_mtime->cache = NULL;
1159 dir_mtime->dir = path;
1160 if (g_stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode)) {
1161 dir_mtime->mtime = stat_buf.st_mtime;
1162 dir_mtime->exists = TRUE;
1163 } else {
1164 dir_mtime->mtime = 0;
1165 dir_mtime->exists = FALSE;
1166 }
1167
1168 priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1169 }
1170
1171 theme_file = NULL;
1172 for (i = 0; i < priv->search_path_len && !theme_file; i++)
1173 {
1174 path = g_build_filename (priv->search_path[i],
1175 theme_name,
1176 "index.theme",
1177 NULL);
1178 if (g_file_test (path, G_FILE_TEST_IS_REGULAR))
1179 {
1180 theme_file = g_key_file_new ();
1181 g_key_file_set_list_separator (theme_file, ',');
1182 if (!g_key_file_load_from_file (theme_file, path, 0, &error))
1183 {
1184 g_key_file_free (theme_file);
1185 theme_file = NULL;
1186 g_error_free (error);
1187 error = NULL;
1188 }
1189 }
1190 g_free (path);
1191 }
1192
1193 if (theme_file || strcmp (theme_name, FALLBACK_ICON_THEME) == 0)
1194 {
1195 theme = g_new0 (IconTheme, 1);
1196 theme->name = g_strdup (theme_name);
1197 priv->themes = g_list_prepend (priv->themes, theme);
1198 if (!theme_file)
1199 {
1200 theme_file = g_key_file_new ();
1201 g_key_file_set_list_separator (theme_file, ',');
1202 g_key_file_load_from_data (theme_file, builtin_hicolor_index, -1, 0, NULL);
1203 }
1204 }
1205
1206 if (theme_file == NULL)
1207 return;
1208
1209 theme->display_name =
1210 g_key_file_get_locale_string (theme_file, "Icon Theme", "Name", NULL, NULL);
1211 if (!theme->display_name)
1212 g_warning ("Theme file for %s has no name", theme_name);
1213
1214 dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "Directories", NULL, NULL);
1215 if (!dirs)
1216 {
1217 g_warning ("Theme file for %s has no directories", theme_name);
1218 priv->themes = g_list_remove (priv->themes, theme);
1219 g_free (theme->name);
1220 g_free (theme->display_name);
1221 g_free (theme);
1222 g_key_file_free (theme_file);
1223 return;
1224 }
1225
1226 scaled_dirs = g_key_file_get_string_list (theme_file, "Icon Theme", "ScaledDirectories", NULL, NULL);
1227
1228 theme->comment =
1229 g_key_file_get_locale_string (theme_file,
1230 "Icon Theme", "Comment",
1231 NULL, NULL);
1232 theme->example =
1233 g_key_file_get_string (theme_file,
1234 "Icon Theme", "Example",
1235 NULL);
1236
1237 theme->dirs = NULL;
1238 for (i = 0; dirs[i] != NULL; i++)
1239 theme_subdir_load (icon_theme, theme, theme_file, dirs[i]);
1240
1241 if (scaled_dirs)
1242 {
1243 for (i = 0; scaled_dirs[i] != NULL; i++)
1244 theme_subdir_load (icon_theme, theme, theme_file, scaled_dirs[i]);
1245 }
1246 g_strfreev (dirs);
1247 g_strfreev (scaled_dirs);
1248
1249 theme->dirs = g_list_reverse (theme->dirs);
1250
1251 themes = g_key_file_get_string_list (theme_file,
1252 "Icon Theme",
1253 "Inherits",
1254 NULL,
1255 NULL);
1256 if (themes)
1257 {
1258 for (i = 0; themes[i] != NULL; i++)
1259 insert_theme (icon_theme, themes[i]);
1260
1261 g_strfreev (themes);
1262 }
1263
1264 g_key_file_free (theme_file);
1265 }
1266
1267 static void
free_unthemed_icon(UnthemedIcon * unthemed_icon)1268 free_unthemed_icon (UnthemedIcon *unthemed_icon)
1269 {
1270 g_free (unthemed_icon->svg_filename);
1271 g_free (unthemed_icon->no_svg_filename);
1272 g_slice_free (UnthemedIcon, unthemed_icon);
1273 }
1274
1275 static gchar *
strip_suffix(const gchar * filename)1276 strip_suffix (const gchar *filename)
1277 {
1278 const gchar *dot;
1279
1280 if (g_str_has_suffix (filename, ".symbolic.png"))
1281 return g_strndup (filename, strlen(filename)-13);
1282
1283 dot = strrchr (filename, '.');
1284
1285 if (dot == NULL)
1286 return g_strdup (filename);
1287
1288 return g_strndup (filename, dot - filename);
1289 }
1290
1291 static void
add_unthemed_icon(GtkIconTheme * icon_theme,const gchar * dir,const gchar * file,gboolean is_resource)1292 add_unthemed_icon (GtkIconTheme *icon_theme,
1293 const gchar *dir,
1294 const gchar *file,
1295 gboolean is_resource)
1296 {
1297 GtkIconThemePrivate *priv = icon_theme->priv;
1298 IconSuffix new_suffix, old_suffix;
1299 gchar *abs_file;
1300 gchar *base_name;
1301 UnthemedIcon *unthemed_icon;
1302
1303 new_suffix = suffix_from_name (file);
1304
1305 if (new_suffix == ICON_SUFFIX_NONE)
1306 return;
1307
1308 abs_file = g_build_filename (dir, file, NULL);
1309 base_name = strip_suffix (file);
1310
1311 unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, base_name);
1312
1313 if (unthemed_icon)
1314 {
1315 if (new_suffix == ICON_SUFFIX_SVG)
1316 {
1317 if (unthemed_icon->svg_filename)
1318 g_free (abs_file);
1319 else
1320 unthemed_icon->svg_filename = abs_file;
1321 }
1322 else
1323 {
1324 if (unthemed_icon->no_svg_filename)
1325 {
1326 old_suffix = suffix_from_name (unthemed_icon->no_svg_filename);
1327 if (new_suffix > old_suffix)
1328 {
1329 g_free (unthemed_icon->no_svg_filename);
1330 unthemed_icon->no_svg_filename = abs_file;
1331 }
1332 else
1333 g_free (abs_file);
1334 }
1335 else
1336 unthemed_icon->no_svg_filename = abs_file;
1337 }
1338
1339 g_free (base_name);
1340 }
1341 else
1342 {
1343 unthemed_icon = g_slice_new0 (UnthemedIcon);
1344
1345 unthemed_icon->is_resource = is_resource;
1346
1347 if (new_suffix == ICON_SUFFIX_SVG)
1348 unthemed_icon->svg_filename = abs_file;
1349 else
1350 unthemed_icon->no_svg_filename = abs_file;
1351
1352 /* takes ownership of base_name */
1353 g_hash_table_replace (priv->unthemed_icons, base_name, unthemed_icon);
1354 }
1355 }
1356
1357 static void
load_themes(GtkIconTheme * icon_theme)1358 load_themes (GtkIconTheme *icon_theme)
1359 {
1360 GtkIconThemePrivate *priv;
1361 GDir *gdir;
1362 gint base;
1363 gchar *dir;
1364 const gchar *file;
1365 GTimeVal tv;
1366 IconThemeDirMtime *dir_mtime;
1367 GStatBuf stat_buf;
1368 GList *d;
1369
1370 priv = icon_theme->priv;
1371
1372 if (priv->current_theme)
1373 insert_theme (icon_theme, priv->current_theme);
1374
1375 /* Always look in the Adwaita, gnome and hicolor icon themes.
1376 * Looking in hicolor is mandated by the spec, looking in Adwaita
1377 * and gnome is a pragmatic solution to prevent missing icons in
1378 * GTK+ applications when run under, e.g. KDE.
1379 */
1380 insert_theme (icon_theme, DEFAULT_ICON_THEME);
1381 insert_theme (icon_theme, "gnome");
1382 insert_theme (icon_theme, FALLBACK_ICON_THEME);
1383 priv->themes = g_list_reverse (priv->themes);
1384
1385
1386 priv->unthemed_icons = g_hash_table_new_full (g_str_hash, g_str_equal,
1387 g_free, (GDestroyNotify)free_unthemed_icon);
1388
1389 for (base = 0; base < icon_theme->priv->search_path_len; base++)
1390 {
1391 dir = icon_theme->priv->search_path[base];
1392
1393 dir_mtime = g_slice_new (IconThemeDirMtime);
1394 priv->dir_mtimes = g_list_prepend (priv->dir_mtimes, dir_mtime);
1395
1396 dir_mtime->dir = g_strdup (dir);
1397 dir_mtime->mtime = 0;
1398 dir_mtime->exists = FALSE;
1399 dir_mtime->cache = NULL;
1400
1401 if (g_stat (dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode))
1402 continue;
1403 dir_mtime->mtime = stat_buf.st_mtime;
1404 dir_mtime->exists = TRUE;
1405
1406 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir);
1407 if (dir_mtime->cache != NULL)
1408 continue;
1409
1410 gdir = g_dir_open (dir, 0, NULL);
1411 if (gdir == NULL)
1412 continue;
1413
1414 while ((file = g_dir_read_name (gdir)))
1415 add_unthemed_icon (icon_theme, dir, file, FALSE);
1416
1417 g_dir_close (gdir);
1418 }
1419 priv->dir_mtimes = g_list_reverse (priv->dir_mtimes);
1420
1421 for (d = priv->resource_paths; d; d = d->next)
1422 {
1423 gchar **children;
1424 gint i;
1425
1426 dir = d->data;
1427 children = g_resources_enumerate_children (dir, 0, NULL);
1428 if (!children)
1429 continue;
1430
1431 for (i = 0; children[i]; i++)
1432 add_unthemed_icon (icon_theme, dir, children[i], TRUE);
1433
1434 g_strfreev (children);
1435 }
1436
1437 priv->themes_valid = TRUE;
1438
1439 g_get_current_time (&tv);
1440 priv->last_stat_time = tv.tv_sec;
1441
1442 GTK_NOTE (ICONTHEME, {
1443 GList *l;
1444 GString *s;
1445 s = g_string_new ("Current icon themes ");
1446 for (l = icon_theme->priv->themes; l; l = l->next)
1447 {
1448 IconTheme *theme = l->data;
1449 g_string_append (s, theme->name);
1450 g_string_append_c (s, ' ');
1451 }
1452 g_message ("%s", s->str);
1453 g_string_free (s, TRUE);
1454 });
1455 }
1456
1457 static void
ensure_valid_themes(GtkIconTheme * icon_theme)1458 ensure_valid_themes (GtkIconTheme *icon_theme)
1459 {
1460 GtkIconThemePrivate *priv = icon_theme->priv;
1461 GTimeVal tv;
1462 gboolean was_valid = priv->themes_valid;
1463
1464 if (priv->loading_themes)
1465 return;
1466 priv->loading_themes = TRUE;
1467
1468 if (priv->themes_valid)
1469 {
1470 g_get_current_time (&tv);
1471
1472 if (ABS (tv.tv_sec - priv->last_stat_time) > 5 &&
1473 rescan_themes (icon_theme))
1474 {
1475 g_hash_table_remove_all (priv->info_cache);
1476 blow_themes (icon_theme);
1477 }
1478 }
1479
1480 if (!priv->themes_valid)
1481 {
1482 load_themes (icon_theme);
1483
1484 if (was_valid)
1485 queue_theme_changed (icon_theme);
1486 }
1487
1488 priv->loading_themes = FALSE;
1489 }
1490
1491 /* The LRU cache is a short list of IconInfos that are kept
1492 * alive even though their IconInfo would otherwise have
1493 * been freed, so that we can avoid reloading these
1494 * constantly.
1495 * We put infos on the lru list when nothing otherwise
1496 * references the info. So, when we get a cache hit
1497 * we remove it from the list, and when the proxy
1498 * pixmap is released we put it on the list.
1499 */
1500 static void
ensure_lru_cache_space(GtkIconTheme * icon_theme)1501 ensure_lru_cache_space (GtkIconTheme *icon_theme)
1502 {
1503 GtkIconThemePrivate *priv = icon_theme->priv;
1504 GList *l;
1505
1506 /* Remove last item if LRU full */
1507 l = g_list_nth (priv->info_cache_lru, INFO_CACHE_LRU_SIZE - 1);
1508 if (l)
1509 {
1510 GtkIconInfo *icon_info = l->data;
1511
1512 DEBUG_CACHE (("removing (due to out of space) %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1513 icon_info,
1514 g_strjoinv (",", icon_info->key.icon_names),
1515 icon_info->key.size, icon_info->key.flags,
1516 g_list_length (priv->info_cache_lru)));
1517
1518 priv->info_cache_lru = g_list_delete_link (priv->info_cache_lru, l);
1519 g_object_unref (icon_info);
1520 }
1521 }
1522
1523 static void
add_to_lru_cache(GtkIconTheme * icon_theme,GtkIconInfo * icon_info)1524 add_to_lru_cache (GtkIconTheme *icon_theme,
1525 GtkIconInfo *icon_info)
1526 {
1527 GtkIconThemePrivate *priv = icon_theme->priv;
1528
1529 DEBUG_CACHE (("adding %p (%s %d 0x%x) to LRU cache (cache size %d)\n",
1530 icon_info,
1531 g_strjoinv (",", icon_info->key.icon_names),
1532 icon_info->key.size, icon_info->key.flags,
1533 g_list_length (priv->info_cache_lru)));
1534
1535 g_assert (g_list_find (priv->info_cache_lru, icon_info) == NULL);
1536
1537 ensure_lru_cache_space (icon_theme);
1538 /* prepend new info to LRU */
1539 priv->info_cache_lru = g_list_prepend (priv->info_cache_lru,
1540 g_object_ref (icon_info));
1541 }
1542
1543 static void
ensure_in_lru_cache(GtkIconTheme * icon_theme,GtkIconInfo * icon_info)1544 ensure_in_lru_cache (GtkIconTheme *icon_theme,
1545 GtkIconInfo *icon_info)
1546 {
1547 GtkIconThemePrivate *priv = icon_theme->priv;
1548 GList *l;
1549
1550 l = g_list_find (priv->info_cache_lru, icon_info);
1551 if (l)
1552 {
1553 /* Move to front of LRU if already in it */
1554 priv->info_cache_lru = g_list_remove_link (priv->info_cache_lru, l);
1555 priv->info_cache_lru = g_list_concat (l, priv->info_cache_lru);
1556 }
1557 else
1558 add_to_lru_cache (icon_theme, icon_info);
1559 }
1560
1561 static void
remove_from_lru_cache(GtkIconTheme * icon_theme,GtkIconInfo * icon_info)1562 remove_from_lru_cache (GtkIconTheme *icon_theme,
1563 GtkIconInfo *icon_info)
1564 {
1565 GtkIconThemePrivate *priv = icon_theme->priv;
1566 if (g_list_find (priv->info_cache_lru, icon_info))
1567 {
1568 DEBUG_CACHE (("removing %p (%s %d 0x%x) from LRU cache (cache size %d)\n",
1569 icon_info,
1570 g_strjoinv (",", icon_info->key.icon_names),
1571 icon_info->key.size, icon_info->key.flags,
1572 g_list_length (priv->info_cache_lru)));
1573
1574 priv->info_cache_lru = g_list_remove (priv->info_cache_lru, icon_info);
1575 g_object_unref (icon_info);
1576 }
1577 }
1578
1579 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)1580 symbolic_pixbuf_cache_new (GdkPixbuf *pixbuf,
1581 const GdkRGBA *fg,
1582 const GdkRGBA *success_color,
1583 const GdkRGBA *warning_color,
1584 const GdkRGBA *error_color,
1585 SymbolicPixbufCache *next)
1586 {
1587 SymbolicPixbufCache *cache;
1588
1589 cache = g_new0 (SymbolicPixbufCache, 1);
1590 cache->pixbuf = g_object_ref (pixbuf);
1591 if (fg)
1592 cache->fg = *fg;
1593 if (success_color)
1594 cache->success_color = *success_color;
1595 if (warning_color)
1596 cache->warning_color = *warning_color;
1597 if (error_color)
1598 cache->error_color = *error_color;
1599 cache->next = next;
1600 return cache;
1601 }
1602
1603 static gboolean
rgba_matches(const GdkRGBA * a,const GdkRGBA * b)1604 rgba_matches (const GdkRGBA *a,
1605 const GdkRGBA *b)
1606 {
1607 GdkRGBA transparent = { 0 };
1608
1609 /* For matching we treat unset colors as transparent rather
1610 than default, which works as well, because transparent
1611 will never be used for real symbolic icon colors */
1612 if (a == NULL)
1613 a = &transparent;
1614
1615 return
1616 fabs(a->red - b->red) < 0.0001 &&
1617 fabs(a->green - b->green) < 0.0001 &&
1618 fabs(a->blue - b->blue) < 0.0001 &&
1619 fabs(a->alpha - b->alpha) < 0.0001;
1620 }
1621
1622 static SymbolicPixbufCache *
symbolic_pixbuf_cache_matches(SymbolicPixbufCache * cache,const GdkRGBA * fg,const GdkRGBA * success_color,const GdkRGBA * warning_color,const GdkRGBA * error_color)1623 symbolic_pixbuf_cache_matches (SymbolicPixbufCache *cache,
1624 const GdkRGBA *fg,
1625 const GdkRGBA *success_color,
1626 const GdkRGBA *warning_color,
1627 const GdkRGBA *error_color)
1628 {
1629 while (cache != NULL)
1630 {
1631 if (rgba_matches (fg, &cache->fg) &&
1632 rgba_matches (success_color, &cache->success_color) &&
1633 rgba_matches (warning_color, &cache->warning_color) &&
1634 rgba_matches (error_color, &cache->error_color))
1635 return cache;
1636
1637 cache = cache->next;
1638 }
1639
1640 return NULL;
1641 }
1642
1643 static void
symbolic_pixbuf_cache_free(SymbolicPixbufCache * cache)1644 symbolic_pixbuf_cache_free (SymbolicPixbufCache *cache)
1645 {
1646 SymbolicPixbufCache *next;
1647
1648 while (cache != NULL)
1649 {
1650 next = cache->next;
1651 g_object_unref (cache->pixbuf);
1652 g_free (cache);
1653
1654 cache = next;
1655 }
1656 }
1657
1658 static gboolean
icon_name_is_symbolic(const gchar * icon_name)1659 icon_name_is_symbolic (const gchar *icon_name)
1660 {
1661 return g_str_has_suffix (icon_name, "-symbolic")
1662 || g_str_has_suffix (icon_name, "-symbolic-ltr")
1663 || g_str_has_suffix (icon_name, "-symbolic-rtl");
1664 }
1665
1666 static gboolean
icon_uri_is_symbolic(const gchar * icon_name)1667 icon_uri_is_symbolic (const gchar *icon_name)
1668 {
1669 return g_str_has_suffix (icon_name, "-symbolic.svg")
1670 || g_str_has_suffix (icon_name, "-symbolic-ltr.svg")
1671 || g_str_has_suffix (icon_name, "-symbolic-rtl.svg")
1672 || g_str_has_suffix (icon_name, ".symbolic.png");
1673 }
1674
1675 static GtkIconInfo *
real_choose_icon(GtkIconTheme * icon_theme,const gchar * icon_names[],gint size,gint scale,GtkIconLookupFlags flags)1676 real_choose_icon (GtkIconTheme *icon_theme,
1677 const gchar *icon_names[],
1678 gint size,
1679 gint scale,
1680 GtkIconLookupFlags flags)
1681 {
1682 GtkIconThemePrivate *priv;
1683 GList *l;
1684 GtkIconInfo *icon_info = NULL;
1685 GtkIconInfo *unscaled_icon_info;
1686 UnthemedIcon *unthemed_icon = NULL;
1687 const gchar *icon_name = NULL;
1688 gboolean allow_svg;
1689 gboolean use_builtin;
1690 IconTheme *theme = NULL;
1691 gint i;
1692 IconInfoKey key;
1693
1694 priv = icon_theme->priv;
1695
1696 ensure_valid_themes (icon_theme);
1697
1698 key.icon_names = (gchar **)icon_names;
1699 key.size = size;
1700 key.scale = scale;
1701 key.flags = flags;
1702
1703 icon_info = g_hash_table_lookup (priv->info_cache, &key);
1704 if (icon_info != NULL)
1705 {
1706 DEBUG_CACHE (("cache hit %p (%s %d 0x%x) (cache size %d)\n",
1707 icon_info,
1708 g_strjoinv (",", icon_info->key.icon_names),
1709 icon_info->key.size, icon_info->key.flags,
1710 g_hash_table_size (priv->info_cache)));
1711
1712 icon_info = g_object_ref (icon_info);
1713 remove_from_lru_cache (icon_theme, icon_info);
1714
1715 return icon_info;
1716 }
1717
1718 if (flags & GTK_ICON_LOOKUP_NO_SVG)
1719 allow_svg = FALSE;
1720 else if (flags & GTK_ICON_LOOKUP_FORCE_SVG)
1721 allow_svg = TRUE;
1722 else
1723 allow_svg = priv->pixbuf_supports_svg;
1724
1725 use_builtin = flags & GTK_ICON_LOOKUP_USE_BUILTIN;
1726
1727 /* This is used in the icontheme unit test */
1728 GTK_NOTE (ICONTHEME,
1729 for (i = 0; icon_names[i]; i++)
1730 g_message ("\tlookup name: %s", icon_names[i]));
1731
1732 /* For symbolic icons, do a search in all registered themes first;
1733 * a theme that inherits them from a parent theme might provide
1734 * an alternative full-color version, but still expect the symbolic icon
1735 * to show up instead.
1736 *
1737 * In other words: We prefer symbolic icons in inherited themes over
1738 * generic icons in the theme.
1739 */
1740 for (l = priv->themes; l; l = l->next)
1741 {
1742 theme = l->data;
1743 for (i = 0; icon_names[i] && icon_name_is_symbolic (icon_names[i]); i++)
1744 {
1745 icon_name = icon_names[i];
1746 icon_info = theme_lookup_icon (theme, icon_name, size, scale, allow_svg, use_builtin);
1747 if (icon_info)
1748 goto out;
1749 }
1750 }
1751
1752 for (l = priv->themes; l; l = l->next)
1753 {
1754 theme = l->data;
1755
1756 for (i = 0; icon_names[i]; i++)
1757 {
1758 icon_name = icon_names[i];
1759 icon_info = theme_lookup_icon (theme, icon_name, size, scale, allow_svg, use_builtin);
1760 if (icon_info)
1761 goto out;
1762 }
1763 }
1764
1765 theme = NULL;
1766
1767 for (i = 0; icon_names[i]; i++)
1768 {
1769 unthemed_icon = g_hash_table_lookup (priv->unthemed_icons, icon_names[i]);
1770 if (unthemed_icon)
1771 break;
1772 }
1773 #ifdef G_OS_WIN32
1774 /* Still not found an icon, check if reference to a Win32 resource */
1775 if (!unthemed_icon)
1776 {
1777 gchar **resources;
1778 HICON hIcon = NULL;
1779
1780 resources = g_strsplit (icon_names[0], ",", 0);
1781 if (resources[0])
1782 {
1783 wchar_t *wfile = g_utf8_to_utf16 (resources[0], -1, NULL, NULL, NULL);
1784 ExtractIconExW (wfile, resources[1] ? atoi (resources[1]) : 0, &hIcon, NULL, 1);
1785 g_free (wfile);
1786 }
1787
1788 if (hIcon)
1789 {
1790 icon_info = icon_info_new (ICON_THEME_DIR_UNTHEMED, size, 1);
1791 icon_info->cache_pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hIcon, NULL, NULL);
1792 DestroyIcon (hIcon);
1793 }
1794 g_strfreev (resources);
1795 }
1796 #endif
1797
1798 if (unthemed_icon)
1799 {
1800 icon_info = icon_info_new (ICON_THEME_DIR_UNTHEMED, size, 1);
1801
1802 /* A SVG icon, when allowed, beats out a XPM icon, but not a PNG icon */
1803 if (allow_svg &&
1804 unthemed_icon->svg_filename &&
1805 (!unthemed_icon->no_svg_filename ||
1806 suffix_from_name (unthemed_icon->no_svg_filename) < ICON_SUFFIX_PNG))
1807 icon_info->filename = g_strdup (unthemed_icon->svg_filename);
1808 else if (unthemed_icon->no_svg_filename)
1809 icon_info->filename = g_strdup (unthemed_icon->no_svg_filename);
1810 else
1811 {
1812 static gboolean warned_once = FALSE;
1813
1814 if (!warned_once)
1815 {
1816 g_warning ("Found an icon but could not load it. "
1817 "Most likely gdk-pixbuf does not provide SVG support.");
1818 warned_once = TRUE;
1819 }
1820
1821 g_clear_object (&icon_info);
1822 goto out;
1823 }
1824
1825 if (unthemed_icon->is_resource)
1826 {
1827 gchar *uri;
1828 uri = g_strconcat ("resource://", icon_info->filename, NULL);
1829 icon_info->icon_file = g_file_new_for_uri (uri);
1830 g_free (uri);
1831 }
1832 else
1833 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
1834
1835 icon_info->is_svg = suffix_from_name (icon_info->filename) == ICON_SUFFIX_SVG;
1836 icon_info->is_resource = unthemed_icon->is_resource;
1837 }
1838
1839 out:
1840 if (icon_info)
1841 {
1842 icon_info->desired_size = size;
1843 icon_info->desired_scale = scale;
1844 icon_info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
1845
1846 /* In case we're not scaling the icon we want to reuse the exact same
1847 * size as a scale==1 lookup would be, rather than not scaling at all
1848 * and causing a different layout
1849 */
1850 icon_info->unscaled_scale = 1.0;
1851 if (scale != 1 && !icon_info->forced_size && theme != NULL)
1852 {
1853 unscaled_icon_info = theme_lookup_icon (theme, icon_name, size, 1, allow_svg, use_builtin);
1854 if (unscaled_icon_info)
1855 {
1856 icon_info->unscaled_scale =
1857 (gdouble) unscaled_icon_info->dir_size * scale / (icon_info->dir_size * icon_info->dir_scale);
1858 g_object_unref (unscaled_icon_info);
1859 }
1860 }
1861
1862 icon_info->key.icon_names = g_strdupv ((char **)icon_names);
1863 icon_info->key.size = size;
1864 icon_info->key.scale = scale;
1865 icon_info->key.flags = flags;
1866 icon_info->in_cache = icon_theme;
1867 DEBUG_CACHE (("adding %p (%s %d 0x%x) to cache (cache size %d)\n",
1868 icon_info,
1869 g_strjoinv (",", icon_info->key.icon_names),
1870 icon_info->key.size, icon_info->key.flags,
1871 g_hash_table_size (priv->info_cache)));
1872 g_hash_table_insert (priv->info_cache, &icon_info->key, icon_info);
1873 }
1874 else
1875 {
1876 static gboolean check_for_default_theme = TRUE;
1877 gchar *default_theme_path;
1878 gboolean found = FALSE;
1879
1880 if (check_for_default_theme)
1881 {
1882 check_for_default_theme = FALSE;
1883
1884 for (i = 0; !found && i < priv->search_path_len; i++)
1885 {
1886 default_theme_path = g_build_filename (priv->search_path[i],
1887 FALLBACK_ICON_THEME,
1888 "index.theme",
1889 NULL);
1890 found = g_file_test (default_theme_path, G_FILE_TEST_IS_REGULAR);
1891 g_free (default_theme_path);
1892 }
1893
1894 if (!found)
1895 {
1896 g_warning ("Could not find the icon '%s'. The '%s' theme\n"
1897 "was not found either, perhaps you need to install it.\n"
1898 "You can get a copy from:\n"
1899 "\t%s",
1900 icon_names[0], FALLBACK_ICON_THEME, "http://icon-theme.freedesktop.org/releases");
1901 }
1902 }
1903 }
1904
1905 return icon_info;
1906 }
1907
1908 static void
icon_name_list_add_icon(GPtrArray * icons,const gchar * dir_suffix,gchar * icon_name)1909 icon_name_list_add_icon (GPtrArray *icons,
1910 const gchar *dir_suffix,
1911 gchar *icon_name)
1912 {
1913 if (dir_suffix)
1914 g_ptr_array_add (icons, g_strconcat (icon_name, dir_suffix, NULL));
1915 g_ptr_array_add (icons, icon_name);
1916 }
1917
1918 static GtkIconInfo *
choose_icon(GtkIconTheme * icon_theme,const gchar * icon_names[],gint size,gint scale,GtkIconLookupFlags flags)1919 choose_icon (GtkIconTheme *icon_theme,
1920 const gchar *icon_names[],
1921 gint size,
1922 gint scale,
1923 GtkIconLookupFlags flags)
1924 {
1925 gboolean has_regular = FALSE, has_symbolic = FALSE;
1926 GtkIconInfo *icon_info;
1927 GPtrArray *new_names;
1928 const gchar *dir_suffix;
1929 guint i;
1930
1931 if (flags & GTK_ICON_LOOKUP_DIR_LTR)
1932 dir_suffix = "-ltr";
1933 else if (flags & GTK_ICON_LOOKUP_DIR_RTL)
1934 dir_suffix = "-rtl";
1935 else
1936 dir_suffix = NULL;
1937
1938 for (i = 0; icon_names[i]; i++)
1939 {
1940 if (icon_name_is_symbolic (icon_names[i]))
1941 has_symbolic = TRUE;
1942 else
1943 has_regular = TRUE;
1944 }
1945
1946 if ((flags & GTK_ICON_LOOKUP_FORCE_REGULAR) && has_symbolic)
1947 {
1948 new_names = g_ptr_array_new_with_free_func (g_free);
1949 for (i = 0; icon_names[i]; i++)
1950 {
1951 if (icon_name_is_symbolic (icon_names[i]))
1952 icon_name_list_add_icon (new_names, dir_suffix, g_strndup (icon_names[i], strlen (icon_names[i]) - strlen ("-symbolic")));
1953 else
1954 icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
1955 }
1956 for (i = 0; icon_names[i]; i++)
1957 {
1958 if (icon_name_is_symbolic (icon_names[i]))
1959 icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
1960 }
1961 g_ptr_array_add (new_names, NULL);
1962
1963 icon_info = real_choose_icon (icon_theme,
1964 (const gchar **) new_names->pdata,
1965 size,
1966 scale,
1967 flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC));
1968
1969 g_ptr_array_free (new_names, TRUE);
1970 }
1971 else if ((flags & GTK_ICON_LOOKUP_FORCE_SYMBOLIC) && has_regular)
1972 {
1973 new_names = g_ptr_array_new_with_free_func (g_free);
1974 for (i = 0; icon_names[i]; i++)
1975 {
1976 if (!icon_name_is_symbolic (icon_names[i]))
1977 icon_name_list_add_icon (new_names, dir_suffix, g_strconcat (icon_names[i], "-symbolic", NULL));
1978 else
1979 icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
1980 }
1981 for (i = 0; icon_names[i]; i++)
1982 {
1983 if (!icon_name_is_symbolic (icon_names[i]))
1984 icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
1985 }
1986 g_ptr_array_add (new_names, NULL);
1987
1988 icon_info = real_choose_icon (icon_theme,
1989 (const gchar **) new_names->pdata,
1990 size,
1991 scale,
1992 flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC));
1993
1994 g_ptr_array_free (new_names, TRUE);
1995 }
1996 else if (dir_suffix)
1997 {
1998 new_names = g_ptr_array_new_with_free_func (g_free);
1999 for (i = 0; icon_names[i]; i++)
2000 {
2001 icon_name_list_add_icon (new_names, dir_suffix, g_strdup (icon_names[i]));
2002 }
2003 g_ptr_array_add (new_names, NULL);
2004
2005 icon_info = real_choose_icon (icon_theme,
2006 (const gchar **) new_names->pdata,
2007 size,
2008 scale,
2009 flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC));
2010
2011 g_ptr_array_free (new_names, TRUE);
2012 }
2013 else
2014 {
2015 icon_info = real_choose_icon (icon_theme,
2016 icon_names,
2017 size,
2018 scale,
2019 flags & ~(GTK_ICON_LOOKUP_FORCE_REGULAR | GTK_ICON_LOOKUP_FORCE_SYMBOLIC));
2020 }
2021
2022 return icon_info;
2023 }
2024
2025 /**
2026 * gtk_icon_theme_lookup_icon:
2027 * @icon_theme: a #GtkIconTheme
2028 * @icon_name: the name of the icon to lookup
2029 * @size: desired icon size
2030 * @flags: flags modifying the behavior of the icon lookup
2031 *
2032 * Looks up a named icon and returns a #GtkIconInfo containing
2033 * information such as the filename of the icon. The icon
2034 * can then be rendered into a pixbuf using
2035 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
2036 * combines these two steps if all you need is the pixbuf.)
2037 *
2038 * When rendering on displays with high pixel densities you should not
2039 * use a @size multiplied by the scaling factor returned by functions
2040 * like gdk_window_get_scale_factor(). Instead, you should use
2041 * gtk_icon_theme_lookup_icon_for_scale(), as the assets loaded
2042 * for a given scaling factor may be different.
2043 *
2044 * Returns: (nullable) (transfer full): a #GtkIconInfo object
2045 * containing information about the icon, or %NULL if the
2046 * icon wasn’t found.
2047 *
2048 * Since: 2.4
2049 */
2050 GtkIconInfo *
gtk_icon_theme_lookup_icon(GtkIconTheme * icon_theme,const gchar * icon_name,gint size,GtkIconLookupFlags flags)2051 gtk_icon_theme_lookup_icon (GtkIconTheme *icon_theme,
2052 const gchar *icon_name,
2053 gint size,
2054 GtkIconLookupFlags flags)
2055 {
2056 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2057 g_return_val_if_fail (icon_name != NULL, NULL);
2058 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
2059 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
2060
2061 GTK_NOTE (ICONTHEME, g_message ("looking up icon %s", icon_name));
2062
2063 return gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name,
2064 size, 1, flags);
2065 }
2066
2067 /**
2068 * gtk_icon_theme_lookup_icon_for_scale:
2069 * @icon_theme: a #GtkIconTheme
2070 * @icon_name: the name of the icon to lookup
2071 * @size: desired icon size
2072 * @scale: the desired scale
2073 * @flags: flags modifying the behavior of the icon lookup
2074 *
2075 * Looks up a named icon for a particular window scale and returns a
2076 * #GtkIconInfo containing information such as the filename of the
2077 * icon. The icon can then be rendered into a pixbuf using
2078 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon() combines
2079 * these two steps if all you need is the pixbuf.)
2080 *
2081 * Returns: (nullable) (transfer full): a #GtkIconInfo object
2082 * containing information about the icon, or %NULL if the
2083 * icon wasn’t found.
2084 *
2085 * Since: 3.10
2086 */
2087 GtkIconInfo *
gtk_icon_theme_lookup_icon_for_scale(GtkIconTheme * icon_theme,const gchar * icon_name,gint size,gint scale,GtkIconLookupFlags flags)2088 gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *icon_theme,
2089 const gchar *icon_name,
2090 gint size,
2091 gint scale,
2092 GtkIconLookupFlags flags)
2093 {
2094 GtkIconInfo *info;
2095
2096 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2097 g_return_val_if_fail (icon_name != NULL, NULL);
2098 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
2099 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
2100 g_return_val_if_fail (scale >= 1, NULL);
2101
2102 GTK_NOTE (ICONTHEME, g_message ("looking up icon %s for scale %d", icon_name, scale));
2103
2104 if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
2105 {
2106 gchar **names, **nonsymbolic_names;
2107 gint dashes, i;
2108 gchar *p, *nonsymbolic_icon_name;
2109 gboolean is_symbolic;
2110
2111 is_symbolic = icon_name_is_symbolic (icon_name);
2112 if (is_symbolic)
2113 nonsymbolic_icon_name = g_strndup (icon_name, strlen (icon_name) - strlen ("-symbolic"));
2114 else
2115 nonsymbolic_icon_name = g_strdup (icon_name);
2116
2117 dashes = 0;
2118 for (p = (gchar *) nonsymbolic_icon_name; *p; p++)
2119 if (*p == '-')
2120 dashes++;
2121
2122 nonsymbolic_names = g_new (gchar *, dashes + 2);
2123 nonsymbolic_names[0] = nonsymbolic_icon_name;
2124
2125 for (i = 1; i <= dashes; i++)
2126 {
2127 nonsymbolic_names[i] = g_strdup (nonsymbolic_names[i - 1]);
2128 p = strrchr (nonsymbolic_names[i], '-');
2129 *p = '\0';
2130 }
2131 nonsymbolic_names[dashes + 1] = NULL;
2132
2133 if (is_symbolic)
2134 {
2135 names = g_new (gchar *, 2 * dashes + 3);
2136 for (i = 0; nonsymbolic_names[i] != NULL; i++)
2137 {
2138 names[i] = g_strconcat (nonsymbolic_names[i], "-symbolic", NULL);
2139 names[dashes + 1 + i] = nonsymbolic_names[i];
2140 }
2141
2142 names[dashes + 1 + i] = NULL;
2143 g_free (nonsymbolic_names);
2144 }
2145 else
2146 {
2147 names = nonsymbolic_names;
2148 }
2149
2150 info = choose_icon (icon_theme, (const gchar **) names, size, scale, flags);
2151
2152 g_strfreev (names);
2153 }
2154 else
2155 {
2156 const gchar *names[2];
2157
2158 names[0] = icon_name;
2159 names[1] = NULL;
2160
2161 info = choose_icon (icon_theme, names, size, scale, flags);
2162 }
2163
2164 return info;
2165 }
2166
2167 /**
2168 * gtk_icon_theme_choose_icon:
2169 * @icon_theme: a #GtkIconTheme
2170 * @icon_names: (array zero-terminated=1): %NULL-terminated array of
2171 * icon names to lookup
2172 * @size: desired icon size
2173 * @flags: flags modifying the behavior of the icon lookup
2174 *
2175 * Looks up a named icon and returns a #GtkIconInfo containing
2176 * information such as the filename of the icon. The icon
2177 * can then be rendered into a pixbuf using
2178 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
2179 * combines these two steps if all you need is the pixbuf.)
2180 *
2181 * If @icon_names contains more than one name, this function
2182 * tries them all in the given order before falling back to
2183 * inherited icon themes.
2184 *
2185 * Returns: (nullable) (transfer full): a #GtkIconInfo object
2186 * containing information about the icon, or %NULL if the icon wasn’t
2187 * found.
2188 *
2189 * Since: 2.12
2190 */
2191 GtkIconInfo *
gtk_icon_theme_choose_icon(GtkIconTheme * icon_theme,const gchar * icon_names[],gint size,GtkIconLookupFlags flags)2192 gtk_icon_theme_choose_icon (GtkIconTheme *icon_theme,
2193 const gchar *icon_names[],
2194 gint size,
2195 GtkIconLookupFlags flags)
2196 {
2197 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2198 g_return_val_if_fail (icon_names != NULL, NULL);
2199 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
2200 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
2201 g_warn_if_fail ((flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK) == 0);
2202
2203 return choose_icon (icon_theme, icon_names, size, 1, flags);
2204 }
2205
2206 /**
2207 * gtk_icon_theme_choose_icon_for_scale:
2208 * @icon_theme: a #GtkIconTheme
2209 * @icon_names: (array zero-terminated=1): %NULL-terminated
2210 * array of icon names to lookup
2211 * @size: desired icon size
2212 * @scale: desired scale
2213 * @flags: flags modifying the behavior of the icon lookup
2214 *
2215 * Looks up a named icon for a particular window scale and returns
2216 * a #GtkIconInfo containing information such as the filename of the
2217 * icon. The icon can then be rendered into a pixbuf using
2218 * gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon()
2219 * combines these two steps if all you need is the pixbuf.)
2220 *
2221 * If @icon_names contains more than one name, this function
2222 * tries them all in the given order before falling back to
2223 * inherited icon themes.
2224 *
2225 * Returns: (nullable) (transfer full): a #GtkIconInfo object
2226 * containing information about the icon, or %NULL if the
2227 * icon wasn’t found.
2228 *
2229 * Since: 3.10
2230 */
2231 GtkIconInfo *
gtk_icon_theme_choose_icon_for_scale(GtkIconTheme * icon_theme,const gchar * icon_names[],gint size,gint scale,GtkIconLookupFlags flags)2232 gtk_icon_theme_choose_icon_for_scale (GtkIconTheme *icon_theme,
2233 const gchar *icon_names[],
2234 gint size,
2235 gint scale,
2236 GtkIconLookupFlags flags)
2237 {
2238 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2239 g_return_val_if_fail (icon_names != NULL, NULL);
2240 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
2241 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
2242 g_return_val_if_fail (scale >= 1, NULL);
2243 g_warn_if_fail ((flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK) == 0);
2244
2245 return choose_icon (icon_theme, icon_names, size, scale, flags);
2246 }
2247
2248
2249 /* Error quark */
2250 GQuark
gtk_icon_theme_error_quark(void)2251 gtk_icon_theme_error_quark (void)
2252 {
2253 return g_quark_from_static_string ("gtk-icon-theme-error-quark");
2254 }
2255
2256 /**
2257 * gtk_icon_theme_load_icon:
2258 * @icon_theme: a #GtkIconTheme
2259 * @icon_name: the name of the icon to lookup
2260 * @size: the desired icon size. The resulting icon may not be
2261 * exactly this size; see gtk_icon_info_load_icon().
2262 * @flags: flags modifying the behavior of the icon lookup
2263 * @error: (allow-none): Location to store error information on failure,
2264 * or %NULL.
2265 *
2266 * Looks up an icon in an icon theme, scales it to the given size
2267 * and renders it into a pixbuf. This is a convenience function;
2268 * if more details about the icon are needed, use
2269 * gtk_icon_theme_lookup_icon() followed by gtk_icon_info_load_icon().
2270 *
2271 * Note that you probably want to listen for icon theme changes and
2272 * update the icon. This is usually done by connecting to the
2273 * GtkWidget::style-set signal. If for some reason you do not want to
2274 * update the icon when the icon theme changes, you should consider
2275 * using gdk_pixbuf_copy() to make a private copy of the pixbuf
2276 * returned by this function. Otherwise GTK+ may need to keep the old
2277 * icon theme loaded, which would be a waste of memory.
2278 *
2279 * Returns: (nullable) (transfer full): the rendered icon; this may be
2280 * a newly created icon or a new reference to an internal icon, so
2281 * you must not modify the icon. Use g_object_unref() to release
2282 * your reference to the icon. %NULL if the icon isn’t found.
2283 *
2284 * Since: 2.4
2285 */
2286 GdkPixbuf *
gtk_icon_theme_load_icon(GtkIconTheme * icon_theme,const gchar * icon_name,gint size,GtkIconLookupFlags flags,GError ** error)2287 gtk_icon_theme_load_icon (GtkIconTheme *icon_theme,
2288 const gchar *icon_name,
2289 gint size,
2290 GtkIconLookupFlags flags,
2291 GError **error)
2292 {
2293 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2294 g_return_val_if_fail (icon_name != NULL, NULL);
2295 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
2296 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
2297 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2298
2299 return gtk_icon_theme_load_icon_for_scale (icon_theme, icon_name,
2300 size, 1, flags, error);
2301 }
2302
2303 /**
2304 * gtk_icon_theme_load_icon_for_scale:
2305 * @icon_theme: a #GtkIconTheme
2306 * @icon_name: the name of the icon to lookup
2307 * @size: the desired icon size. The resulting icon may not be
2308 * exactly this size; see gtk_icon_info_load_icon().
2309 * @scale: desired scale
2310 * @flags: flags modifying the behavior of the icon lookup
2311 * @error: (allow-none): Location to store error information on failure,
2312 * or %NULL.
2313 *
2314 * Looks up an icon in an icon theme for a particular window scale,
2315 * scales it to the given size and renders it into a pixbuf. This is a
2316 * convenience function; if more details about the icon are needed,
2317 * use gtk_icon_theme_lookup_icon() followed by
2318 * gtk_icon_info_load_icon().
2319 *
2320 * Note that you probably want to listen for icon theme changes and
2321 * update the icon. This is usually done by connecting to the
2322 * GtkWidget::style-set signal. If for some reason you do not want to
2323 * update the icon when the icon theme changes, you should consider
2324 * using gdk_pixbuf_copy() to make a private copy of the pixbuf
2325 * returned by this function. Otherwise GTK+ may need to keep the old
2326 * icon theme loaded, which would be a waste of memory.
2327 *
2328 * Returns: (nullable) (transfer full): the rendered icon; this may be
2329 * a newly created icon or a new reference to an internal icon, so
2330 * you must not modify the icon. Use g_object_unref() to release
2331 * your reference to the icon. %NULL if the icon isn’t found.
2332 *
2333 * Since: 3.10
2334 */
2335 GdkPixbuf *
gtk_icon_theme_load_icon_for_scale(GtkIconTheme * icon_theme,const gchar * icon_name,gint size,gint scale,GtkIconLookupFlags flags,GError ** error)2336 gtk_icon_theme_load_icon_for_scale (GtkIconTheme *icon_theme,
2337 const gchar *icon_name,
2338 gint size,
2339 gint scale,
2340 GtkIconLookupFlags flags,
2341 GError **error)
2342 {
2343 GtkIconInfo *icon_info;
2344 GdkPixbuf *pixbuf = NULL;
2345
2346 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2347 g_return_val_if_fail (icon_name != NULL, NULL);
2348 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
2349 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
2350 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2351 g_return_val_if_fail (scale >= 1, NULL);
2352
2353 icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name, size, scale,
2354 flags | GTK_ICON_LOOKUP_USE_BUILTIN);
2355 if (!icon_info)
2356 {
2357 g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
2358 _("Icon '%s' not present in theme %s"), icon_name, icon_theme->priv->current_theme);
2359 return NULL;
2360 }
2361
2362 pixbuf = gtk_icon_info_load_icon (icon_info, error);
2363 g_prefix_error (error, "Failed to load %s: ", icon_info->filename);
2364 g_object_unref (icon_info);
2365
2366 return pixbuf;
2367 }
2368
2369 /**
2370 * gtk_icon_theme_load_surface:
2371 * @icon_theme: a #GtkIconTheme
2372 * @icon_name: the name of the icon to lookup
2373 * @size: the desired icon size. The resulting icon may not be
2374 * exactly this size; see gtk_icon_info_load_icon().
2375 * @scale: desired scale
2376 * @for_window: (allow-none): #GdkWindow to optimize drawing for, or %NULL
2377 * @flags: flags modifying the behavior of the icon lookup
2378 * @error: (allow-none): Location to store error information on failure,
2379 * or %NULL.
2380 *
2381 * Looks up an icon in an icon theme for a particular window scale,
2382 * scales it to the given size and renders it into a cairo surface. This is a
2383 * convenience function; if more details about the icon are needed,
2384 * use gtk_icon_theme_lookup_icon() followed by
2385 * gtk_icon_info_load_surface().
2386 *
2387 * Note that you probably want to listen for icon theme changes and
2388 * update the icon. This is usually done by connecting to the
2389 * GtkWidget::style-set signal.
2390 *
2391 * Returns: (nullable) (transfer full): the rendered icon; this may be
2392 * a newly created icon or a new reference to an internal icon, so
2393 * you must not modify the icon. Use cairo_surface_destroy() to
2394 * release your reference to the icon. %NULL if the icon isn’t
2395 * found.
2396 *
2397 * Since: 3.10
2398 */
2399 cairo_surface_t *
gtk_icon_theme_load_surface(GtkIconTheme * icon_theme,const gchar * icon_name,gint size,gint scale,GdkWindow * for_window,GtkIconLookupFlags flags,GError ** error)2400 gtk_icon_theme_load_surface (GtkIconTheme *icon_theme,
2401 const gchar *icon_name,
2402 gint size,
2403 gint scale,
2404 GdkWindow *for_window,
2405 GtkIconLookupFlags flags,
2406 GError **error)
2407 {
2408 GtkIconInfo *icon_info;
2409 cairo_surface_t *surface = NULL;
2410
2411 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2412 g_return_val_if_fail (icon_name != NULL, NULL);
2413 g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
2414 (flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
2415 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2416 g_return_val_if_fail (scale >= 1, NULL);
2417
2418 icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, icon_name, size, scale,
2419 flags | GTK_ICON_LOOKUP_USE_BUILTIN);
2420 if (!icon_info)
2421 {
2422 g_set_error (error, GTK_ICON_THEME_ERROR, GTK_ICON_THEME_NOT_FOUND,
2423 _("Icon '%s' not present in theme %s"), icon_name, icon_theme->priv->current_theme);
2424 return NULL;
2425 }
2426
2427 surface = gtk_icon_info_load_surface (icon_info, for_window, error);
2428 g_object_unref (icon_info);
2429
2430 return surface;
2431 }
2432
2433 /**
2434 * gtk_icon_theme_has_icon:
2435 * @icon_theme: a #GtkIconTheme
2436 * @icon_name: the name of an icon
2437 *
2438 * Checks whether an icon theme includes an icon
2439 * for a particular name.
2440 *
2441 * Returns: %TRUE if @icon_theme includes an
2442 * icon for @icon_name.
2443 *
2444 * Since: 2.4
2445 */
2446 gboolean
gtk_icon_theme_has_icon(GtkIconTheme * icon_theme,const gchar * icon_name)2447 gtk_icon_theme_has_icon (GtkIconTheme *icon_theme,
2448 const gchar *icon_name)
2449 {
2450 GtkIconThemePrivate *priv;
2451 GList *l;
2452
2453 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2454 g_return_val_if_fail (icon_name != NULL, FALSE);
2455
2456 priv = icon_theme->priv;
2457
2458 ensure_valid_themes (icon_theme);
2459
2460 for (l = priv->dir_mtimes; l; l = l->next)
2461 {
2462 IconThemeDirMtime *dir_mtime = l->data;
2463 GtkIconCache *cache = dir_mtime->cache;
2464
2465 if (cache && _gtk_icon_cache_has_icon (cache, icon_name))
2466 return TRUE;
2467 }
2468
2469 for (l = priv->themes; l; l = l->next)
2470 {
2471 if (theme_has_icon (l->data, icon_name))
2472 return TRUE;
2473 }
2474
2475 if (icon_theme_builtin_icons &&
2476 g_hash_table_lookup_extended (icon_theme_builtin_icons,
2477 icon_name, NULL, NULL))
2478 return TRUE;
2479
2480 return FALSE;
2481 }
2482
2483 static void
add_size(gpointer key,gpointer value,gpointer user_data)2484 add_size (gpointer key,
2485 gpointer value,
2486 gpointer user_data)
2487 {
2488 gint **res_p = user_data;
2489
2490 **res_p = GPOINTER_TO_INT (key);
2491
2492 (*res_p)++;
2493 }
2494
2495 /**
2496 * gtk_icon_theme_get_icon_sizes:
2497 * @icon_theme: a #GtkIconTheme
2498 * @icon_name: the name of an icon
2499 *
2500 * Returns an array of integers describing the sizes at which
2501 * the icon is available without scaling. A size of -1 means
2502 * that the icon is available in a scalable format. The array
2503 * is zero-terminated.
2504 *
2505 * Returns: (array zero-terminated=1) (transfer full): An newly
2506 * allocated array describing the sizes at which the icon is
2507 * available. The array should be freed with g_free() when it is no
2508 * longer needed.
2509 *
2510 * Since: 2.6
2511 */
2512 gint *
gtk_icon_theme_get_icon_sizes(GtkIconTheme * icon_theme,const gchar * icon_name)2513 gtk_icon_theme_get_icon_sizes (GtkIconTheme *icon_theme,
2514 const gchar *icon_name)
2515 {
2516 GList *l, *d, *icons;
2517 GHashTable *sizes;
2518 gint *result, *r;
2519 guint suffix;
2520 GtkIconThemePrivate *priv;
2521
2522 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2523
2524 priv = icon_theme->priv;
2525
2526 ensure_valid_themes (icon_theme);
2527
2528 sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
2529
2530 for (l = priv->themes; l; l = l->next)
2531 {
2532 IconTheme *theme = l->data;
2533 for (d = theme->dirs; d; d = d->next)
2534 {
2535 IconThemeDir *dir = d->data;
2536
2537 if (dir->type != ICON_THEME_DIR_SCALABLE && g_hash_table_lookup_extended (sizes, GINT_TO_POINTER (dir->size), NULL, NULL))
2538 continue;
2539
2540 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
2541 if (suffix != ICON_SUFFIX_NONE)
2542 {
2543 if (suffix == ICON_SUFFIX_SVG)
2544 g_hash_table_insert (sizes, GINT_TO_POINTER (-1), NULL);
2545 else
2546 g_hash_table_insert (sizes, GINT_TO_POINTER (dir->size), NULL);
2547 }
2548 }
2549 }
2550
2551 if (icon_theme_builtin_icons)
2552 {
2553 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
2554
2555 while (icons)
2556 {
2557 BuiltinIcon *icon = icons->data;
2558
2559 g_hash_table_insert (sizes, GINT_TO_POINTER (icon->size), NULL);
2560 icons = icons->next;
2561 }
2562 }
2563
2564 r = result = g_new0 (gint, g_hash_table_size (sizes) + 1);
2565
2566 g_hash_table_foreach (sizes, add_size, &r);
2567 g_hash_table_destroy (sizes);
2568
2569 return result;
2570 }
2571
2572 static void
add_key_to_hash(gpointer key,gpointer value,gpointer user_data)2573 add_key_to_hash (gpointer key,
2574 gpointer value,
2575 gpointer user_data)
2576 {
2577 GHashTable *hash = user_data;
2578
2579 g_hash_table_insert (hash, key, NULL);
2580 }
2581
2582 static void
add_key_to_list(gpointer key,gpointer value,gpointer user_data)2583 add_key_to_list (gpointer key,
2584 gpointer value,
2585 gpointer user_data)
2586 {
2587 GList **list = user_data;
2588
2589 *list = g_list_prepend (*list, g_strdup (key));
2590 }
2591
2592 /**
2593 * gtk_icon_theme_list_icons:
2594 * @icon_theme: a #GtkIconTheme
2595 * @context: (allow-none): a string identifying a particular type of
2596 * icon, or %NULL to list all icons.
2597 *
2598 * Lists the icons in the current icon theme. Only a subset
2599 * of the icons can be listed by providing a context string.
2600 * The set of values for the context string is system dependent,
2601 * but will typically include such values as “Applications” and
2602 * “MimeTypes”. Contexts are explained in the
2603 * [Icon Theme Specification](http://www.freedesktop.org/wiki/Specifications/icon-theme-spec).
2604 * The standard contexts are listed in the
2605 * [Icon Naming Specification](http://www.freedesktop.org/wiki/Specifications/icon-naming-spec).
2606 * Also see gtk_icon_theme_list_contexts().
2607 *
2608 * Returns: (element-type utf8) (transfer full): a #GList list
2609 * holding the names of all the icons in the theme. You must
2610 * first free each element in the list with g_free(), then
2611 * free the list itself with g_list_free().
2612 *
2613 * Since: 2.4
2614 */
2615 GList *
gtk_icon_theme_list_icons(GtkIconTheme * icon_theme,const gchar * context)2616 gtk_icon_theme_list_icons (GtkIconTheme *icon_theme,
2617 const gchar *context)
2618 {
2619 GtkIconThemePrivate *priv;
2620 GHashTable *icons;
2621 GList *list, *l;
2622 GQuark context_quark;
2623
2624 priv = icon_theme->priv;
2625
2626 ensure_valid_themes (icon_theme);
2627
2628 if (context)
2629 {
2630 context_quark = g_quark_try_string (context);
2631
2632 if (!context_quark)
2633 return NULL;
2634 }
2635 else
2636 context_quark = 0;
2637
2638 icons = g_hash_table_new (g_str_hash, g_str_equal);
2639
2640 l = priv->themes;
2641 while (l != NULL)
2642 {
2643 theme_list_icons (l->data, icons, context_quark);
2644 l = l->next;
2645 }
2646
2647 if (context_quark == 0)
2648 g_hash_table_foreach (priv->unthemed_icons,
2649 add_key_to_hash,
2650 icons);
2651
2652 list = NULL;
2653
2654 g_hash_table_foreach (icons,
2655 add_key_to_list,
2656 &list);
2657
2658 g_hash_table_destroy (icons);
2659
2660 return list;
2661 }
2662
2663 /**
2664 * gtk_icon_theme_list_contexts:
2665 * @icon_theme: a #GtkIconTheme
2666 *
2667 * Gets the list of contexts available within the current
2668 * hierarchy of icon themes.
2669 * See gtk_icon_theme_list_icons() for details about contexts.
2670 *
2671 * Returns: (element-type utf8) (transfer full): a #GList list
2672 * holding the names of all the contexts in the theme. You must first
2673 * free each element in the list with g_free(), then free the list
2674 * itself with g_list_free().
2675 *
2676 * Since: 2.12
2677 */
2678 GList *
gtk_icon_theme_list_contexts(GtkIconTheme * icon_theme)2679 gtk_icon_theme_list_contexts (GtkIconTheme *icon_theme)
2680 {
2681 GtkIconThemePrivate *priv;
2682 GHashTable *contexts;
2683 GList *list, *l;
2684
2685 priv = icon_theme->priv;
2686
2687 ensure_valid_themes (icon_theme);
2688
2689 contexts = g_hash_table_new (g_str_hash, g_str_equal);
2690
2691 l = priv->themes;
2692 while (l != NULL)
2693 {
2694 theme_list_contexts (l->data, contexts);
2695 l = l->next;
2696 }
2697
2698 list = NULL;
2699
2700 g_hash_table_foreach (contexts,
2701 add_key_to_list,
2702 &list);
2703
2704 g_hash_table_destroy (contexts);
2705
2706 return list;
2707 }
2708
2709 /**
2710 * gtk_icon_theme_get_example_icon_name:
2711 * @icon_theme: a #GtkIconTheme
2712 *
2713 * Gets the name of an icon that is representative of the
2714 * current theme (for instance, to use when presenting
2715 * a list of themes to the user.)
2716 *
2717 * Returns: (nullable): the name of an example icon or %NULL.
2718 * Free with g_free().
2719 *
2720 * Since: 2.4
2721 */
2722 gchar *
gtk_icon_theme_get_example_icon_name(GtkIconTheme * icon_theme)2723 gtk_icon_theme_get_example_icon_name (GtkIconTheme *icon_theme)
2724 {
2725 GtkIconThemePrivate *priv;
2726 GList *l;
2727 IconTheme *theme;
2728
2729 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
2730
2731 priv = icon_theme->priv;
2732
2733 ensure_valid_themes (icon_theme);
2734
2735 l = priv->themes;
2736 while (l != NULL)
2737 {
2738 theme = l->data;
2739 if (theme->example)
2740 return g_strdup (theme->example);
2741
2742 l = l->next;
2743 }
2744
2745 return NULL;
2746 }
2747
2748
2749 static gboolean
rescan_themes(GtkIconTheme * icon_theme)2750 rescan_themes (GtkIconTheme *icon_theme)
2751 {
2752 GtkIconThemePrivate *priv;
2753 IconThemeDirMtime *dir_mtime;
2754 GList *d;
2755 gint stat_res;
2756 GStatBuf stat_buf;
2757 GTimeVal tv;
2758
2759 priv = icon_theme->priv;
2760
2761 for (d = priv->dir_mtimes; d != NULL; d = d->next)
2762 {
2763 dir_mtime = d->data;
2764
2765 stat_res = g_stat (dir_mtime->dir, &stat_buf);
2766
2767 /* dir mtime didn't change */
2768 if (stat_res == 0 && dir_mtime->exists &&
2769 S_ISDIR (stat_buf.st_mode) &&
2770 dir_mtime->mtime == stat_buf.st_mtime)
2771 continue;
2772 /* didn't exist before, and still doesn't */
2773 if (!dir_mtime->exists &&
2774 (stat_res != 0 || !S_ISDIR (stat_buf.st_mode)))
2775 continue;
2776
2777 return TRUE;
2778 }
2779
2780 g_get_current_time (&tv);
2781 priv->last_stat_time = tv.tv_sec;
2782
2783 return FALSE;
2784 }
2785
2786 /**
2787 * gtk_icon_theme_rescan_if_needed:
2788 * @icon_theme: a #GtkIconTheme
2789 *
2790 * Checks to see if the icon theme has changed; if it has, any
2791 * currently cached information is discarded and will be reloaded
2792 * next time @icon_theme is accessed.
2793 *
2794 * Returns: %TRUE if the icon theme has changed and needed
2795 * to be reloaded.
2796 *
2797 * Since: 2.4
2798 */
2799 gboolean
gtk_icon_theme_rescan_if_needed(GtkIconTheme * icon_theme)2800 gtk_icon_theme_rescan_if_needed (GtkIconTheme *icon_theme)
2801 {
2802 gboolean retval;
2803
2804 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), FALSE);
2805
2806 retval = rescan_themes (icon_theme);
2807 if (retval)
2808 do_theme_change (icon_theme);
2809
2810 return retval;
2811 }
2812
2813 static void
theme_destroy(IconTheme * theme)2814 theme_destroy (IconTheme *theme)
2815 {
2816 g_free (theme->display_name);
2817 g_free (theme->comment);
2818 g_free (theme->name);
2819 g_free (theme->example);
2820
2821 g_list_free_full (theme->dirs, (GDestroyNotify) theme_dir_destroy);
2822
2823 g_free (theme);
2824 }
2825
2826 static void
theme_dir_destroy(IconThemeDir * dir)2827 theme_dir_destroy (IconThemeDir *dir)
2828 {
2829 if (dir->cache)
2830 _gtk_icon_cache_unref (dir->cache);
2831 if (dir->icons)
2832 g_hash_table_destroy (dir->icons);
2833
2834 g_free (dir->dir);
2835 g_free (dir->subdir);
2836 g_free (dir);
2837 }
2838
2839 static int
theme_dir_size_difference(IconThemeDir * dir,gint size,gint scale)2840 theme_dir_size_difference (IconThemeDir *dir,
2841 gint size,
2842 gint scale)
2843 {
2844 gint scaled_size, scaled_dir_size;
2845 gint min, max;
2846
2847 scaled_size = size * scale;
2848 scaled_dir_size = dir->size * dir->scale;
2849
2850 switch (dir->type)
2851 {
2852 case ICON_THEME_DIR_FIXED:
2853 return abs (scaled_size - scaled_dir_size);
2854 break;
2855 case ICON_THEME_DIR_SCALABLE:
2856 if (scaled_size < (dir->min_size * dir->scale))
2857 return (dir->min_size * dir->scale) - scaled_size;
2858 if (size > (dir->max_size * dir->scale))
2859 return scaled_size - (dir->max_size * dir->scale);
2860 return 0;
2861 break;
2862 case ICON_THEME_DIR_THRESHOLD:
2863 min = (dir->size - dir->threshold) * dir->scale;
2864 max = (dir->size + dir->threshold) * dir->scale;
2865 if (scaled_size < min)
2866 return min - scaled_size;
2867 if (scaled_size > max)
2868 return scaled_size - max;
2869 return 0;
2870 break;
2871 case ICON_THEME_DIR_UNTHEMED:
2872 g_assert_not_reached ();
2873 break;
2874 }
2875 g_assert_not_reached ();
2876 return 1000;
2877 }
2878
2879 static const gchar *
string_from_suffix(IconSuffix suffix)2880 string_from_suffix (IconSuffix suffix)
2881 {
2882 switch (suffix)
2883 {
2884 case ICON_SUFFIX_XPM:
2885 return ".xpm";
2886 case ICON_SUFFIX_SVG:
2887 return ".svg";
2888 case ICON_SUFFIX_PNG:
2889 return ".png";
2890 case ICON_SUFFIX_SYMBOLIC_PNG:
2891 return ".symbolic.png";
2892 default:
2893 g_assert_not_reached();
2894 }
2895 return NULL;
2896 }
2897
2898 static IconSuffix
suffix_from_name(const gchar * name)2899 suffix_from_name (const gchar *name)
2900 {
2901 IconSuffix retval = ICON_SUFFIX_NONE;
2902
2903 if (name != NULL)
2904 {
2905 if (g_str_has_suffix (name, ".symbolic.png"))
2906 retval = ICON_SUFFIX_SYMBOLIC_PNG;
2907 else if (g_str_has_suffix (name, ".png"))
2908 retval = ICON_SUFFIX_PNG;
2909 else if (g_str_has_suffix (name, ".svg"))
2910 retval = ICON_SUFFIX_SVG;
2911 else if (g_str_has_suffix (name, ".xpm"))
2912 retval = ICON_SUFFIX_XPM;
2913 }
2914
2915 return retval;
2916 }
2917
2918 static IconSuffix
best_suffix(IconSuffix suffix,gboolean allow_svg)2919 best_suffix (IconSuffix suffix,
2920 gboolean allow_svg)
2921 {
2922 if ((suffix & ICON_SUFFIX_SYMBOLIC_PNG) != 0)
2923 return ICON_SUFFIX_SYMBOLIC_PNG;
2924 else if ((suffix & ICON_SUFFIX_PNG) != 0)
2925 return ICON_SUFFIX_PNG;
2926 else if (allow_svg && ((suffix & ICON_SUFFIX_SVG) != 0))
2927 return ICON_SUFFIX_SVG;
2928 else if ((suffix & ICON_SUFFIX_XPM) != 0)
2929 return ICON_SUFFIX_XPM;
2930 else
2931 return ICON_SUFFIX_NONE;
2932 }
2933
2934 static IconSuffix
theme_dir_get_icon_suffix(IconThemeDir * dir,const gchar * icon_name,gboolean * has_icon_file)2935 theme_dir_get_icon_suffix (IconThemeDir *dir,
2936 const gchar *icon_name,
2937 gboolean *has_icon_file)
2938 {
2939 IconSuffix suffix, symbolic_suffix;
2940
2941 if (dir->cache)
2942 {
2943 suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2944 icon_name,
2945 dir->subdir_index);
2946
2947 if (icon_name_is_symbolic (icon_name))
2948 {
2949 /* Look for foo-symbolic.symbolic.png, as the cache only stores the ".png" suffix */
2950 char *icon_name_with_prefix = g_strconcat (icon_name, ".symbolic", NULL);
2951 symbolic_suffix = (IconSuffix)_gtk_icon_cache_get_icon_flags (dir->cache,
2952 icon_name_with_prefix,
2953 dir->subdir_index);
2954 g_free (icon_name_with_prefix);
2955
2956 if (symbolic_suffix & ICON_SUFFIX_PNG)
2957 suffix = ICON_SUFFIX_SYMBOLIC_PNG;
2958 }
2959
2960 if (has_icon_file)
2961 *has_icon_file = suffix & HAS_ICON_FILE;
2962
2963 suffix = suffix & ~HAS_ICON_FILE;
2964 }
2965 else
2966 suffix = GPOINTER_TO_UINT (g_hash_table_lookup (dir->icons, icon_name));
2967
2968 GTK_NOTE (ICONTHEME, g_message ("get icon suffix%s: %u", dir->cache ? " (cached)" : "", suffix));
2969
2970 return suffix;
2971 }
2972
2973 /* returns TRUE if dir_a is a better match */
2974 static gboolean
compare_dir_matches(IconThemeDir * dir_a,gint difference_a,IconThemeDir * dir_b,gint difference_b,gint requested_size,gint requested_scale)2975 compare_dir_matches (IconThemeDir *dir_a, gint difference_a,
2976 IconThemeDir *dir_b, gint difference_b,
2977 gint requested_size,
2978 gint requested_scale)
2979 {
2980 gint diff_a;
2981 gint diff_b;
2982
2983 if (difference_a == 0)
2984 {
2985 if (difference_b != 0)
2986 return TRUE;
2987
2988 /* a and b both exact matches */
2989 }
2990 else
2991 {
2992 /* If scaling, *always* prefer downscaling */
2993 if (dir_a->size >= requested_size &&
2994 dir_b->size < requested_size)
2995 return TRUE;
2996
2997 if (dir_a->size < requested_size &&
2998 dir_b->size >= requested_size)
2999 return FALSE;
3000
3001 /* Otherwise prefer the closest match */
3002
3003 if (difference_a < difference_b)
3004 return TRUE;
3005
3006 if (difference_a > difference_b)
3007 return FALSE;
3008
3009 /* same pixel difference */
3010 }
3011
3012 if (dir_a->scale == requested_scale &&
3013 dir_b->scale != requested_scale)
3014 return TRUE;
3015
3016 if (dir_a->scale != requested_scale &&
3017 dir_b->scale == requested_scale)
3018 return FALSE;
3019
3020 /* a and b both match the scale */
3021
3022 if (dir_a->type != ICON_THEME_DIR_SCALABLE &&
3023 dir_b->type == ICON_THEME_DIR_SCALABLE)
3024 return TRUE;
3025
3026 if (dir_a->type == ICON_THEME_DIR_SCALABLE &&
3027 dir_b->type != ICON_THEME_DIR_SCALABLE)
3028 return FALSE;
3029
3030 /* a and b both are scalable */
3031
3032 diff_a = abs (requested_size * requested_scale - dir_a->size * dir_a->scale);
3033 diff_b = abs (requested_size * requested_scale - dir_b->size * dir_b->scale);
3034
3035 return diff_a <= diff_b;
3036 }
3037
3038 static GtkIconInfo *
theme_lookup_icon(IconTheme * theme,const gchar * icon_name,gint size,gint scale,gboolean allow_svg,gboolean use_builtin)3039 theme_lookup_icon (IconTheme *theme,
3040 const gchar *icon_name,
3041 gint size,
3042 gint scale,
3043 gboolean allow_svg,
3044 gboolean use_builtin)
3045 {
3046 GList *dirs, *l;
3047 IconThemeDir *dir, *min_dir;
3048 gchar *file;
3049 gint min_difference, difference;
3050 BuiltinIcon *closest_builtin = NULL;
3051 IconSuffix suffix;
3052
3053 min_difference = G_MAXINT;
3054 min_dir = NULL;
3055
3056 /* Builtin icons are logically part of the default theme and
3057 * are searched before other subdirectories of the default theme.
3058 */
3059 if (use_builtin && strcmp (theme->name, FALLBACK_ICON_THEME) == 0)
3060 {
3061 closest_builtin = find_builtin_icon (icon_name,
3062 size, scale,
3063 &min_difference);
3064
3065 if (min_difference == 0)
3066 return icon_info_new_builtin (closest_builtin);
3067 }
3068
3069 dirs = theme->dirs;
3070
3071 l = dirs;
3072 while (l != NULL)
3073 {
3074 dir = l->data;
3075
3076 GTK_NOTE (ICONTHEME, g_message ("look up icon dir %s", dir->dir));
3077 suffix = theme_dir_get_icon_suffix (dir, icon_name, NULL);
3078 if (best_suffix (suffix, allow_svg) != ICON_SUFFIX_NONE)
3079 {
3080 difference = theme_dir_size_difference (dir, size, scale);
3081 if (min_dir == NULL ||
3082 compare_dir_matches (dir, difference,
3083 min_dir, min_difference,
3084 size, scale))
3085 {
3086 min_dir = dir;
3087 min_difference = difference;
3088 }
3089 }
3090
3091 l = l->next;
3092 }
3093
3094 if (min_dir)
3095 {
3096 GtkIconInfo *icon_info;
3097 gboolean has_icon_file = FALSE;
3098
3099 icon_info = icon_info_new (min_dir->type, min_dir->size, min_dir->scale);
3100 icon_info->min_size = min_dir->min_size;
3101 icon_info->max_size = min_dir->max_size;
3102
3103 suffix = theme_dir_get_icon_suffix (min_dir, icon_name, &has_icon_file);
3104 suffix = best_suffix (suffix, allow_svg);
3105 g_assert (suffix != ICON_SUFFIX_NONE);
3106
3107 if (min_dir->dir)
3108 {
3109 file = g_strconcat (icon_name, string_from_suffix (suffix), NULL);
3110 icon_info->filename = g_build_filename (min_dir->dir, file, NULL);
3111
3112 if (min_dir->is_resource)
3113 {
3114 gchar *uri;
3115 uri = g_strconcat ("resource://", icon_info->filename, NULL);
3116 icon_info->icon_file = g_file_new_for_uri (uri);
3117 g_free (uri);
3118 }
3119 else
3120 icon_info->icon_file = g_file_new_for_path (icon_info->filename);
3121
3122 icon_info->is_svg = suffix == ICON_SUFFIX_SVG;
3123 icon_info->is_resource = min_dir->is_resource;
3124 g_free (file);
3125 }
3126 else
3127 {
3128 icon_info->filename = NULL;
3129 icon_info->icon_file = NULL;
3130 }
3131
3132 if (min_dir->cache)
3133 {
3134 icon_info->cache_pixbuf = _gtk_icon_cache_get_icon (min_dir->cache, icon_name,
3135 min_dir->subdir_index);
3136 }
3137
3138 return icon_info;
3139 }
3140
3141 if (closest_builtin)
3142 return icon_info_new_builtin (closest_builtin);
3143
3144 return NULL;
3145 }
3146
3147 static void
theme_list_icons(IconTheme * theme,GHashTable * icons,GQuark context)3148 theme_list_icons (IconTheme *theme,
3149 GHashTable *icons,
3150 GQuark context)
3151 {
3152 GList *l = theme->dirs;
3153 IconThemeDir *dir;
3154
3155 while (l != NULL)
3156 {
3157 dir = l->data;
3158
3159 if (context == dir->context ||
3160 context == 0)
3161 {
3162 if (dir->cache)
3163 _gtk_icon_cache_add_icons (dir->cache, dir->subdir, icons);
3164 else
3165 g_hash_table_foreach (dir->icons, add_key_to_hash, icons);
3166 }
3167 l = l->next;
3168 }
3169 }
3170
3171 static gboolean
theme_has_icon(IconTheme * theme,const gchar * icon_name)3172 theme_has_icon (IconTheme *theme,
3173 const gchar *icon_name)
3174 {
3175 GList *l;
3176
3177 for (l = theme->dirs; l; l = l->next)
3178 {
3179 IconThemeDir *dir = l->data;
3180
3181 if (dir->cache)
3182 {
3183 if (_gtk_icon_cache_has_icon (dir->cache, icon_name))
3184 return TRUE;
3185 }
3186 else
3187 {
3188 if (g_hash_table_lookup (dir->icons, icon_name) != NULL)
3189 return TRUE;
3190 }
3191 }
3192
3193 return FALSE;
3194 }
3195
3196 static void
theme_list_contexts(IconTheme * theme,GHashTable * contexts)3197 theme_list_contexts (IconTheme *theme,
3198 GHashTable *contexts)
3199 {
3200 GList *l = theme->dirs;
3201 IconThemeDir *dir;
3202 const gchar *context;
3203
3204 while (l != NULL)
3205 {
3206 dir = l->data;
3207
3208 /* The "Context" key can be unset */
3209 if (dir->context != 0)
3210 {
3211 context = g_quark_to_string (dir->context);
3212 g_hash_table_replace (contexts, (gpointer) context, NULL);
3213 }
3214
3215 l = l->next;
3216 }
3217 }
3218
3219 static gboolean
scan_directory(GtkIconThemePrivate * icon_theme,IconThemeDir * dir,gchar * full_dir)3220 scan_directory (GtkIconThemePrivate *icon_theme,
3221 IconThemeDir *dir,
3222 gchar *full_dir)
3223 {
3224 GDir *gdir;
3225 const gchar *name;
3226
3227 GTK_NOTE (ICONTHEME, g_message ("scanning directory %s", full_dir));
3228
3229 gdir = g_dir_open (full_dir, 0, NULL);
3230
3231 if (gdir == NULL)
3232 return FALSE;
3233
3234 dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3235
3236 while ((name = g_dir_read_name (gdir)))
3237 {
3238 gchar *base_name;
3239 IconSuffix suffix, hash_suffix;
3240
3241 suffix = suffix_from_name (name);
3242 if (suffix == ICON_SUFFIX_NONE)
3243 continue;
3244
3245 base_name = strip_suffix (name);
3246
3247 hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
3248 /* takes ownership of base_name */
3249 g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix|suffix));
3250 }
3251
3252 g_dir_close (gdir);
3253
3254 return g_hash_table_size (dir->icons) > 0;
3255 }
3256
3257 static gboolean
scan_resources(GtkIconThemePrivate * icon_theme,IconThemeDir * dir,gchar * full_dir)3258 scan_resources (GtkIconThemePrivate *icon_theme,
3259 IconThemeDir *dir,
3260 gchar *full_dir)
3261 {
3262 gint i;
3263 gchar **children;
3264
3265 GTK_NOTE (ICONTHEME, g_message ("scanning resources %s", full_dir));
3266
3267 children = g_resources_enumerate_children (full_dir, 0, NULL);
3268 if (!children)
3269 return FALSE;
3270
3271 dir->icons = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3272
3273 for (i = 0; children[i]; i++)
3274 {
3275 gchar *base_name;
3276 IconSuffix suffix, hash_suffix;
3277
3278 suffix = suffix_from_name (children[i]);
3279 if (suffix == ICON_SUFFIX_NONE)
3280 continue;
3281
3282 base_name = strip_suffix (children[i]);
3283
3284 hash_suffix = GPOINTER_TO_INT (g_hash_table_lookup (dir->icons, base_name));
3285 /* takes ownership of base_name */
3286 g_hash_table_replace (dir->icons, base_name, GUINT_TO_POINTER (hash_suffix|suffix));
3287 }
3288 g_strfreev (children);
3289
3290 return g_hash_table_size (dir->icons) > 0;
3291 }
3292
3293 static void
theme_subdir_load(GtkIconTheme * icon_theme,IconTheme * theme,GKeyFile * theme_file,gchar * subdir)3294 theme_subdir_load (GtkIconTheme *icon_theme,
3295 IconTheme *theme,
3296 GKeyFile *theme_file,
3297 gchar *subdir)
3298 {
3299 GList *d;
3300 gchar *type_string;
3301 IconThemeDir *dir;
3302 IconThemeDirType type;
3303 gchar *context_string;
3304 GQuark context;
3305 gint size;
3306 gint min_size;
3307 gint max_size;
3308 gint threshold;
3309 gchar *full_dir;
3310 GError *error = NULL;
3311 IconThemeDirMtime *dir_mtime;
3312 gint scale;
3313 gboolean has_icons;
3314
3315 size = g_key_file_get_integer (theme_file, subdir, "Size", &error);
3316 if (error)
3317 {
3318 g_error_free (error);
3319 g_warning ("Theme directory %s of theme %s has no size field\n",
3320 subdir, theme->name);
3321 return;
3322 }
3323
3324 type = ICON_THEME_DIR_THRESHOLD;
3325 type_string = g_key_file_get_string (theme_file, subdir, "Type", NULL);
3326 if (type_string)
3327 {
3328 if (strcmp (type_string, "Fixed") == 0)
3329 type = ICON_THEME_DIR_FIXED;
3330 else if (strcmp (type_string, "Scalable") == 0)
3331 type = ICON_THEME_DIR_SCALABLE;
3332 else if (strcmp (type_string, "Threshold") == 0)
3333 type = ICON_THEME_DIR_THRESHOLD;
3334
3335 g_free (type_string);
3336 }
3337
3338 context = 0;
3339 context_string = g_key_file_get_string (theme_file, subdir, "Context", NULL);
3340 if (context_string)
3341 {
3342 context = g_quark_from_string (context_string);
3343 g_free (context_string);
3344 }
3345
3346 if (g_key_file_has_key (theme_file, subdir, "MaxSize", NULL))
3347 max_size = g_key_file_get_integer (theme_file, subdir, "MaxSize", NULL);
3348 else
3349 max_size = size;
3350
3351 if (g_key_file_has_key (theme_file, subdir, "MinSize", NULL))
3352 min_size = g_key_file_get_integer (theme_file, subdir, "MinSize", NULL);
3353 else
3354 min_size = size;
3355
3356 if (g_key_file_has_key (theme_file, subdir, "Threshold", NULL))
3357 threshold = g_key_file_get_integer (theme_file, subdir, "Threshold", NULL);
3358 else
3359 threshold = 2;
3360
3361 if (g_key_file_has_key (theme_file, subdir, "Scale", NULL))
3362 scale = g_key_file_get_integer (theme_file, subdir, "Scale", NULL);
3363 else
3364 scale = 1;
3365
3366 for (d = icon_theme->priv->dir_mtimes; d; d = d->next)
3367 {
3368 dir_mtime = (IconThemeDirMtime *)d->data;
3369
3370 if (!dir_mtime->exists)
3371 continue; /* directory doesn't exist */
3372
3373 full_dir = g_build_filename (dir_mtime->dir, subdir, NULL);
3374
3375 /* First, see if we have a cache for the directory */
3376 if (dir_mtime->cache != NULL || g_file_test (full_dir, G_FILE_TEST_IS_DIR))
3377 {
3378 if (dir_mtime->cache == NULL)
3379 {
3380 /* This will return NULL if the cache doesn't exist or is outdated */
3381 dir_mtime->cache = _gtk_icon_cache_new_for_path (dir_mtime->dir);
3382 }
3383
3384 dir = g_new0 (IconThemeDir, 1);
3385 dir->type = type;
3386 dir->is_resource = FALSE;
3387 dir->context = context;
3388 dir->size = size;
3389 dir->min_size = min_size;
3390 dir->max_size = max_size;
3391 dir->threshold = threshold;
3392 dir->dir = full_dir;
3393 dir->subdir = g_strdup (subdir);
3394 dir->scale = scale;
3395
3396 if (dir_mtime->cache != NULL)
3397 {
3398 dir->cache = _gtk_icon_cache_ref (dir_mtime->cache);
3399 dir->subdir_index = _gtk_icon_cache_get_directory_index (dir->cache, dir->subdir);
3400 has_icons = _gtk_icon_cache_has_icons (dir->cache, dir->subdir);
3401 }
3402 else
3403 {
3404 dir->cache = NULL;
3405 dir->subdir_index = -1;
3406 has_icons = scan_directory (icon_theme->priv, dir, full_dir);
3407 }
3408
3409 if (has_icons)
3410 theme->dirs = g_list_prepend (theme->dirs, dir);
3411 else
3412 theme_dir_destroy (dir);
3413 }
3414 else
3415 g_free (full_dir);
3416 }
3417
3418 if (strcmp (theme->name, FALLBACK_ICON_THEME) == 0)
3419 {
3420 for (d = icon_theme->priv->resource_paths; d; d = d->next)
3421 {
3422 /* Force a trailing / here, to avoid extra copies in GResource */
3423 full_dir = g_build_filename ((const gchar *)d->data, subdir, " ", NULL);
3424 full_dir[strlen (full_dir) - 1] = '\0';
3425 dir = g_new0 (IconThemeDir, 1);
3426 dir->type = type;
3427 dir->is_resource = TRUE;
3428 dir->context = context;
3429 dir->size = size;
3430 dir->min_size = min_size;
3431 dir->max_size = max_size;
3432 dir->threshold = threshold;
3433 dir->dir = full_dir;
3434 dir->subdir = g_strdup (subdir);
3435 dir->scale = scale;
3436 dir->cache = NULL;
3437 dir->subdir_index = -1;
3438
3439 if (scan_resources (icon_theme->priv, dir, full_dir))
3440 theme->dirs = g_list_prepend (theme->dirs, dir);
3441 else
3442 theme_dir_destroy (dir);
3443 }
3444 }
3445 }
3446
3447 /*
3448 * GtkIconInfo
3449 */
3450
3451 static void gtk_icon_info_class_init (GtkIconInfoClass *klass);
3452
G_DEFINE_TYPE(GtkIconInfo,gtk_icon_info,G_TYPE_OBJECT)3453 G_DEFINE_TYPE (GtkIconInfo, gtk_icon_info, G_TYPE_OBJECT)
3454
3455 static void
3456 gtk_icon_info_init (GtkIconInfo *icon_info)
3457 {
3458 icon_info->scale = -1.;
3459 }
3460
3461 static GtkIconInfo *
icon_info_new(IconThemeDirType type,gint dir_size,gint dir_scale)3462 icon_info_new (IconThemeDirType type,
3463 gint dir_size,
3464 gint dir_scale)
3465 {
3466 GtkIconInfo *icon_info;
3467
3468 icon_info = g_object_new (GTK_TYPE_ICON_INFO, NULL);
3469
3470 icon_info->dir_type = type;
3471 icon_info->dir_size = dir_size;
3472 icon_info->dir_scale = dir_scale;
3473 icon_info->unscaled_scale = 1.0;
3474 icon_info->is_svg = FALSE;
3475 icon_info->is_resource = FALSE;
3476
3477 return icon_info;
3478 }
3479
3480 /* This only copies whatever is needed to load the pixbuf,
3481 * so that we can do a load in a thread without affecting
3482 * the original IconInfo from the thread.
3483 */
3484 static GtkIconInfo *
icon_info_dup(GtkIconInfo * icon_info)3485 icon_info_dup (GtkIconInfo *icon_info)
3486 {
3487 GtkIconInfo *dup;
3488 GSList *l;
3489
3490 dup = icon_info_new (icon_info->dir_type, icon_info->dir_size, icon_info->dir_scale);
3491
3492 dup->filename = g_strdup (icon_info->filename);
3493 dup->is_svg = icon_info->is_svg;
3494
3495 if (icon_info->icon_file)
3496 dup->icon_file = g_object_ref (icon_info->icon_file);
3497 if (icon_info->loadable)
3498 dup->loadable = g_object_ref (icon_info->loadable);
3499 if (icon_info->pixbuf)
3500 dup->pixbuf = g_object_ref (icon_info->pixbuf);
3501
3502 for (l = icon_info->emblem_infos; l != NULL; l = l->next)
3503 {
3504 dup->emblem_infos =
3505 g_slist_append (dup->emblem_infos,
3506 icon_info_dup (l->data));
3507 }
3508
3509 if (icon_info->cache_pixbuf)
3510 dup->cache_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3511
3512 dup->scale = icon_info->scale;
3513 dup->unscaled_scale = icon_info->unscaled_scale;
3514 dup->desired_size = icon_info->desired_size;
3515 dup->desired_scale = icon_info->desired_scale;
3516 dup->forced_size = icon_info->forced_size;
3517 dup->emblems_applied = icon_info->emblems_applied;
3518 dup->is_resource = icon_info->is_resource;
3519 dup->min_size = icon_info->min_size;
3520 dup->max_size = icon_info->max_size;
3521 dup->symbolic_width = icon_info->symbolic_width;
3522 dup->symbolic_height = icon_info->symbolic_height;
3523
3524 return dup;
3525 }
3526
3527 static GtkIconInfo *
icon_info_new_builtin(BuiltinIcon * icon)3528 icon_info_new_builtin (BuiltinIcon *icon)
3529 {
3530 GtkIconInfo *icon_info = icon_info_new (ICON_THEME_DIR_THRESHOLD, icon->size, 1);
3531
3532 icon_info->cache_pixbuf = g_object_ref (icon->pixbuf);
3533
3534 return icon_info;
3535 }
3536
3537 /**
3538 * gtk_icon_info_copy: (skip)
3539 * @icon_info: a #GtkIconInfo
3540 *
3541 * Make a copy of a #GtkIconInfo.
3542 *
3543 * Returns: (transfer full): the new GtkIconInfo
3544 *
3545 * Since: 2.4
3546 *
3547 * Deprecated: 3.8: Use g_object_ref()
3548 */
3549 GtkIconInfo *
gtk_icon_info_copy(GtkIconInfo * icon_info)3550 gtk_icon_info_copy (GtkIconInfo *icon_info)
3551 {
3552 g_return_val_if_fail (icon_info != NULL, NULL);
3553 return g_object_ref (icon_info);
3554 }
3555
3556 /**
3557 * gtk_icon_info_free: (skip)
3558 * @icon_info: a #GtkIconInfo
3559 *
3560 * Free a #GtkIconInfo and associated information
3561 *
3562 * Since: 2.4
3563 *
3564 * Deprecated: 3.8: Use g_object_unref()
3565 */
3566 void
gtk_icon_info_free(GtkIconInfo * icon_info)3567 gtk_icon_info_free (GtkIconInfo *icon_info)
3568 {
3569 g_return_if_fail (icon_info != NULL);
3570 g_object_unref (icon_info);
3571 }
3572
3573 static void
gtk_icon_info_finalize(GObject * object)3574 gtk_icon_info_finalize (GObject *object)
3575 {
3576 GtkIconInfo *icon_info = (GtkIconInfo *) object;
3577
3578 if (icon_info->in_cache)
3579 g_hash_table_remove (icon_info->in_cache->priv->info_cache, &icon_info->key);
3580
3581 g_strfreev (icon_info->key.icon_names);
3582
3583 g_free (icon_info->filename);
3584 g_clear_object (&icon_info->icon_file);
3585
3586 g_clear_object (&icon_info->loadable);
3587 g_slist_free_full (icon_info->emblem_infos, (GDestroyNotify) g_object_unref);
3588 g_clear_object (&icon_info->pixbuf);
3589 g_clear_object (&icon_info->proxy_pixbuf);
3590 g_clear_object (&icon_info->cache_pixbuf);
3591 g_clear_error (&icon_info->load_error);
3592
3593 symbolic_pixbuf_cache_free (icon_info->symbolic_pixbuf_cache);
3594
3595 G_OBJECT_CLASS (gtk_icon_info_parent_class)->finalize (object);
3596 }
3597
3598 static void
gtk_icon_info_class_init(GtkIconInfoClass * klass)3599 gtk_icon_info_class_init (GtkIconInfoClass *klass)
3600 {
3601 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
3602
3603 gobject_class->finalize = gtk_icon_info_finalize;
3604 }
3605
3606 /**
3607 * gtk_icon_info_get_base_size:
3608 * @icon_info: a #GtkIconInfo
3609 *
3610 * Gets the base size for the icon. The base size
3611 * is a size for the icon that was specified by
3612 * the icon theme creator. This may be different
3613 * than the actual size of image; an example of
3614 * this is small emblem icons that can be attached
3615 * to a larger icon. These icons will be given
3616 * the same base size as the larger icons to which
3617 * they are attached.
3618 *
3619 * Note that for scaled icons the base size does
3620 * not include the base scale.
3621 *
3622 * Returns: the base size, or 0, if no base
3623 * size is known for the icon.
3624 *
3625 * Since: 2.4
3626 */
3627 gint
gtk_icon_info_get_base_size(GtkIconInfo * icon_info)3628 gtk_icon_info_get_base_size (GtkIconInfo *icon_info)
3629 {
3630 g_return_val_if_fail (icon_info != NULL, 0);
3631
3632 return icon_info->dir_size;
3633 }
3634
3635 /**
3636 * gtk_icon_info_get_base_scale:
3637 * @icon_info: a #GtkIconInfo
3638 *
3639 * Gets the base scale for the icon. The base scale is a scale
3640 * for the icon that was specified by the icon theme creator.
3641 * For instance an icon drawn for a high-dpi screen with window
3642 * scale 2 for a base size of 32 will be 64 pixels tall and have
3643 * a base scale of 2.
3644 *
3645 * Returns: the base scale
3646 *
3647 * Since: 3.10
3648 */
3649 gint
gtk_icon_info_get_base_scale(GtkIconInfo * icon_info)3650 gtk_icon_info_get_base_scale (GtkIconInfo *icon_info)
3651 {
3652 g_return_val_if_fail (icon_info != NULL, 0);
3653
3654 return icon_info->dir_scale;
3655 }
3656
3657 /**
3658 * gtk_icon_info_get_filename:
3659 * @icon_info: a #GtkIconInfo
3660 *
3661 * Gets the filename for the icon. If the %GTK_ICON_LOOKUP_USE_BUILTIN
3662 * flag was passed to gtk_icon_theme_lookup_icon(), there may be no
3663 * filename if a builtin icon is returned; in this case, you should
3664 * use gtk_icon_info_get_builtin_pixbuf().
3665 *
3666 * Returns: (nullable) (type filename): the filename for the icon, or %NULL
3667 * if gtk_icon_info_get_builtin_pixbuf() should be used instead.
3668 * The return value is owned by GTK+ and should not be modified
3669 * or freed.
3670 *
3671 * Since: 2.4
3672 */
3673 const gchar *
gtk_icon_info_get_filename(GtkIconInfo * icon_info)3674 gtk_icon_info_get_filename (GtkIconInfo *icon_info)
3675 {
3676 g_return_val_if_fail (icon_info != NULL, NULL);
3677
3678 return icon_info->filename;
3679 }
3680
3681 /**
3682 * gtk_icon_info_get_builtin_pixbuf:
3683 * @icon_info: a #GtkIconInfo
3684 *
3685 * Gets the built-in image for this icon, if any. To allow GTK+ to use
3686 * built in icon images, you must pass the %GTK_ICON_LOOKUP_USE_BUILTIN
3687 * to gtk_icon_theme_lookup_icon().
3688 *
3689 * Returns: (nullable) (transfer none): the built-in image pixbuf, or %NULL.
3690 * No extra reference is added to the returned pixbuf, so if
3691 * you want to keep it around, you must use g_object_ref().
3692 * The returned image must not be modified.
3693 *
3694 * Since: 2.4
3695 *
3696 * Deprecated: 3.14: This function is deprecated, use
3697 * gtk_icon_theme_add_resource_path() instead of builtin icons.
3698 */
3699 GdkPixbuf *
gtk_icon_info_get_builtin_pixbuf(GtkIconInfo * icon_info)3700 gtk_icon_info_get_builtin_pixbuf (GtkIconInfo *icon_info)
3701 {
3702 g_return_val_if_fail (icon_info != NULL, NULL);
3703
3704 if (icon_info->filename)
3705 return NULL;
3706
3707 return icon_info->cache_pixbuf;
3708 }
3709
3710 /**
3711 * gtk_icon_info_is_symbolic:
3712 * @icon_info: a #GtkIconInfo
3713 *
3714 * Checks if the icon is symbolic or not. This currently uses only
3715 * the file name and not the file contents for determining this.
3716 * This behaviour may change in the future.
3717 *
3718 * Returns: %TRUE if the icon is symbolic, %FALSE otherwise
3719 *
3720 * Since: 3.12
3721 */
3722 gboolean
gtk_icon_info_is_symbolic(GtkIconInfo * icon_info)3723 gtk_icon_info_is_symbolic (GtkIconInfo *icon_info)
3724 {
3725 gchar *icon_uri;
3726 gboolean is_symbolic;
3727
3728 g_return_val_if_fail (GTK_IS_ICON_INFO (icon_info), FALSE);
3729
3730 icon_uri = NULL;
3731 if (icon_info->icon_file)
3732 icon_uri = g_file_get_uri (icon_info->icon_file);
3733
3734 is_symbolic = (icon_uri != NULL) && (icon_uri_is_symbolic (icon_uri));
3735 g_free (icon_uri);
3736
3737 return is_symbolic;
3738 }
3739
3740 static GdkPixbuf *
apply_emblems_to_pixbuf(GdkPixbuf * pixbuf,GtkIconInfo * info)3741 apply_emblems_to_pixbuf (GdkPixbuf *pixbuf,
3742 GtkIconInfo *info)
3743 {
3744 GdkPixbuf *icon = NULL;
3745 gint w, h, pos;
3746 GSList *l;
3747
3748 if (info->emblem_infos == NULL)
3749 return NULL;
3750
3751 w = gdk_pixbuf_get_width (pixbuf);
3752 h = gdk_pixbuf_get_height (pixbuf);
3753
3754 for (l = info->emblem_infos, pos = 0; l; l = l->next, pos++)
3755 {
3756 GtkIconInfo *emblem_info = l->data;
3757
3758 if (icon_info_ensure_scale_and_pixbuf (emblem_info))
3759 {
3760 GdkPixbuf *emblem = emblem_info->pixbuf;
3761 gint ew, eh;
3762 gint x = 0, y = 0; /* silence compiler */
3763 gdouble scale;
3764
3765 ew = gdk_pixbuf_get_width (emblem);
3766 eh = gdk_pixbuf_get_height (emblem);
3767 if (ew >= w)
3768 {
3769 scale = 0.75;
3770 ew = ew * 0.75;
3771 eh = eh * 0.75;
3772 }
3773 else
3774 scale = 1.0;
3775
3776 switch (pos % 4)
3777 {
3778 case 0:
3779 x = w - ew;
3780 y = h - eh;
3781 break;
3782 case 1:
3783 x = w - ew;
3784 y = 0;
3785 break;
3786 case 2:
3787 x = 0;
3788 y = h - eh;
3789 break;
3790 case 3:
3791 x = 0;
3792 y = 0;
3793 break;
3794 }
3795
3796 if (icon == NULL)
3797 {
3798 icon = gdk_pixbuf_copy (pixbuf);
3799 if (icon == NULL)
3800 break;
3801 }
3802
3803 gdk_pixbuf_composite (emblem, icon, x, y, ew, eh, x, y,
3804 scale, scale, GDK_INTERP_BILINEAR, 255);
3805 }
3806 }
3807
3808 return icon;
3809 }
3810
3811 /* Combine the icon with all emblems, the first emblem is placed
3812 * in the southeast corner. Scale emblems to be at most 3/4 of the
3813 * size of the icon itself.
3814 */
3815 static void
apply_emblems(GtkIconInfo * info)3816 apply_emblems (GtkIconInfo *info)
3817 {
3818 GdkPixbuf *icon;
3819
3820 if (info->emblems_applied)
3821 return;
3822
3823 icon = apply_emblems_to_pixbuf (info->pixbuf, info);
3824
3825 if (icon)
3826 {
3827 g_object_unref (info->pixbuf);
3828 info->pixbuf = icon;
3829 info->emblems_applied = TRUE;
3830 }
3831 }
3832
3833 /* If this returns TRUE, its safe to call icon_info_ensure_scale_and_pixbuf
3834 * without blocking
3835 */
3836 static gboolean
icon_info_get_pixbuf_ready(GtkIconInfo * icon_info)3837 icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
3838 {
3839 if (icon_info->pixbuf &&
3840 (icon_info->emblem_infos == NULL || icon_info->emblems_applied))
3841 return TRUE;
3842
3843 if (icon_info->load_error)
3844 return TRUE;
3845
3846 return FALSE;
3847 }
3848
3849 /* This function contains the complicated logic for deciding
3850 * on the size at which to load the icon and loading it at
3851 * that size.
3852 */
3853 static gboolean
icon_info_ensure_scale_and_pixbuf(GtkIconInfo * icon_info)3854 icon_info_ensure_scale_and_pixbuf (GtkIconInfo *icon_info)
3855 {
3856 gint image_width, image_height, image_size;
3857 gint scaled_desired_size;
3858 GdkPixbuf *source_pixbuf;
3859 gdouble dir_scale;
3860
3861 if (icon_info->pixbuf)
3862 {
3863 apply_emblems (icon_info);
3864 return TRUE;
3865 }
3866
3867 if (icon_info->load_error)
3868 return FALSE;
3869
3870 if (icon_info->icon_file && !icon_info->loadable)
3871 icon_info->loadable = G_LOADABLE_ICON (g_file_icon_new (icon_info->icon_file));
3872
3873 scaled_desired_size = icon_info->desired_size * icon_info->desired_scale;
3874
3875 dir_scale = icon_info->dir_scale;
3876
3877 /* In many cases, the scale can be determined without actual access
3878 * to the icon file. This is generally true when we have a size
3879 * for the directory where the icon is; the image size doesn't
3880 * matter in that case.
3881 */
3882 if (icon_info->forced_size ||
3883 icon_info->dir_type == ICON_THEME_DIR_UNTHEMED)
3884 icon_info->scale = -1;
3885 else if (icon_info->dir_type == ICON_THEME_DIR_FIXED ||
3886 icon_info->dir_type == ICON_THEME_DIR_THRESHOLD)
3887 icon_info->scale = icon_info->unscaled_scale;
3888 else if (icon_info->dir_type == ICON_THEME_DIR_SCALABLE)
3889 {
3890 /* For svg icons, treat scalable directories as if they had
3891 * a Scale=<desired_scale> entry. In particular, this means
3892 * spinners that are restriced to size 32 will loaded at size
3893 * up to 64 with Scale=2.
3894 */
3895 if (icon_info->is_svg)
3896 dir_scale = icon_info->desired_scale;
3897
3898 if (scaled_desired_size < icon_info->min_size * dir_scale)
3899 icon_info->scale = (gdouble) icon_info->min_size / (gdouble) icon_info->dir_size;
3900 else if (scaled_desired_size > icon_info->max_size * dir_scale)
3901 icon_info->scale = (gdouble) icon_info->max_size / (gdouble) icon_info->dir_size;
3902 else
3903 icon_info->scale = (gdouble) scaled_desired_size / (icon_info->dir_size * dir_scale);
3904 }
3905
3906 /* At this point, we need to actually get the icon; either from the
3907 * builtin image or by loading the file
3908 */
3909 source_pixbuf = NULL;
3910 if (icon_info->cache_pixbuf)
3911 source_pixbuf = g_object_ref (icon_info->cache_pixbuf);
3912 else if (icon_info->is_resource)
3913 {
3914 if (icon_info->is_svg)
3915 {
3916 gint size;
3917
3918 if (icon_info->forced_size || icon_info->dir_type == ICON_THEME_DIR_UNTHEMED)
3919 size = scaled_desired_size;
3920 else
3921 size = icon_info->dir_size * dir_scale * icon_info->scale;
3922
3923 if (size == 0)
3924 source_pixbuf = _gdk_pixbuf_new_from_resource_scaled (icon_info->filename,
3925 icon_info->desired_scale,
3926 &icon_info->load_error);
3927 else
3928 source_pixbuf = gdk_pixbuf_new_from_resource_at_scale (icon_info->filename,
3929 size, size, TRUE,
3930 &icon_info->load_error);
3931 }
3932 else
3933 source_pixbuf = gdk_pixbuf_new_from_resource (icon_info->filename,
3934 &icon_info->load_error);
3935 }
3936 else
3937 {
3938 GInputStream *stream;
3939
3940 /* TODO: We should have a load_at_scale */
3941 stream = g_loadable_icon_load (icon_info->loadable,
3942 scaled_desired_size,
3943 NULL, NULL,
3944 &icon_info->load_error);
3945 if (stream)
3946 {
3947 /* SVG icons are a special case - we just immediately scale them
3948 * to the desired size
3949 */
3950 if (icon_info->is_svg)
3951 {
3952 gint size;
3953
3954 if (icon_info->forced_size || icon_info->dir_type == ICON_THEME_DIR_UNTHEMED)
3955 size = scaled_desired_size;
3956 else
3957 size = icon_info->dir_size * dir_scale * icon_info->scale;
3958 if (size == 0)
3959 source_pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream,
3960 icon_info->desired_scale,
3961 NULL,
3962 &icon_info->load_error);
3963 else
3964 source_pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
3965 size, size,
3966 TRUE, NULL,
3967 &icon_info->load_error);
3968 }
3969 else
3970 source_pixbuf = gdk_pixbuf_new_from_stream (stream,
3971 NULL,
3972 &icon_info->load_error);
3973 g_object_unref (stream);
3974 }
3975 }
3976
3977 if (!source_pixbuf)
3978 {
3979 static gboolean warn_about_load_failure = TRUE;
3980
3981 if (warn_about_load_failure)
3982 {
3983 gchar *path;
3984
3985 if (icon_info->is_resource)
3986 path = g_strdup (icon_info->filename);
3987 else if (G_IS_FILE (icon_info->loadable))
3988 path = g_file_get_path (G_FILE (icon_info->loadable));
3989 else
3990 path = g_strdup ("icon theme");
3991
3992 g_warning ("Could not load a pixbuf from %s.\n"
3993 "This may indicate that pixbuf loaders or the mime database could not be found.",
3994 path);
3995 g_free (path);
3996
3997 warn_about_load_failure = FALSE;
3998 }
3999
4000 return FALSE;
4001 }
4002
4003 /* Do scale calculations that depend on the image size
4004 */
4005 image_width = gdk_pixbuf_get_width (source_pixbuf);
4006 image_height = gdk_pixbuf_get_height (source_pixbuf);
4007 image_size = MAX (image_width, image_height);
4008
4009 if (icon_info->is_svg)
4010 icon_info->scale = image_size / 1000.;
4011 else if (icon_info->scale < 0.0)
4012 {
4013 if (image_size > 0 && scaled_desired_size > 0)
4014 icon_info->scale = (gdouble)scaled_desired_size / (gdouble)image_size;
4015 else
4016 icon_info->scale = 1.0;
4017
4018 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED &&
4019 !icon_info->forced_size)
4020 icon_info->scale = MIN (icon_info->scale, 1.0);
4021 }
4022
4023 if (icon_info->is_svg)
4024 icon_info->pixbuf = source_pixbuf;
4025 else if (icon_info->scale == 1.0)
4026 icon_info->pixbuf = source_pixbuf;
4027 else
4028 {
4029 icon_info->pixbuf = gdk_pixbuf_scale_simple (source_pixbuf,
4030 MAX (1, 0.5 + image_width * icon_info->scale),
4031 MAX (1, 0.5 + image_height * icon_info->scale),
4032 GDK_INTERP_BILINEAR);
4033 g_object_unref (source_pixbuf);
4034 }
4035
4036 apply_emblems (icon_info);
4037
4038 return TRUE;
4039 }
4040
4041 static void
proxy_pixbuf_destroy(guchar * pixels,gpointer data)4042 proxy_pixbuf_destroy (guchar *pixels, gpointer data)
4043 {
4044 GtkIconInfo *icon_info = data;
4045 GtkIconTheme *icon_theme = icon_info->in_cache;
4046
4047 g_assert (icon_info->proxy_pixbuf != NULL);
4048 icon_info->proxy_pixbuf = NULL;
4049
4050 /* Keep it alive a bit longer */
4051 if (icon_theme != NULL)
4052 ensure_in_lru_cache (icon_theme, icon_info);
4053
4054 g_object_unref (icon_info);
4055 }
4056
4057 /**
4058 * gtk_icon_info_load_icon:
4059 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
4060 * @error: (allow-none): location to store error information on failure,
4061 * or %NULL.
4062 *
4063 * Renders an icon previously looked up in an icon theme using
4064 * gtk_icon_theme_lookup_icon(); the size will be based on the size
4065 * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
4066 * pixbuf may not be exactly this size; an icon theme may have icons
4067 * that differ slightly from their nominal sizes, and in addition GTK+
4068 * will avoid scaling icons that it considers sufficiently close to the
4069 * requested size or for which the source image would have to be scaled
4070 * up too far. (This maintains sharpness.). This behaviour can be changed
4071 * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
4072 * the #GtkIconInfo. If this flag has been specified, the pixbuf
4073 * returned by this function will be scaled to the exact size.
4074 *
4075 * Returns: (transfer full): the rendered icon; this may be a newly
4076 * created icon or a new reference to an internal icon, so you must
4077 * not modify the icon. Use g_object_unref() to release your reference
4078 * to the icon.
4079 *
4080 * Since: 2.4
4081 */
4082 GdkPixbuf *
gtk_icon_info_load_icon(GtkIconInfo * icon_info,GError ** error)4083 gtk_icon_info_load_icon (GtkIconInfo *icon_info,
4084 GError **error)
4085 {
4086 g_return_val_if_fail (icon_info != NULL, NULL);
4087 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4088
4089 if (!icon_info_ensure_scale_and_pixbuf (icon_info))
4090 {
4091 if (icon_info->load_error)
4092 {
4093 if (error)
4094 *error = g_error_copy (icon_info->load_error);
4095 }
4096 else
4097 {
4098 g_set_error_literal (error,
4099 GTK_ICON_THEME_ERROR,
4100 GTK_ICON_THEME_NOT_FOUND,
4101 _("Failed to load icon"));
4102 }
4103
4104 return NULL;
4105 }
4106
4107 /* Instead of returning the pixbuf directly we return a proxy
4108 * to it that we don't own (but that shares the data with the
4109 * one we own). This way we can know when it is freed and ensure
4110 * the IconInfo is alive (and thus cached) while the pixbuf is
4111 * still alive.
4112 */
4113 if (icon_info->proxy_pixbuf != NULL)
4114 return g_object_ref (icon_info->proxy_pixbuf);
4115
4116 icon_info->proxy_pixbuf =
4117 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (icon_info->pixbuf),
4118 gdk_pixbuf_get_colorspace (icon_info->pixbuf),
4119 gdk_pixbuf_get_has_alpha (icon_info->pixbuf),
4120 gdk_pixbuf_get_bits_per_sample (icon_info->pixbuf),
4121 gdk_pixbuf_get_width (icon_info->pixbuf),
4122 gdk_pixbuf_get_height (icon_info->pixbuf),
4123 gdk_pixbuf_get_rowstride (icon_info->pixbuf),
4124 proxy_pixbuf_destroy,
4125 g_object_ref (icon_info));
4126
4127 return icon_info->proxy_pixbuf;
4128 }
4129
4130 /**
4131 * gtk_icon_info_load_surface:
4132 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
4133 * @for_window: (allow-none): #GdkWindow to optimize drawing for, or %NULL
4134 * @error: (allow-none): location for error information on failure, or %NULL
4135 *
4136 * Renders an icon previously looked up in an icon theme using
4137 * gtk_icon_theme_lookup_icon(); the size will be based on the size
4138 * passed to gtk_icon_theme_lookup_icon(). Note that the resulting
4139 * surface may not be exactly this size; an icon theme may have icons
4140 * that differ slightly from their nominal sizes, and in addition GTK+
4141 * will avoid scaling icons that it considers sufficiently close to the
4142 * requested size or for which the source image would have to be scaled
4143 * up too far. (This maintains sharpness.). This behaviour can be changed
4144 * by passing the %GTK_ICON_LOOKUP_FORCE_SIZE flag when obtaining
4145 * the #GtkIconInfo. If this flag has been specified, the pixbuf
4146 * returned by this function will be scaled to the exact size.
4147 *
4148 * Returns: (transfer full): the rendered icon; this may be a newly
4149 * created icon or a new reference to an internal icon, so you must
4150 * not modify the icon. Use cairo_surface_destroy() to release your
4151 * reference to the icon.
4152 *
4153 * Since: 3.10
4154 */
4155 cairo_surface_t *
gtk_icon_info_load_surface(GtkIconInfo * icon_info,GdkWindow * for_window,GError ** error)4156 gtk_icon_info_load_surface (GtkIconInfo *icon_info,
4157 GdkWindow *for_window,
4158 GError **error)
4159 {
4160 GdkPixbuf *pixbuf;
4161 cairo_surface_t *surface;
4162
4163 g_return_val_if_fail (icon_info != NULL, NULL);
4164 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4165
4166 pixbuf = gtk_icon_info_load_icon (icon_info, error);
4167
4168 if (pixbuf == NULL)
4169 return NULL;
4170
4171 surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, icon_info->desired_scale, for_window);
4172 g_object_unref (pixbuf);
4173
4174 return surface;
4175 }
4176
4177 static void
load_icon_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)4178 load_icon_thread (GTask *task,
4179 gpointer source_object,
4180 gpointer task_data,
4181 GCancellable *cancellable)
4182 {
4183 GtkIconInfo *dup = task_data;
4184
4185 (void)icon_info_ensure_scale_and_pixbuf (dup);
4186 g_task_return_pointer (task, NULL, NULL);
4187 }
4188
4189 /**
4190 * gtk_icon_info_load_icon_async:
4191 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
4192 * @cancellable: (allow-none): optional #GCancellable object, %NULL to ignore
4193 * @callback: (scope async): a #GAsyncReadyCallback to call when the
4194 * request is satisfied
4195 * @user_data: (closure): the data to pass to callback function
4196 *
4197 * Asynchronously load, render and scale an icon previously looked up
4198 * from the icon theme using gtk_icon_theme_lookup_icon().
4199 *
4200 * For more details, see gtk_icon_info_load_icon() which is the synchronous
4201 * version of this call.
4202 *
4203 * Since: 3.8
4204 */
4205 void
gtk_icon_info_load_icon_async(GtkIconInfo * icon_info,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4206 gtk_icon_info_load_icon_async (GtkIconInfo *icon_info,
4207 GCancellable *cancellable,
4208 GAsyncReadyCallback callback,
4209 gpointer user_data)
4210 {
4211 GTask *task;
4212 GdkPixbuf *pixbuf;
4213 GtkIconInfo *dup;
4214 GError *error = NULL;
4215
4216 task = g_task_new (icon_info, cancellable, callback, user_data);
4217
4218 if (icon_info_get_pixbuf_ready (icon_info))
4219 {
4220 pixbuf = gtk_icon_info_load_icon (icon_info, &error);
4221 if (pixbuf == NULL)
4222 g_task_return_error (task, error);
4223 else
4224 g_task_return_pointer (task, pixbuf, g_object_unref);
4225 g_object_unref (task);
4226 }
4227 else
4228 {
4229 dup = icon_info_dup (icon_info);
4230 g_task_set_task_data (task, dup, g_object_unref);
4231 g_task_run_in_thread (task, load_icon_thread);
4232 g_object_unref (task);
4233 }
4234 }
4235
4236 /**
4237 * gtk_icon_info_load_icon_finish:
4238 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
4239 * @res: a #GAsyncResult
4240 * @error: (allow-none): location to store error information on failure,
4241 * or %NULL.
4242 *
4243 * Finishes an async icon load, see gtk_icon_info_load_icon_async().
4244 *
4245 * Returns: (transfer full): the rendered icon; this may be a newly
4246 * created icon or a new reference to an internal icon, so you must
4247 * not modify the icon. Use g_object_unref() to release your reference
4248 * to the icon.
4249 *
4250 * Since: 3.8
4251 */
4252 GdkPixbuf *
gtk_icon_info_load_icon_finish(GtkIconInfo * icon_info,GAsyncResult * result,GError ** error)4253 gtk_icon_info_load_icon_finish (GtkIconInfo *icon_info,
4254 GAsyncResult *result,
4255 GError **error)
4256 {
4257 GTask *task = G_TASK (result);
4258 GtkIconInfo *dup;
4259
4260 g_return_val_if_fail (g_task_is_valid (result, icon_info), NULL);
4261
4262 dup = g_task_get_task_data (task);
4263 if (dup == NULL || g_task_had_error (task))
4264 return g_task_propagate_pointer (task, error);
4265
4266 /* We ran the thread and it was not cancelled */
4267
4268 /* Check if someone else updated the icon_info in between */
4269 if (!icon_info_get_pixbuf_ready (icon_info))
4270 {
4271 /* If not, copy results from dup back to icon_info */
4272 icon_info->emblems_applied = dup->emblems_applied;
4273 icon_info->scale = dup->scale;
4274 g_clear_object (&icon_info->pixbuf);
4275 if (dup->pixbuf)
4276 icon_info->pixbuf = g_object_ref (dup->pixbuf);
4277 g_clear_error (&icon_info->load_error);
4278 if (dup->load_error)
4279 icon_info->load_error = g_error_copy (dup->load_error);
4280 }
4281
4282 g_assert (icon_info_get_pixbuf_ready (icon_info));
4283
4284 /* This is now guaranteed to not block */
4285 return gtk_icon_info_load_icon (icon_info, error);
4286 }
4287
4288 static void
proxy_symbolic_pixbuf_destroy(guchar * pixels,gpointer data)4289 proxy_symbolic_pixbuf_destroy (guchar *pixels,
4290 gpointer data)
4291 {
4292 GtkIconInfo *icon_info = data;
4293 GtkIconTheme *icon_theme = icon_info->in_cache;
4294 SymbolicPixbufCache *symbolic_cache;
4295
4296 for (symbolic_cache = icon_info->symbolic_pixbuf_cache;
4297 symbolic_cache != NULL;
4298 symbolic_cache = symbolic_cache->next)
4299 {
4300 if (symbolic_cache->proxy_pixbuf != NULL &&
4301 gdk_pixbuf_get_pixels (symbolic_cache->proxy_pixbuf) == pixels)
4302 break;
4303 }
4304
4305 g_assert (symbolic_cache != NULL);
4306 g_assert (symbolic_cache->proxy_pixbuf != NULL);
4307
4308 symbolic_cache->proxy_pixbuf = NULL;
4309
4310 /* Keep it alive a bit longer */
4311 if (icon_theme != NULL)
4312 ensure_in_lru_cache (icon_theme, icon_info);
4313
4314 g_object_unref (icon_info);
4315 }
4316
4317 static GdkPixbuf *
symbolic_cache_get_proxy(SymbolicPixbufCache * symbolic_cache,GtkIconInfo * icon_info)4318 symbolic_cache_get_proxy (SymbolicPixbufCache *symbolic_cache,
4319 GtkIconInfo *icon_info)
4320 {
4321 if (symbolic_cache->proxy_pixbuf)
4322 return g_object_ref (symbolic_cache->proxy_pixbuf);
4323
4324 symbolic_cache->proxy_pixbuf =
4325 gdk_pixbuf_new_from_data (gdk_pixbuf_get_pixels (symbolic_cache->pixbuf),
4326 gdk_pixbuf_get_colorspace (symbolic_cache->pixbuf),
4327 gdk_pixbuf_get_has_alpha (symbolic_cache->pixbuf),
4328 gdk_pixbuf_get_bits_per_sample (symbolic_cache->pixbuf),
4329 gdk_pixbuf_get_width (symbolic_cache->pixbuf),
4330 gdk_pixbuf_get_height (symbolic_cache->pixbuf),
4331 gdk_pixbuf_get_rowstride (symbolic_cache->pixbuf),
4332 proxy_symbolic_pixbuf_destroy,
4333 g_object_ref (icon_info));
4334
4335 return symbolic_cache->proxy_pixbuf;
4336 }
4337
4338 static gchar *
rgba_to_string_noalpha(const GdkRGBA * rgba)4339 rgba_to_string_noalpha (const GdkRGBA *rgba)
4340 {
4341 GdkRGBA color;
4342
4343 color = *rgba;
4344 color.alpha = 1.0;
4345
4346 return gdk_rgba_to_string (&color);
4347 }
4348
4349 static void
rgba_to_pixel(const GdkRGBA * rgba,guint8 pixel[4])4350 rgba_to_pixel(const GdkRGBA *rgba,
4351 guint8 pixel[4])
4352 {
4353 pixel[0] = rgba->red * 255;
4354 pixel[1] = rgba->green * 255;
4355 pixel[2] = rgba->blue * 255;
4356 pixel[3] = 255;
4357 }
4358
4359 GdkPixbuf *
gtk_icon_theme_color_symbolic_pixbuf(GdkPixbuf * symbolic,const GdkRGBA * fg_color,const GdkRGBA * success_color,const GdkRGBA * warning_color,const GdkRGBA * error_color)4360 gtk_icon_theme_color_symbolic_pixbuf (GdkPixbuf *symbolic,
4361 const GdkRGBA *fg_color,
4362 const GdkRGBA *success_color,
4363 const GdkRGBA *warning_color,
4364 const GdkRGBA *error_color)
4365 {
4366 int width, height, x, y, src_stride, dst_stride;
4367 guchar *src_data, *dst_data;
4368 guchar *src_row, *dst_row;
4369 int alpha;
4370 GdkPixbuf *colored;
4371 guint8 fg_pixel[4], success_pixel[4], warning_pixel[4], error_pixel[4];
4372
4373 alpha = fg_color->alpha * 255;
4374
4375 rgba_to_pixel (fg_color, fg_pixel);
4376 rgba_to_pixel (success_color, success_pixel);
4377 rgba_to_pixel (warning_color, warning_pixel);
4378 rgba_to_pixel (error_color, error_pixel);
4379
4380 width = gdk_pixbuf_get_width (symbolic);
4381 height = gdk_pixbuf_get_height (symbolic);
4382
4383 colored = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
4384
4385 src_stride = gdk_pixbuf_get_rowstride (symbolic);
4386 src_data = gdk_pixbuf_get_pixels (symbolic);
4387
4388 dst_data = gdk_pixbuf_get_pixels (colored);
4389 dst_stride = gdk_pixbuf_get_rowstride (colored);
4390
4391 for (y = 0; y < height; y++)
4392 {
4393 src_row = src_data + src_stride * y;
4394 dst_row = dst_data + dst_stride * y;
4395 for (x = 0; x < width; x++)
4396 {
4397 guint r, g, b, a;
4398 int c1, c2, c3, c4;
4399
4400 a = src_row[3];
4401 dst_row[3] = a * alpha / 255;
4402
4403 if (a == 0)
4404 {
4405 dst_row[0] = 0;
4406 dst_row[1] = 0;
4407 dst_row[2] = 0;
4408 }
4409 else
4410 {
4411 c2 = src_row[0];
4412 c3 = src_row[1];
4413 c4 = src_row[2];
4414
4415 if (c2 == 0 && c3 == 0 && c4 == 0)
4416 {
4417 dst_row[0] = fg_pixel[0];
4418 dst_row[1] = fg_pixel[1];
4419 dst_row[2] = fg_pixel[2];
4420 }
4421 else
4422 {
4423 c1 = 255 - c2 - c3 - c4;
4424
4425 r = fg_pixel[0] * c1 + success_pixel[0] * c2 + warning_pixel[0] * c3 + error_pixel[0] * c4;
4426 g = fg_pixel[1] * c1 + success_pixel[1] * c2 + warning_pixel[1] * c3 + error_pixel[1] * c4;
4427 b = fg_pixel[2] * c1 + success_pixel[2] * c2 + warning_pixel[2] * c3 + error_pixel[2] * c4;
4428
4429 dst_row[0] = r / 255;
4430 dst_row[1] = g / 255;
4431 dst_row[2] = b / 255;
4432 }
4433 }
4434
4435 src_row += 4;
4436 dst_row += 4;
4437 }
4438 }
4439
4440 return colored;
4441 }
4442
4443 static GdkPixbuf *
gtk_icon_info_load_symbolic_png(GtkIconInfo * icon_info,const GdkRGBA * fg,const GdkRGBA * success_color,const GdkRGBA * warning_color,const GdkRGBA * error_color,GError ** error)4444 gtk_icon_info_load_symbolic_png (GtkIconInfo *icon_info,
4445 const GdkRGBA *fg,
4446 const GdkRGBA *success_color,
4447 const GdkRGBA *warning_color,
4448 const GdkRGBA *error_color,
4449 GError **error)
4450 {
4451 GdkRGBA fg_default = { 0.7450980392156863, 0.7450980392156863, 0.7450980392156863, 1.0};
4452 GdkRGBA success_default = { 0.3046921492332342,0.6015716792553597, 0.023437857633325704, 1.0};
4453 GdkRGBA warning_default = {0.9570458533607996, 0.47266346227206835, 0.2421911955443656, 1.0 };
4454 GdkRGBA error_default = { 0.796887159533074, 0 ,0, 1.0 };
4455
4456 if (!icon_info_ensure_scale_and_pixbuf (icon_info))
4457 {
4458 if (icon_info->load_error)
4459 {
4460 if (error)
4461 *error = g_error_copy (icon_info->load_error);
4462 }
4463 else
4464 {
4465 g_set_error_literal (error,
4466 GTK_ICON_THEME_ERROR,
4467 GTK_ICON_THEME_NOT_FOUND,
4468 _("Failed to load icon"));
4469 }
4470
4471 return NULL;
4472 }
4473
4474 return gtk_icon_theme_color_symbolic_pixbuf (icon_info->pixbuf,
4475 fg ? fg : &fg_default,
4476 success_color ? success_color : &success_default,
4477 warning_color ? warning_color : &warning_default,
4478 error_color ? error_color : &error_default);
4479 }
4480
4481 static GdkPixbuf *
gtk_icon_info_load_symbolic_svg(GtkIconInfo * icon_info,const GdkRGBA * fg,const GdkRGBA * success_color,const GdkRGBA * warning_color,const GdkRGBA * error_color,GError ** error)4482 gtk_icon_info_load_symbolic_svg (GtkIconInfo *icon_info,
4483 const GdkRGBA *fg,
4484 const GdkRGBA *success_color,
4485 const GdkRGBA *warning_color,
4486 const GdkRGBA *error_color,
4487 GError **error)
4488 {
4489 GInputStream *stream;
4490 GdkPixbuf *pixbuf;
4491 gchar *css_fg;
4492 gchar *css_success;
4493 gchar *css_warning;
4494 gchar *css_error;
4495 gchar *data;
4496 gchar *width;
4497 gchar *height;
4498 gchar *file_data, *escaped_file_data;
4499 gsize file_len;
4500 gint symbolic_size;
4501 double alpha;
4502 gchar alphastr[G_ASCII_DTOSTR_BUF_SIZE];
4503
4504 alpha = fg->alpha;
4505
4506 css_fg = rgba_to_string_noalpha (fg);
4507
4508 css_success = css_warning = css_error = NULL;
4509
4510 if (warning_color)
4511 css_warning = rgba_to_string_noalpha (warning_color);
4512 else
4513 css_warning = g_strdup ("rgb(245,121,62)");
4514
4515 if (error_color)
4516 css_error = rgba_to_string_noalpha (error_color);
4517 else
4518 css_error = g_strdup ("rgb(204,0,0)");
4519
4520 if (success_color)
4521 css_success = rgba_to_string_noalpha (success_color);
4522 else
4523 css_success = g_strdup ("rgb(78,154,6)");
4524
4525 if (!g_file_load_contents (icon_info->icon_file, NULL, &file_data, &file_len, NULL, error))
4526 return NULL;
4527
4528 if (!icon_info_ensure_scale_and_pixbuf (icon_info))
4529 {
4530 g_propagate_error (error, icon_info->load_error);
4531 icon_info->load_error = NULL;
4532 g_free (css_fg);
4533 g_free (css_warning);
4534 g_free (css_error);
4535 g_free (css_success);
4536 g_free (file_data);
4537 return NULL;
4538 }
4539
4540 if (icon_info->symbolic_width == 0 ||
4541 icon_info->symbolic_height == 0)
4542 {
4543 /* Fetch size from the original icon */
4544 stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
4545 pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
4546 g_object_unref (stream);
4547
4548 if (!pixbuf)
4549 {
4550 g_free (css_fg);
4551 g_free (css_warning);
4552 g_free (css_error);
4553 g_free (css_success);
4554 g_free (file_data);
4555 return NULL;
4556 }
4557
4558 icon_info->symbolic_width = gdk_pixbuf_get_width (pixbuf);
4559 icon_info->symbolic_height = gdk_pixbuf_get_height (pixbuf);
4560 g_object_unref (pixbuf);
4561 }
4562
4563 symbolic_size = MAX (icon_info->symbolic_width, icon_info->symbolic_height);
4564
4565 GTK_NOTE (ICONTHEME,
4566 if (icon_info->dir_type == ICON_THEME_DIR_UNTHEMED)
4567 g_message ("Symbolic icon %s is not in an icon theme directory",
4568 icon_info->key.icon_names ? icon_info->key.icon_names[0] : icon_info->filename);
4569 else if (icon_info->dir_size * icon_info->dir_scale != symbolic_size)
4570 g_message ("Symbolic icon %s of size %d is in an icon theme directory of size %d",
4571 icon_info->key.icon_names ? icon_info->key.icon_names[0] : icon_info->filename,
4572 symbolic_size,
4573 icon_info->dir_size * icon_info->dir_scale)
4574 );
4575
4576 width = g_strdup_printf ("%d", icon_info->symbolic_width);
4577 height = g_strdup_printf ("%d", icon_info->symbolic_height);
4578
4579 escaped_file_data = g_base64_encode ((guchar *) file_data, file_len);
4580 g_free (file_data);
4581
4582 g_ascii_dtostr (alphastr, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (alpha, 0, 1));
4583
4584 data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
4585 "<svg version=\"1.1\"\n"
4586 " xmlns=\"http://www.w3.org/2000/svg\"\n"
4587 " xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
4588 " width=\"", width, "\"\n"
4589 " height=\"", height, "\">\n"
4590 " <style type=\"text/css\">\n"
4591 " rect,path,ellipse,circle,polygon {\n"
4592 " fill: ", css_fg," !important;\n"
4593 " }\n"
4594 " .warning {\n"
4595 " fill: ", css_warning, " !important;\n"
4596 " }\n"
4597 " .error {\n"
4598 " fill: ", css_error ," !important;\n"
4599 " }\n"
4600 " .success {\n"
4601 " fill: ", css_success, " !important;\n"
4602 " }\n"
4603 " </style>\n"
4604 " <g opacity=\"", alphastr, "\" ><xi:include href=\"data:text/xml;base64,", escaped_file_data, "\"/></g>\n"
4605 "</svg>",
4606 NULL);
4607 g_free (escaped_file_data);
4608 g_free (css_fg);
4609 g_free (css_warning);
4610 g_free (css_error);
4611 g_free (css_success);
4612 g_free (width);
4613 g_free (height);
4614
4615 stream = g_memory_input_stream_new_from_data (data, -1, g_free);
4616 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
4617 gdk_pixbuf_get_width (icon_info->pixbuf),
4618 gdk_pixbuf_get_height (icon_info->pixbuf),
4619 TRUE,
4620 NULL,
4621 error);
4622 g_object_unref (stream);
4623
4624 return pixbuf;
4625 }
4626
4627
4628 static GdkPixbuf *
gtk_icon_info_load_symbolic_internal(GtkIconInfo * icon_info,const GdkRGBA * fg,const GdkRGBA * success_color,const GdkRGBA * warning_color,const GdkRGBA * error_color,gboolean use_cache,GError ** error)4629 gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info,
4630 const GdkRGBA *fg,
4631 const GdkRGBA *success_color,
4632 const GdkRGBA *warning_color,
4633 const GdkRGBA *error_color,
4634 gboolean use_cache,
4635 GError **error)
4636 {
4637 GdkPixbuf *pixbuf;
4638 SymbolicPixbufCache *symbolic_cache;
4639 char *icon_uri;
4640
4641 if (use_cache)
4642 {
4643 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4644 fg, success_color, warning_color, error_color);
4645 if (symbolic_cache)
4646 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
4647 }
4648
4649 /* css_fg can't possibly have failed, otherwise
4650 * that would mean we have a broken style
4651 */
4652 g_return_val_if_fail (fg != NULL, NULL);
4653
4654 icon_uri = g_file_get_uri (icon_info->icon_file);
4655 if (g_str_has_suffix (icon_uri, ".symbolic.png"))
4656 pixbuf = gtk_icon_info_load_symbolic_png (icon_info, fg, success_color, warning_color, error_color, error);
4657 else
4658 pixbuf = gtk_icon_info_load_symbolic_svg (icon_info, fg, success_color, warning_color, error_color, error);
4659
4660 g_free (icon_uri);
4661
4662 if (pixbuf != NULL)
4663 {
4664 GdkPixbuf *icon;
4665
4666 icon = apply_emblems_to_pixbuf (pixbuf, icon_info);
4667 if (icon != NULL)
4668 {
4669 g_object_unref (pixbuf);
4670 pixbuf = icon;
4671 }
4672
4673 if (use_cache)
4674 {
4675 icon_info->symbolic_pixbuf_cache =
4676 symbolic_pixbuf_cache_new (pixbuf, fg, success_color, warning_color, error_color,
4677 icon_info->symbolic_pixbuf_cache);
4678 g_object_unref (pixbuf);
4679 return symbolic_cache_get_proxy (icon_info->symbolic_pixbuf_cache, icon_info);
4680 }
4681 else
4682 return pixbuf;
4683 }
4684
4685 return NULL;
4686 }
4687
4688 /**
4689 * gtk_icon_info_load_symbolic:
4690 * @icon_info: a #GtkIconInfo
4691 * @fg: a #GdkRGBA representing the foreground color of the icon
4692 * @success_color: (allow-none): a #GdkRGBA representing the warning color
4693 * of the icon or %NULL to use the default color
4694 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
4695 * of the icon or %NULL to use the default color
4696 * @error_color: (allow-none): a #GdkRGBA representing the error color
4697 * of the icon or %NULL to use the default color (allow-none)
4698 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4699 * loaded icon was a symbolic one and whether the @fg color was
4700 * applied to it.
4701 * @error: (allow-none): location to store error information on failure,
4702 * or %NULL.
4703 *
4704 * Loads an icon, modifying it to match the system colours for the foreground,
4705 * success, warning and error colors provided. If the icon is not a symbolic
4706 * one, the function will return the result from gtk_icon_info_load_icon().
4707 *
4708 * This allows loading symbolic icons that will match the system theme.
4709 *
4710 * Unless you are implementing a widget, you will want to use
4711 * g_themed_icon_new_with_default_fallbacks() to load the icon.
4712 *
4713 * As implementation details, the icon loaded needs to be of SVG type,
4714 * contain the “symbolic” term as the last component of the icon name,
4715 * and use the “fg”, “success”, “warning” and “error” CSS styles in the
4716 * SVG file itself.
4717 *
4718 * See the [Symbolic Icons Specification](http://www.freedesktop.org/wiki/SymbolicIcons)
4719 * for more information about symbolic icons.
4720 *
4721 * Returns: (transfer full): a #GdkPixbuf representing the loaded icon
4722 *
4723 * Since: 3.0
4724 */
4725 GdkPixbuf *
gtk_icon_info_load_symbolic(GtkIconInfo * icon_info,const GdkRGBA * fg,const GdkRGBA * success_color,const GdkRGBA * warning_color,const GdkRGBA * error_color,gboolean * was_symbolic,GError ** error)4726 gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
4727 const GdkRGBA *fg,
4728 const GdkRGBA *success_color,
4729 const GdkRGBA *warning_color,
4730 const GdkRGBA *error_color,
4731 gboolean *was_symbolic,
4732 GError **error)
4733 {
4734 gboolean is_symbolic;
4735
4736 g_return_val_if_fail (icon_info != NULL, NULL);
4737 g_return_val_if_fail (fg != NULL, NULL);
4738
4739 is_symbolic = gtk_icon_info_is_symbolic (icon_info);
4740
4741 if (was_symbolic)
4742 *was_symbolic = is_symbolic;
4743
4744 if (!is_symbolic)
4745 return gtk_icon_info_load_icon (icon_info, error);
4746
4747 return gtk_icon_info_load_symbolic_internal (icon_info,
4748 fg, success_color,
4749 warning_color, error_color,
4750 TRUE,
4751 error);
4752 }
4753
4754 void
gtk_icon_theme_lookup_symbolic_colors(GtkCssStyle * style,GdkRGBA * color_out,GdkRGBA * success_out,GdkRGBA * warning_out,GdkRGBA * error_out)4755 gtk_icon_theme_lookup_symbolic_colors (GtkCssStyle *style,
4756 GdkRGBA *color_out,
4757 GdkRGBA *success_out,
4758 GdkRGBA *warning_out,
4759 GdkRGBA *error_out)
4760 {
4761 GtkCssValue *palette, *color;
4762 const GdkRGBA *lookup;
4763
4764 color = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_COLOR);
4765 palette = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_PALETTE);
4766 *color_out = *_gtk_css_rgba_value_get_rgba (color);
4767
4768 lookup = gtk_css_palette_value_get_color (palette, "success");
4769 if (lookup)
4770 *success_out = *lookup;
4771 else
4772 *success_out = *color_out;
4773
4774 lookup = gtk_css_palette_value_get_color (palette, "warning");
4775 if (lookup)
4776 *warning_out = *lookup;
4777 else
4778 *warning_out = *color_out;
4779
4780 lookup = gtk_css_palette_value_get_color (palette, "error");
4781 if (lookup)
4782 *error_out = *lookup;
4783 else
4784 *error_out = *color_out;
4785 }
4786
4787 /**
4788 * gtk_icon_info_load_symbolic_for_context:
4789 * @icon_info: a #GtkIconInfo
4790 * @context: a #GtkStyleContext
4791 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
4792 * loaded icon was a symbolic one and whether the @fg color was
4793 * applied to it.
4794 * @error: (allow-none): location to store error information on failure,
4795 * or %NULL.
4796 *
4797 * Loads an icon, modifying it to match the system colors for the foreground,
4798 * success, warning and error colors provided. If the icon is not a symbolic
4799 * one, the function will return the result from gtk_icon_info_load_icon().
4800 * This function uses the regular foreground color and the symbolic colors
4801 * with the names “success_color”, “warning_color” and “error_color” from
4802 * the context.
4803 *
4804 * This allows loading symbolic icons that will match the system theme.
4805 *
4806 * See gtk_icon_info_load_symbolic() for more details.
4807 *
4808 * Returns: (transfer full): a #GdkPixbuf representing the loaded icon
4809 *
4810 * Since: 3.0
4811 */
4812 GdkPixbuf *
gtk_icon_info_load_symbolic_for_context(GtkIconInfo * icon_info,GtkStyleContext * context,gboolean * was_symbolic,GError ** error)4813 gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
4814 GtkStyleContext *context,
4815 gboolean *was_symbolic,
4816 GError **error)
4817 {
4818 GdkRGBA fg;
4819 GdkRGBA success_color;
4820 GdkRGBA warning_color;
4821 GdkRGBA error_color;
4822 gboolean is_symbolic;
4823
4824 g_return_val_if_fail (icon_info != NULL, NULL);
4825 g_return_val_if_fail (context != NULL, NULL);
4826
4827 is_symbolic = gtk_icon_info_is_symbolic (icon_info);
4828
4829 if (was_symbolic)
4830 *was_symbolic = is_symbolic;
4831
4832 if (!is_symbolic)
4833 return gtk_icon_info_load_icon (icon_info, error);
4834
4835 gtk_icon_theme_lookup_symbolic_colors (gtk_style_context_lookup_style (context),
4836 &fg, &success_color,
4837 &warning_color, &error_color);
4838
4839 return gtk_icon_info_load_symbolic_internal (icon_info,
4840 &fg, &success_color,
4841 &warning_color, &error_color,
4842 TRUE,
4843 error);
4844 }
4845
4846 typedef struct {
4847 gboolean is_symbolic;
4848 GtkIconInfo *dup;
4849 GdkRGBA fg;
4850 gboolean fg_set;
4851 GdkRGBA success_color;
4852 gboolean success_color_set;
4853 GdkRGBA warning_color;
4854 gboolean warning_color_set;
4855 GdkRGBA error_color;
4856 gboolean error_color_set;
4857 } AsyncSymbolicData;
4858
4859 static void
async_symbolic_data_free(AsyncSymbolicData * data)4860 async_symbolic_data_free (AsyncSymbolicData *data)
4861 {
4862 if (data->dup)
4863 g_object_unref (data->dup);
4864 g_slice_free (AsyncSymbolicData, data);
4865 }
4866
4867 static void
async_load_no_symbolic_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)4868 async_load_no_symbolic_cb (GObject *source_object,
4869 GAsyncResult *res,
4870 gpointer user_data)
4871 {
4872 GtkIconInfo *icon_info = GTK_ICON_INFO (source_object);
4873 GTask *task = user_data;
4874 GError *error = NULL;
4875 GdkPixbuf *pixbuf;
4876
4877 pixbuf = gtk_icon_info_load_icon_finish (icon_info, res, &error);
4878 if (pixbuf == NULL)
4879 g_task_return_error (task, error);
4880 else
4881 g_task_return_pointer (task, pixbuf, g_object_unref);
4882 g_object_unref (task);
4883 }
4884
4885 static void
load_symbolic_icon_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)4886 load_symbolic_icon_thread (GTask *task,
4887 gpointer source_object,
4888 gpointer task_data,
4889 GCancellable *cancellable)
4890 {
4891 AsyncSymbolicData *data = task_data;
4892 GError *error;
4893 GdkPixbuf *pixbuf;
4894
4895 error = NULL;
4896 pixbuf = gtk_icon_info_load_symbolic_internal (data->dup,
4897 data->fg_set ? &data->fg : NULL,
4898 data->success_color_set ? &data->success_color : NULL,
4899 data->warning_color_set ? &data->warning_color : NULL,
4900 data->error_color_set ? &data->error_color : NULL,
4901 FALSE,
4902 &error);
4903 if (pixbuf == NULL)
4904 g_task_return_error (task, error);
4905 else
4906 g_task_return_pointer (task, pixbuf, g_object_unref);
4907 }
4908
4909 /**
4910 * gtk_icon_info_load_symbolic_async:
4911 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
4912 * @fg: a #GdkRGBA representing the foreground color of the icon
4913 * @success_color: (allow-none): a #GdkRGBA representing the warning color
4914 * of the icon or %NULL to use the default color
4915 * @warning_color: (allow-none): a #GdkRGBA representing the warning color
4916 * of the icon or %NULL to use the default color
4917 * @error_color: (allow-none): a #GdkRGBA representing the error color
4918 * of the icon or %NULL to use the default color (allow-none)
4919 * @cancellable: (allow-none): optional #GCancellable object,
4920 * %NULL to ignore
4921 * @callback: (scope async): a #GAsyncReadyCallback to call when the
4922 * request is satisfied
4923 * @user_data: (closure): the data to pass to callback function
4924 *
4925 * Asynchronously load, render and scale a symbolic icon previously looked up
4926 * from the icon theme using gtk_icon_theme_lookup_icon().
4927 *
4928 * For more details, see gtk_icon_info_load_symbolic() which is the synchronous
4929 * version of this call.
4930 *
4931 * Since: 3.8
4932 */
4933 void
gtk_icon_info_load_symbolic_async(GtkIconInfo * icon_info,const GdkRGBA * fg,const GdkRGBA * success_color,const GdkRGBA * warning_color,const GdkRGBA * error_color,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)4934 gtk_icon_info_load_symbolic_async (GtkIconInfo *icon_info,
4935 const GdkRGBA *fg,
4936 const GdkRGBA *success_color,
4937 const GdkRGBA *warning_color,
4938 const GdkRGBA *error_color,
4939 GCancellable *cancellable,
4940 GAsyncReadyCallback callback,
4941 gpointer user_data)
4942 {
4943 GTask *task;
4944 AsyncSymbolicData *data;
4945 SymbolicPixbufCache *symbolic_cache;
4946 GdkPixbuf *pixbuf;
4947
4948 g_return_if_fail (icon_info != NULL);
4949 g_return_if_fail (fg != NULL);
4950
4951 task = g_task_new (icon_info, cancellable, callback, user_data);
4952
4953 data = g_slice_new0 (AsyncSymbolicData);
4954 g_task_set_task_data (task, data, (GDestroyNotify) async_symbolic_data_free);
4955
4956 data->is_symbolic = gtk_icon_info_is_symbolic (icon_info);
4957
4958 if (!data->is_symbolic)
4959 {
4960 gtk_icon_info_load_icon_async (icon_info, cancellable, async_load_no_symbolic_cb, g_object_ref (task));
4961 }
4962 else
4963 {
4964 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
4965 fg, success_color, warning_color, error_color);
4966 if (symbolic_cache)
4967 {
4968 pixbuf = symbolic_cache_get_proxy (symbolic_cache, icon_info);
4969 g_task_return_pointer (task, pixbuf, g_object_unref);
4970 }
4971 else
4972 {
4973 if (fg)
4974 {
4975 data->fg = *fg;
4976 data->fg_set = TRUE;
4977 }
4978
4979 if (success_color)
4980 {
4981 data->success_color = *success_color;
4982 data->success_color_set = TRUE;
4983 }
4984
4985 if (warning_color)
4986 {
4987 data->warning_color = *warning_color;
4988 data->warning_color_set = TRUE;
4989 }
4990
4991 if (error_color)
4992 {
4993 data->error_color = *error_color;
4994 data->error_color_set = TRUE;
4995 }
4996
4997 data->dup = icon_info_dup (icon_info);
4998 g_task_run_in_thread (task, load_symbolic_icon_thread);
4999 }
5000 }
5001 g_object_unref (task);
5002 }
5003
5004 /**
5005 * gtk_icon_info_load_symbolic_finish:
5006 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
5007 * @res: a #GAsyncResult
5008 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
5009 * loaded icon was a symbolic one and whether the @fg color was
5010 * applied to it.
5011 * @error: (allow-none): location to store error information on failure,
5012 * or %NULL.
5013 *
5014 * Finishes an async icon load, see gtk_icon_info_load_symbolic_async().
5015 *
5016 * Returns: (transfer full): the rendered icon; this may be a newly
5017 * created icon or a new reference to an internal icon, so you must
5018 * not modify the icon. Use g_object_unref() to release your reference
5019 * to the icon.
5020 *
5021 * Since: 3.8
5022 */
5023 GdkPixbuf *
gtk_icon_info_load_symbolic_finish(GtkIconInfo * icon_info,GAsyncResult * result,gboolean * was_symbolic,GError ** error)5024 gtk_icon_info_load_symbolic_finish (GtkIconInfo *icon_info,
5025 GAsyncResult *result,
5026 gboolean *was_symbolic,
5027 GError **error)
5028 {
5029 GTask *task = G_TASK (result);
5030 AsyncSymbolicData *data = g_task_get_task_data (task);
5031 SymbolicPixbufCache *symbolic_cache;
5032 GdkPixbuf *pixbuf;
5033
5034 if (was_symbolic)
5035 *was_symbolic = data->is_symbolic;
5036
5037 if (data->dup && !g_task_had_error (task))
5038 {
5039 pixbuf = g_task_propagate_pointer (task, NULL);
5040
5041 g_assert (pixbuf != NULL); /* we checked for !had_error above */
5042
5043 symbolic_cache = symbolic_pixbuf_cache_matches (icon_info->symbolic_pixbuf_cache,
5044 data->fg_set ? &data->fg : NULL,
5045 data->success_color_set ? &data->success_color : NULL,
5046 data->warning_color_set ? &data->warning_color : NULL,
5047 data->error_color_set ? &data->error_color : NULL);
5048
5049 if (symbolic_cache == NULL)
5050 {
5051 symbolic_cache = icon_info->symbolic_pixbuf_cache =
5052 symbolic_pixbuf_cache_new (pixbuf,
5053 data->fg_set ? &data->fg : NULL,
5054 data->success_color_set ? &data->success_color : NULL,
5055 data->warning_color_set ? &data->warning_color : NULL,
5056 data->error_color_set ? &data->error_color : NULL,
5057 icon_info->symbolic_pixbuf_cache);
5058 }
5059
5060 g_object_unref (pixbuf);
5061
5062 return symbolic_cache_get_proxy (symbolic_cache, icon_info);
5063 }
5064
5065 return g_task_propagate_pointer (task, error);
5066 }
5067
5068 /**
5069 * gtk_icon_info_load_symbolic_for_context_async:
5070 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
5071 * @context: a #GtkStyleContext
5072 * @cancellable: (allow-none): optional #GCancellable object,
5073 * %NULL to ignore
5074 * @callback: (scope async): a #GAsyncReadyCallback to call when the
5075 * request is satisfied
5076 * @user_data: (closure): the data to pass to callback function
5077 *
5078 * Asynchronously load, render and scale a symbolic icon previously
5079 * looked up from the icon theme using gtk_icon_theme_lookup_icon().
5080 *
5081 * For more details, see gtk_icon_info_load_symbolic_for_context()
5082 * which is the synchronous version of this call.
5083 *
5084 * Since: 3.8
5085 */
5086 void
gtk_icon_info_load_symbolic_for_context_async(GtkIconInfo * icon_info,GtkStyleContext * context,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)5087 gtk_icon_info_load_symbolic_for_context_async (GtkIconInfo *icon_info,
5088 GtkStyleContext *context,
5089 GCancellable *cancellable,
5090 GAsyncReadyCallback callback,
5091 gpointer user_data)
5092 {
5093 GdkRGBA fg;
5094 GdkRGBA success_color;
5095 GdkRGBA warning_color;
5096 GdkRGBA error_color;
5097
5098 g_return_if_fail (icon_info != NULL);
5099 g_return_if_fail (context != NULL);
5100
5101 gtk_icon_theme_lookup_symbolic_colors (gtk_style_context_lookup_style (context),
5102 &fg, &success_color,
5103 &warning_color, &error_color);
5104
5105 gtk_icon_info_load_symbolic_async (icon_info,
5106 &fg, &success_color,
5107 &warning_color, &error_color,
5108 cancellable, callback, user_data);
5109 }
5110
5111 /**
5112 * gtk_icon_info_load_symbolic_for_context_finish:
5113 * @icon_info: a #GtkIconInfo from gtk_icon_theme_lookup_icon()
5114 * @res: a #GAsyncResult
5115 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
5116 * loaded icon was a symbolic one and whether the @fg color was
5117 * applied to it.
5118 * @error: (allow-none): location to store error information on failure,
5119 * or %NULL.
5120 *
5121 * Finishes an async icon load, see gtk_icon_info_load_symbolic_for_context_async().
5122 *
5123 * Returns: (transfer full): the rendered icon; this may be a newly
5124 * created icon or a new reference to an internal icon, so you must
5125 * not modify the icon. Use g_object_unref() to release your reference
5126 * to the icon.
5127 *
5128 * Since: 3.8
5129 */
5130 GdkPixbuf *
gtk_icon_info_load_symbolic_for_context_finish(GtkIconInfo * icon_info,GAsyncResult * result,gboolean * was_symbolic,GError ** error)5131 gtk_icon_info_load_symbolic_for_context_finish (GtkIconInfo *icon_info,
5132 GAsyncResult *result,
5133 gboolean *was_symbolic,
5134 GError **error)
5135 {
5136 return gtk_icon_info_load_symbolic_finish (icon_info, result, was_symbolic, error);
5137 }
5138
5139 static GdkRGBA *
color_to_rgba(GdkColor * color,GdkRGBA * rgba)5140 color_to_rgba (GdkColor *color,
5141 GdkRGBA *rgba)
5142 {
5143 rgba->red = color->red / 65535.0;
5144 rgba->green = color->green / 65535.0;
5145 rgba->blue = color->blue / 65535.0;
5146 rgba->alpha = 1.0;
5147 return rgba;
5148 }
5149
5150 /**
5151 * gtk_icon_info_load_symbolic_for_style:
5152 * @icon_info: a #GtkIconInfo
5153 * @style: a #GtkStyle to take the colors from
5154 * @state: the widget state to use for colors
5155 * @was_symbolic: (out) (allow-none): a #gboolean, returns whether the
5156 * loaded icon was a symbolic one and whether the @fg color was
5157 * applied to it.
5158 * @error: (allow-none): location to store error information on failure,
5159 * or %NULL.
5160 *
5161 * Loads an icon, modifying it to match the system colours for the foreground,
5162 * success, warning and error colors provided. If the icon is not a symbolic
5163 * one, the function will return the result from gtk_icon_info_load_icon().
5164 *
5165 * This allows loading symbolic icons that will match the system theme.
5166 *
5167 * See gtk_icon_info_load_symbolic() for more details.
5168 *
5169 * Returns: (transfer full): a #GdkPixbuf representing the loaded icon
5170 *
5171 * Since: 3.0
5172 *
5173 * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
5174 */
5175 GdkPixbuf *
gtk_icon_info_load_symbolic_for_style(GtkIconInfo * icon_info,GtkStyle * style,GtkStateType state,gboolean * was_symbolic,GError ** error)5176 gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
5177 GtkStyle *style,
5178 GtkStateType state,
5179 gboolean *was_symbolic,
5180 GError **error)
5181 {
5182 GdkColor color;
5183 GdkRGBA fg;
5184 GdkRGBA success_color;
5185 GdkRGBA *success_colorp;
5186 GdkRGBA warning_color;
5187 GdkRGBA *warning_colorp;
5188 GdkRGBA error_color;
5189 GdkRGBA *error_colorp;
5190 gboolean is_symbolic;
5191
5192 g_return_val_if_fail (icon_info != NULL, NULL);
5193 g_return_val_if_fail (style != NULL, NULL);
5194
5195 is_symbolic = gtk_icon_info_is_symbolic (icon_info);
5196
5197 if (was_symbolic)
5198 *was_symbolic = is_symbolic;
5199
5200 if (!is_symbolic)
5201 return gtk_icon_info_load_icon (icon_info, error);
5202
5203 color_to_rgba (&style->fg[state], &fg);
5204
5205 success_colorp = warning_colorp = error_colorp = NULL;
5206
5207 if (gtk_style_lookup_color (style, "success_color", &color))
5208 success_colorp = color_to_rgba (&color, &success_color);
5209
5210 if (gtk_style_lookup_color (style, "warning_color", &color))
5211 warning_colorp = color_to_rgba (&color, &warning_color);
5212
5213 if (gtk_style_lookup_color (style, "error_color", &color))
5214 error_colorp = color_to_rgba (&color, &error_color);
5215
5216 return gtk_icon_info_load_symbolic_internal (icon_info,
5217 &fg, success_colorp,
5218 warning_colorp, error_colorp,
5219 TRUE,
5220 error);
5221 }
5222
5223 /**
5224 * gtk_icon_info_set_raw_coordinates:
5225 * @icon_info: a #GtkIconInfo
5226 * @raw_coordinates: whether the coordinates of embedded rectangles
5227 * and attached points should be returned in their original
5228 * (unscaled) form.
5229 *
5230 * Sets whether the coordinates returned by gtk_icon_info_get_embedded_rect()
5231 * and gtk_icon_info_get_attach_points() should be returned in their
5232 * original form as specified in the icon theme, instead of scaled
5233 * appropriately for the pixbuf returned by gtk_icon_info_load_icon().
5234 *
5235 * Raw coordinates are somewhat strange; they are specified to be with
5236 * respect to the unscaled pixmap for PNG and XPM icons, but for SVG
5237 * icons, they are in a 1000x1000 coordinate space that is scaled
5238 * to the final size of the icon. You can determine if the icon is an SVG
5239 * icon by using gtk_icon_info_get_filename(), and seeing if it is non-%NULL
5240 * and ends in “.svg”.
5241 *
5242 * This function is provided primarily to allow compatibility wrappers
5243 * for older API's, and is not expected to be useful for applications.
5244 *
5245 * Since: 2.4
5246 *
5247 * Deprecated: 3.14: Embedded rectangles and attachment points are deprecated
5248 */
5249 void
gtk_icon_info_set_raw_coordinates(GtkIconInfo * icon_info,gboolean raw_coordinates)5250 gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
5251 gboolean raw_coordinates)
5252 {
5253 }
5254
5255 /**
5256 * gtk_icon_info_get_embedded_rect:
5257 * @icon_info: a #GtkIconInfo
5258 * @rectangle: (out): #GdkRectangle in which to store embedded
5259 * rectangle coordinates; coordinates are only stored
5260 * when this function returns %TRUE.
5261 *
5262 * This function is deprecated and always returns %FALSE.
5263 *
5264 * Returns: %FALSE
5265 *
5266 * Since: 2.4
5267 *
5268 * Deprecated: 3.14: Embedded rectangles are deprecated
5269 */
5270 gboolean
gtk_icon_info_get_embedded_rect(GtkIconInfo * icon_info,GdkRectangle * rectangle)5271 gtk_icon_info_get_embedded_rect (GtkIconInfo *icon_info,
5272 GdkRectangle *rectangle)
5273 {
5274 return FALSE;
5275 }
5276
5277 /**
5278 * gtk_icon_info_get_attach_points:
5279 * @icon_info: a #GtkIconInfo
5280 * @points: (allow-none) (array length=n_points) (out): location to store pointer
5281 * to an array of points, or %NULL free the array of points with g_free().
5282 * @n_points: (allow-none): location to store the number of points in @points,
5283 * or %NULL
5284 *
5285 * This function is deprecated and always returns %FALSE.
5286 *
5287 * Returns: %FALSE
5288 *
5289 * Since: 2.4
5290 *
5291 * Deprecated: 3.14: Attachment points are deprecated
5292 */
5293 gboolean
gtk_icon_info_get_attach_points(GtkIconInfo * icon_info,GdkPoint ** points,gint * n_points)5294 gtk_icon_info_get_attach_points (GtkIconInfo *icon_info,
5295 GdkPoint **points,
5296 gint *n_points)
5297 {
5298 return FALSE;
5299 }
5300
5301 /**
5302 * gtk_icon_info_get_display_name:
5303 * @icon_info: a #GtkIconInfo
5304 *
5305 * This function is deprecated and always returns %NULL.
5306 *
5307 * Returns: %NULL
5308 *
5309 * Since: 2.4
5310 *
5311 * Deprecated: 3.14: Display names are deprecated
5312 */
5313 const gchar *
gtk_icon_info_get_display_name(GtkIconInfo * icon_info)5314 gtk_icon_info_get_display_name (GtkIconInfo *icon_info)
5315 {
5316 return NULL;
5317 }
5318
5319 /*
5320 * Builtin icons
5321 */
5322
5323
5324 /**
5325 * gtk_icon_theme_add_builtin_icon:
5326 * @icon_name: the name of the icon to register
5327 * @size: the size in pixels at which to register the icon (different
5328 * images can be registered for the same icon name at different sizes.)
5329 * @pixbuf: #GdkPixbuf that contains the image to use for @icon_name
5330 *
5331 * Registers a built-in icon for icon theme lookups. The idea
5332 * of built-in icons is to allow an application or library
5333 * that uses themed icons to function requiring files to
5334 * be present in the file system. For instance, the default
5335 * images for all of GTK+’s stock icons are registered
5336 * as built-icons.
5337 *
5338 * In general, if you use gtk_icon_theme_add_builtin_icon()
5339 * you should also install the icon in the icon theme, so
5340 * that the icon is generally available.
5341 *
5342 * This function will generally be used with pixbufs loaded
5343 * via gdk_pixbuf_new_from_inline().
5344 *
5345 * Since: 2.4
5346 *
5347 * Deprecated: 3.14: Use gtk_icon_theme_add_resource_path()
5348 * to add application-specific icons to the icon theme.
5349 */
5350 void
gtk_icon_theme_add_builtin_icon(const gchar * icon_name,gint size,GdkPixbuf * pixbuf)5351 gtk_icon_theme_add_builtin_icon (const gchar *icon_name,
5352 gint size,
5353 GdkPixbuf *pixbuf)
5354 {
5355 BuiltinIcon *default_icon;
5356 GSList *icons;
5357 gpointer key;
5358
5359 g_return_if_fail (icon_name != NULL);
5360 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
5361
5362 if (!icon_theme_builtin_icons)
5363 icon_theme_builtin_icons = g_hash_table_new (g_str_hash, g_str_equal);
5364
5365 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
5366 if (!icons)
5367 key = g_strdup (icon_name);
5368 else
5369 key = (gpointer)icon_name; /* Won't get stored */
5370
5371 default_icon = g_new (BuiltinIcon, 1);
5372 default_icon->size = size;
5373 default_icon->pixbuf = g_object_ref (pixbuf);
5374 icons = g_slist_prepend (icons, default_icon);
5375
5376 /* Replaces value, leaves key untouched
5377 */
5378 g_hash_table_insert (icon_theme_builtin_icons, key, icons);
5379 }
5380
5381 /* Look up a builtin icon; the min_difference_p and
5382 * has_larger_p out parameters allow us to combine
5383 * this lookup with searching through the actual directories
5384 * of the “hicolor” icon theme. See theme_lookup_icon()
5385 * for how they are used.
5386 */
5387 static BuiltinIcon *
find_builtin_icon(const gchar * icon_name,gint size,gint scale,gint * min_difference_p)5388 find_builtin_icon (const gchar *icon_name,
5389 gint size,
5390 gint scale,
5391 gint *min_difference_p)
5392 {
5393 GSList *icons = NULL;
5394 gint min_difference = G_MAXINT;
5395 gboolean has_larger = FALSE;
5396 BuiltinIcon *min_icon = NULL;
5397
5398 if (!icon_theme_builtin_icons)
5399 return NULL;
5400
5401 size *= scale;
5402
5403 icons = g_hash_table_lookup (icon_theme_builtin_icons, icon_name);
5404
5405 while (icons)
5406 {
5407 BuiltinIcon *default_icon = icons->data;
5408 int min, max, difference;
5409 gboolean smaller;
5410
5411 min = default_icon->size - 2;
5412 max = default_icon->size + 2;
5413 smaller = size < min;
5414 if (size < min)
5415 difference = min - size;
5416 else if (size > max)
5417 difference = size - max;
5418 else
5419 difference = 0;
5420
5421 if (difference == 0)
5422 {
5423 min_difference = 0;
5424 min_icon = default_icon;
5425 break;
5426 }
5427
5428 if (!has_larger)
5429 {
5430 if (difference < min_difference || smaller)
5431 {
5432 min_difference = difference;
5433 min_icon = default_icon;
5434 has_larger = smaller;
5435 }
5436 }
5437 else
5438 {
5439 if (difference < min_difference && smaller)
5440 {
5441 min_difference = difference;
5442 min_icon = default_icon;
5443 }
5444 }
5445
5446 icons = icons->next;
5447 }
5448
5449 if (min_difference_p)
5450 *min_difference_p = min_difference;
5451
5452 return min_icon;
5453 }
5454
5455 /**
5456 * gtk_icon_theme_lookup_by_gicon:
5457 * @icon_theme: a #GtkIconTheme
5458 * @icon: the #GIcon to look up
5459 * @size: desired icon size
5460 * @flags: flags modifying the behavior of the icon lookup
5461 *
5462 * Looks up an icon and returns a #GtkIconInfo containing information
5463 * such as the filename of the icon. The icon can then be rendered
5464 * into a pixbuf using gtk_icon_info_load_icon().
5465 *
5466 * When rendering on displays with high pixel densities you should not
5467 * use a @size multiplied by the scaling factor returned by functions
5468 * like gdk_window_get_scale_factor(). Instead, you should use
5469 * gtk_icon_theme_lookup_by_gicon_for_scale(), as the assets loaded
5470 * for a given scaling factor may be different.
5471 *
5472 * Returns: (nullable) (transfer full): a #GtkIconInfo containing
5473 * information about the icon, or %NULL if the icon wasn’t
5474 * found. Unref with g_object_unref()
5475 *
5476 * Since: 2.14
5477 */
5478 GtkIconInfo *
gtk_icon_theme_lookup_by_gicon(GtkIconTheme * icon_theme,GIcon * icon,gint size,GtkIconLookupFlags flags)5479 gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme,
5480 GIcon *icon,
5481 gint size,
5482 GtkIconLookupFlags flags)
5483 {
5484 return gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme, icon,
5485 size, 1, flags);
5486 }
5487
5488
5489 /**
5490 * gtk_icon_theme_lookup_by_gicon_for_scale:
5491 * @icon_theme: a #GtkIconTheme
5492 * @icon: the #GIcon to look up
5493 * @size: desired icon size
5494 * @scale: the desired scale
5495 * @flags: flags modifying the behavior of the icon lookup
5496 *
5497 * Looks up an icon and returns a #GtkIconInfo containing information
5498 * such as the filename of the icon. The icon can then be rendered into
5499 * a pixbuf using gtk_icon_info_load_icon().
5500 *
5501 * Returns: (nullable) (transfer full): a #GtkIconInfo containing
5502 * information about the icon, or %NULL if the icon wasn’t
5503 * found. Unref with g_object_unref()
5504 *
5505 * Since: 3.10
5506 */
5507 GtkIconInfo *
gtk_icon_theme_lookup_by_gicon_for_scale(GtkIconTheme * icon_theme,GIcon * icon,gint size,gint scale,GtkIconLookupFlags flags)5508 gtk_icon_theme_lookup_by_gicon_for_scale (GtkIconTheme *icon_theme,
5509 GIcon *icon,
5510 gint size,
5511 gint scale,
5512 GtkIconLookupFlags flags)
5513 {
5514 GtkIconInfo *info;
5515
5516 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
5517 g_return_val_if_fail (G_IS_ICON (icon), NULL);
5518 g_warn_if_fail ((flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK) == 0);
5519
5520 if (GDK_IS_PIXBUF (icon))
5521 {
5522 GdkPixbuf *pixbuf;
5523
5524 pixbuf = GDK_PIXBUF (icon);
5525
5526 if ((flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0)
5527 {
5528 gint width, height, max;
5529 gdouble pixbuf_scale;
5530 GdkPixbuf *scaled;
5531
5532 width = gdk_pixbuf_get_width (pixbuf);
5533 height = gdk_pixbuf_get_height (pixbuf);
5534 max = MAX (width, height);
5535 pixbuf_scale = (gdouble) size * scale / (gdouble) max;
5536
5537 scaled = gdk_pixbuf_scale_simple (pixbuf,
5538 0.5 + width * pixbuf_scale,
5539 0.5 + height * pixbuf_scale,
5540 GDK_INTERP_BILINEAR);
5541
5542 info = gtk_icon_info_new_for_pixbuf (icon_theme, scaled);
5543
5544 g_object_unref (scaled);
5545 }
5546 else
5547 {
5548 info = gtk_icon_info_new_for_pixbuf (icon_theme, pixbuf);
5549 }
5550
5551 return info;
5552 }
5553 else if (G_IS_FILE_ICON (icon))
5554 {
5555 GFile *file = g_file_icon_get_file (G_FILE_ICON (icon));
5556
5557 info = gtk_icon_info_new_for_file (file, size, scale);
5558 info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
5559
5560 return info;
5561 }
5562 else if (G_IS_LOADABLE_ICON (icon))
5563 {
5564 info = icon_info_new (ICON_THEME_DIR_UNTHEMED, size, 1);
5565 info->loadable = G_LOADABLE_ICON (g_object_ref (icon));
5566 info->is_svg = FALSE;
5567 info->desired_size = size;
5568 info->desired_scale = scale;
5569 info->forced_size = (flags & GTK_ICON_LOOKUP_FORCE_SIZE) != 0;
5570
5571 return info;
5572 }
5573 else if (G_IS_THEMED_ICON (icon))
5574 {
5575 const gchar **names;
5576
5577 names = (const gchar **)g_themed_icon_get_names (G_THEMED_ICON (icon));
5578 info = gtk_icon_theme_choose_icon_for_scale (icon_theme, names, size, scale, flags);
5579
5580 return info;
5581 }
5582 else if (G_IS_EMBLEMED_ICON (icon))
5583 {
5584 GIcon *base, *emblem;
5585 GList *list, *l;
5586 GtkIconInfo *base_info, *emblem_info;
5587
5588 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
5589 if (GTK_IS_NUMERABLE_ICON (icon))
5590 _gtk_numerable_icon_set_background_icon_size (GTK_NUMERABLE_ICON (icon), size / 2);
5591 G_GNUC_END_IGNORE_DEPRECATIONS
5592
5593 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
5594 base_info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme, base, size, scale, flags);
5595 if (base_info)
5596 {
5597 info = icon_info_dup (base_info);
5598 g_object_unref (base_info);
5599
5600 list = g_emblemed_icon_get_emblems (G_EMBLEMED_ICON (icon));
5601 for (l = list; l; l = l->next)
5602 {
5603 emblem = g_emblem_get_icon (G_EMBLEM (l->data));
5604 /* always force size for emblems */
5605 emblem_info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme, emblem, size / 2, scale, flags | GTK_ICON_LOOKUP_FORCE_SIZE);
5606 if (emblem_info)
5607 info->emblem_infos = g_slist_prepend (info->emblem_infos, emblem_info);
5608 }
5609
5610 return info;
5611 }
5612 else
5613 return NULL;
5614 }
5615
5616 return NULL;
5617 }
5618
5619 /**
5620 * gtk_icon_info_new_for_pixbuf:
5621 * @icon_theme: a #GtkIconTheme
5622 * @pixbuf: the pixbuf to wrap in a #GtkIconInfo
5623 *
5624 * Creates a #GtkIconInfo for a #GdkPixbuf.
5625 *
5626 * Returns: (transfer full): a #GtkIconInfo
5627 *
5628 * Since: 2.14
5629 */
5630 GtkIconInfo *
gtk_icon_info_new_for_pixbuf(GtkIconTheme * icon_theme,GdkPixbuf * pixbuf)5631 gtk_icon_info_new_for_pixbuf (GtkIconTheme *icon_theme,
5632 GdkPixbuf *pixbuf)
5633 {
5634 GtkIconInfo *info;
5635
5636 g_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
5637 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
5638
5639 info = icon_info_new (ICON_THEME_DIR_UNTHEMED, 0, 1);
5640 info->pixbuf = g_object_ref (pixbuf);
5641 info->scale = 1.0;
5642
5643 return info;
5644 }
5645
5646 GtkIconInfo *
gtk_icon_info_new_for_file(GFile * file,gint size,gint scale)5647 gtk_icon_info_new_for_file (GFile *file,
5648 gint size,
5649 gint scale)
5650 {
5651 GtkIconInfo *info;
5652
5653 info = icon_info_new (ICON_THEME_DIR_UNTHEMED, size, 1);
5654 info->loadable = G_LOADABLE_ICON (g_file_icon_new (file));
5655 info->icon_file = g_object_ref (file);
5656 info->is_resource = g_file_has_uri_scheme (file, "resource");
5657
5658 if (info->is_resource)
5659 {
5660 gchar *uri;
5661
5662 uri = g_file_get_uri (file);
5663 info->filename = g_strdup (uri + 11); /* resource:// */
5664 g_free (uri);
5665 }
5666 else
5667 {
5668 info->filename = g_file_get_path (file);
5669 }
5670
5671 info->is_svg = suffix_from_name (info->filename) == ICON_SUFFIX_SVG;
5672
5673 info->desired_size = size;
5674 info->desired_scale = scale;
5675 info->forced_size = FALSE;
5676
5677 return info;
5678 }
5679