No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* nautilus-icon-info.c
3 * Copyright (C) 2007 Red Hat, Inc., Alexander Larsson <alexl@redhat.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #include <config.h>
22 #include <string.h>
23 #include "nautilus-icon-info.h"
24 #include "nautilus-icon-names.h"
25 #include "nautilus-default-file-icon.h"
26 #include <gtk/gtk.h>
27 #include <gio/gio.h>
28
29 struct _NautilusIconInfo
30 {
31 GObject parent;
32
33 gboolean sole_owner;
34 gint64 last_use_time;
35 GdkPixbuf *pixbuf;
36
37 gboolean got_embedded_rect;
38 GdkRectangle embedded_rect;
39 gint n_attach_points;
40 GdkPoint *attach_points;
41 char *display_name;
42 char *icon_name;
43 };
44
45 struct _NautilusIconInfoClass
46 {
47 GObjectClass parent_class;
48 };
49
50 static void schedule_reap_cache (void);
51
52 G_DEFINE_TYPE (NautilusIconInfo,
53 nautilus_icon_info,
54 G_TYPE_OBJECT);
55
56 static void
57 nautilus_icon_info_init (NautilusIconInfo *icon)
58 {
59 icon->last_use_time = g_get_monotonic_time ();
60 icon->sole_owner = TRUE;
61 }
62
63 gboolean
64 nautilus_icon_info_is_fallback (NautilusIconInfo *icon)
65 {
66 return icon->pixbuf == NULL;
67 }
68
69 static void
70 pixbuf_toggle_notify (gpointer info,
71 GObject *object,
72 gboolean is_last_ref)
73 {
74 NautilusIconInfo *icon = info;
75
76 if (is_last_ref) {
77 icon->sole_owner = TRUE;
78 g_object_remove_toggle_ref (object,
79 pixbuf_toggle_notify,
80 info);
81 icon->last_use_time = g_get_monotonic_time ();
82 schedule_reap_cache ();
83 }
84 }
85
86 static void
87 nautilus_icon_info_finalize (GObject *object)
88 {
89 NautilusIconInfo *icon;
90
91 icon = NAUTILUS_ICON_INFO (object);
92
93 if (!icon->sole_owner && icon->pixbuf) {
94 g_object_remove_toggle_ref (G_OBJECT (icon->pixbuf),
95 pixbuf_toggle_notify,
96 icon);
97 }
98
99 if (icon->pixbuf) {
100 g_object_unref (icon->pixbuf);
101 }
102 g_free (icon->attach_points);
103 g_free (icon->display_name);
104 g_free (icon->icon_name);
105
106 G_OBJECT_CLASS (nautilus_icon_info_parent_class)->finalize (object);
107 }
108
109 static void
110 nautilus_icon_info_class_init (NautilusIconInfoClass *icon_info_class)
111 {
112 GObjectClass *gobject_class;
113
114 gobject_class = (GObjectClass *) icon_info_class;
115
116 gobject_class->finalize = nautilus_icon_info_finalize;
117
118 }
119
120 NautilusIconInfo *
121 nautilus_icon_info_new_for_pixbuf (GdkPixbuf *pixbuf)
122 {
123 NautilusIconInfo *icon;
124
125 icon = g_object_new (NAUTILUS_TYPE_ICON_INFO, NULL);
126
127 if (pixbuf) {
128 icon->pixbuf = g_object_ref (pixbuf);
129 }
130
131 return icon;
132 }
133
134 static NautilusIconInfo *
135 nautilus_icon_info_new_for_icon_info (GtkIconInfo *icon_info)
136 {
137 NautilusIconInfo *icon;
138 GdkPoint *points;
139 gint n_points;
140 const char *filename;
141 char *basename, *p;
142
143 icon = g_object_new (NAUTILUS_TYPE_ICON_INFO, NULL);
144
145 icon->pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
146
147 icon->got_embedded_rect = gtk_icon_info_get_embedded_rect (icon_info,
148 &icon->embedded_rect);
149
150 if (gtk_icon_info_get_attach_points (icon_info, &points, &n_points)) {
151 icon->n_attach_points = n_points;
152 icon->attach_points = points;
153 }
154
155 icon->display_name = g_strdup (gtk_icon_info_get_display_name (icon_info));
156
157 filename = gtk_icon_info_get_filename (icon_info);
158 if (filename != NULL) {
159 basename = g_path_get_basename (filename);
160 p = strrchr (basename, '.');
161 if (p) {
162 *p = 0;
163 }
164 icon->icon_name = basename;
165 }
166
167 return icon;
168 }
169
170
171 typedef struct {
172 GIcon *icon;
173 int size;
174 } LoadableIconKey;
175
176 typedef struct {
177 char *filename;
178 int size;
179 } ThemedIconKey;
180
181 static GHashTable *loadable_icon_cache = NULL;
182 static GHashTable *themed_icon_cache = NULL;
183 static guint reap_cache_timeout = 0;
184
185 #define MICROSEC_PER_SEC ((guint64)1000000L)
186
187 static guint time_now;
188
189 static gboolean
190 reap_old_icon (gpointer key,
191 gpointer value,
192 gpointer user_info)
193 {
194 NautilusIconInfo *icon = value;
195 gboolean *reapable_icons_left = user_info;
196
197 if (icon->sole_owner) {
198 if (time_now - icon->last_use_time > 30 * MICROSEC_PER_SEC) {
199 /* This went unused 30 secs ago. reap */
200 return TRUE;
201 } else {
202 /* We can reap this soon */
203 *reapable_icons_left = TRUE;
204 }
205 }
206
207 return FALSE;
208 }
209
210 static gboolean
211 reap_cache (gpointer data)
212 {
213 gboolean reapable_icons_left;
214
215 reapable_icons_left = TRUE;
216
217 time_now = g_get_monotonic_time ();
218
219 if (loadable_icon_cache) {
220 g_hash_table_foreach_remove (loadable_icon_cache,
221 reap_old_icon,
222 &reapable_icons_left);
223 }
224
225 if (themed_icon_cache) {
226 g_hash_table_foreach_remove (themed_icon_cache,
227 reap_old_icon,
228 &reapable_icons_left);
229 }
230
231 if (reapable_icons_left) {
232 return TRUE;
233 } else {
234 reap_cache_timeout = 0;
235 return FALSE;
236 }
237 }
238
239 static void
240 schedule_reap_cache (void)
241 {
242 if (reap_cache_timeout == 0) {
243 reap_cache_timeout = g_timeout_add_seconds_full (0, 5,
244 reap_cache,
245 NULL, NULL);
246 }
247 }
248
249 void
250 nautilus_icon_info_clear_caches (void)
251 {
252 if (loadable_icon_cache) {
253 g_hash_table_remove_all (loadable_icon_cache);
254 }
255
256 if (themed_icon_cache) {
257 g_hash_table_remove_all (themed_icon_cache);
258 }
259 }
260
261 static guint
262 loadable_icon_key_hash (LoadableIconKey *key)
263 {
264 return g_icon_hash (key->icon) ^ key->size;
265 }
266
267 static gboolean
268 loadable_icon_key_equal (const LoadableIconKey *a,
269 const LoadableIconKey *b)
270 {
271 return a->size == b->size &&
272 g_icon_equal (a->icon, b->icon);
273 }
274
275 static LoadableIconKey *
276 loadable_icon_key_new (GIcon *icon, int size)
277 {
278 LoadableIconKey *key;
279
280 key = g_slice_new (LoadableIconKey);
281 key->icon = g_object_ref (icon);
282 key->size = size;
283
284 return key;
285 }
286
287 static void
288 loadable_icon_key_free (LoadableIconKey *key)
289 {
290 g_object_unref (key->icon);
291 g_slice_free (LoadableIconKey, key);
292 }
293
294 static guint
295 themed_icon_key_hash (ThemedIconKey *key)
296 {
297 return g_str_hash (key->filename) ^ key->size;
298 }
299
300 static gboolean
301 themed_icon_key_equal (const ThemedIconKey *a,
302 const ThemedIconKey *b)
303 {
304 return a->size == b->size &&
305 g_str_equal (a->filename, b->filename);
306 }
307
308 static ThemedIconKey *
309 themed_icon_key_new (const char *filename, int size)
310 {
311 ThemedIconKey *key;
312
313 key = g_slice_new (ThemedIconKey);
314 key->filename = g_strdup (filename);
315 key->size = size;
316
317 return key;
318 }
319
320 static void
321 themed_icon_key_free (ThemedIconKey *key)
322 {
323 g_free (key->filename);
324 g_slice_free (ThemedIconKey, key);
325 }
326
327 NautilusIconInfo *
328 nautilus_icon_info_lookup (GIcon *icon,
329 int size)
330 {
331 NautilusIconInfo *icon_info;
332 GdkPixbuf *pixbuf;
333
334 if (G_IS_LOADABLE_ICON (icon)) {
335 LoadableIconKey lookup_key;
336 LoadableIconKey *key;
337 GInputStream *stream;
338
339 if (loadable_icon_cache == NULL) {
340 loadable_icon_cache =
341 g_hash_table_new_full ((GHashFunc)loadable_icon_key_hash,
342 (GEqualFunc)loadable_icon_key_equal,
343 (GDestroyNotify) loadable_icon_key_free,
344 (GDestroyNotify) g_object_unref);
345 }
346
347 lookup_key.icon = icon;
348 lookup_key.size = size;
349
350 icon_info = g_hash_table_lookup (loadable_icon_cache, &lookup_key);
351 if (icon_info) {
352 return g_object_ref (icon_info);
353 }
354
355 pixbuf = NULL;
356 stream = g_loadable_icon_load (G_LOADABLE_ICON (icon),
357 size,
358 NULL, NULL, NULL);
359 if (stream) {
360 pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
361 size, size, TRUE,
362 NULL, NULL);
363 g_input_stream_close (stream, NULL, NULL);
364 g_object_unref (stream);
365 }
366
367 icon_info = nautilus_icon_info_new_for_pixbuf (pixbuf);
368
369 key = loadable_icon_key_new (icon, size);
370 g_hash_table_insert (loadable_icon_cache, key, icon_info);
371
372 return g_object_ref (icon_info);
373 } else if (G_IS_THEMED_ICON (icon)) {
374 const char * const *names;
375 ThemedIconKey lookup_key;
376 ThemedIconKey *key;
377 GtkIconTheme *icon_theme;
378 GtkIconInfo *gtkicon_info;
379 const char *filename;
380
381 if (themed_icon_cache == NULL) {
382 themed_icon_cache =
383 g_hash_table_new_full ((GHashFunc)themed_icon_key_hash,
384 (GEqualFunc)themed_icon_key_equal,
385 (GDestroyNotify) themed_icon_key_free,
386 (GDestroyNotify) g_object_unref);
387 }
388
389 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
390
391 icon_theme = gtk_icon_theme_get_default ();
392 gtkicon_info = gtk_icon_theme_choose_icon (icon_theme, (const char **)names, size, 0);
393
394 if (gtkicon_info == NULL) {
395 return nautilus_icon_info_new_for_pixbuf (NULL);
396 }
397
398 filename = gtk_icon_info_get_filename (gtkicon_info);
399 if (filename == NULL) {
400 gtk_icon_info_free (gtkicon_info);
401 return nautilus_icon_info_new_for_pixbuf (NULL);
402 }
403
404 lookup_key.filename = (char *)filename;
405 lookup_key.size = size;
406
407 icon_info = g_hash_table_lookup (themed_icon_cache, &lookup_key);
408 if (icon_info) {
409 gtk_icon_info_free (gtkicon_info);
410 return g_object_ref (icon_info);
411 }
412
413 icon_info = nautilus_icon_info_new_for_icon_info (gtkicon_info);
414
415 key = themed_icon_key_new (filename, size);
416 g_hash_table_insert (themed_icon_cache, key, icon_info);
417
418 gtk_icon_info_free (gtkicon_info);
419
420 return g_object_ref (icon_info);
421 } else {
422 GdkPixbuf *pixbuf;
423 GtkIconInfo *gtk_icon_info;
424
425 gtk_icon_info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
426 icon,
427 size,
428 GTK_ICON_LOOKUP_GENERIC_FALLBACK);
429 if (gtk_icon_info != NULL) {
430 pixbuf = gtk_icon_info_load_icon (gtk_icon_info, NULL);
431 gtk_icon_info_free (gtk_icon_info);
432 } else {
433 pixbuf = NULL;
434 }
435
436 icon_info = nautilus_icon_info_new_for_pixbuf (pixbuf);
437
438 if (pixbuf != NULL) {
439 g_object_unref (pixbuf);
440 }
441
442 return icon_info;
443 }
444 }
445
446 NautilusIconInfo *
447 nautilus_icon_info_lookup_from_name (const char *name,
448 int size)
449 {
450 GIcon *icon;
451 NautilusIconInfo *info;
452
453 icon = g_themed_icon_new (name);
454 info = nautilus_icon_info_lookup (icon, size);
455 g_object_unref (icon);
456 return info;
457 }
458
459 NautilusIconInfo *
460 nautilus_icon_info_lookup_from_path (const char *path,
461 int size)
462 {
463 GFile *icon_file;
464 GIcon *icon;
465 NautilusIconInfo *info;
466
467 icon_file = g_file_new_for_path (path);
468 icon = g_file_icon_new (icon_file);
469 info = nautilus_icon_info_lookup (icon, size);
470 g_object_unref (icon);
471 g_object_unref (icon_file);
472 return info;
473 }
474
475 GdkPixbuf *
476 nautilus_icon_info_get_pixbuf_nodefault (NautilusIconInfo *icon)
477 {
478 GdkPixbuf *res;
479
480 if (icon->pixbuf == NULL) {
481 res = NULL;
482 } else {
483 res = g_object_ref (icon->pixbuf);
484
485 if (icon->sole_owner) {
486 icon->sole_owner = FALSE;
487 g_object_add_toggle_ref (G_OBJECT (res),
488 pixbuf_toggle_notify,
489 icon);
490 }
491 }
492
493 return res;
494 }
495
496
497 GdkPixbuf *
498 nautilus_icon_info_get_pixbuf (NautilusIconInfo *icon)
499 {
500 GdkPixbuf *res;
501
502 res = nautilus_icon_info_get_pixbuf_nodefault (icon);
503 if (res == NULL) {
504 res = gdk_pixbuf_new_from_data (nautilus_default_file_icon,
505 GDK_COLORSPACE_RGB,
506 TRUE,
507 8,
508 nautilus_default_file_icon_width,
509 nautilus_default_file_icon_height,
510 nautilus_default_file_icon_width * 4, /* stride */
511 NULL, /* don't destroy info */
512 NULL);
513 }
514
515 return res;
516 }
517
518 GdkPixbuf *
519 nautilus_icon_info_get_pixbuf_nodefault_at_size (NautilusIconInfo *icon,
520 gsize forced_size)
521 {
522 GdkPixbuf *pixbuf, *scaled_pixbuf;
523 int w, h, s;
524 double scale;
525
526 pixbuf = nautilus_icon_info_get_pixbuf_nodefault (icon);
527
528 if (pixbuf == NULL)
529 return NULL;
530
531 w = gdk_pixbuf_get_width (pixbuf);
532 h = gdk_pixbuf_get_height (pixbuf);
533 s = MAX (w, h);
534 if (s == forced_size) {
535 return pixbuf;
536 }
537
538 scale = (double)forced_size / s;
539 scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
540 w * scale, h * scale,
541 GDK_INTERP_BILINEAR);
542 g_object_unref (pixbuf);
543 return scaled_pixbuf;
544 }
545
546
547 GdkPixbuf *
548 nautilus_icon_info_get_pixbuf_at_size (NautilusIconInfo *icon,
549 gsize forced_size)
550 {
551 GdkPixbuf *pixbuf, *scaled_pixbuf;
552 int w, h, s;
553 double scale;
554
555 pixbuf = nautilus_icon_info_get_pixbuf (icon);
556
557 w = gdk_pixbuf_get_width (pixbuf);
558 h = gdk_pixbuf_get_height (pixbuf);
559 s = MAX (w, h);
560 if (s == forced_size) {
561 return pixbuf;
562 }
563
564 scale = (double)forced_size / s;
565 scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
566 w * scale, h * scale,
567 GDK_INTERP_BILINEAR);
568 g_object_unref (pixbuf);
569 return scaled_pixbuf;
570 }
571
572 gboolean
573 nautilus_icon_info_get_embedded_rect (NautilusIconInfo *icon,
574 GdkRectangle *rectangle)
575 {
576 *rectangle = icon->embedded_rect;
577 return icon->got_embedded_rect;
578 }
579
580 gboolean
581 nautilus_icon_info_get_attach_points (NautilusIconInfo *icon,
582 GdkPoint **points,
583 gint *n_points)
584 {
585 *n_points = icon->n_attach_points;
586 *points = icon->attach_points;
587 return icon->n_attach_points != 0;
588 }
589
590 const char *
591 nautilus_icon_info_get_display_name (NautilusIconInfo *icon)
592 {
593 return icon->display_name;
594 }
595
596 const char *
597 nautilus_icon_info_get_used_name (NautilusIconInfo *icon)
598 {
599 return icon->icon_name;
600 }
601
602 /* Return nominal icon size for given zoom level.
603 * @zoom_level: zoom level for which to find matching icon size.
604 *
605 * Return value: icon size between NAUTILUS_ICON_SIZE_SMALLEST and
606 * NAUTILUS_ICON_SIZE_LARGEST, inclusive.
607 */
608 guint
609 nautilus_get_icon_size_for_zoom_level (NautilusZoomLevel zoom_level)
610 {
611 switch (zoom_level) {
612 case NAUTILUS_ZOOM_LEVEL_SMALLEST:
613 return NAUTILUS_ICON_SIZE_SMALLEST;
614 case NAUTILUS_ZOOM_LEVEL_SMALLER:
615 return NAUTILUS_ICON_SIZE_SMALLER;
616 case NAUTILUS_ZOOM_LEVEL_SMALL:
617 return NAUTILUS_ICON_SIZE_SMALL;
618 case NAUTILUS_ZOOM_LEVEL_STANDARD:
619 return NAUTILUS_ICON_SIZE_STANDARD;
620 case NAUTILUS_ZOOM_LEVEL_LARGE:
621 return NAUTILUS_ICON_SIZE_LARGE;
622 case NAUTILUS_ZOOM_LEVEL_LARGER:
623 return NAUTILUS_ICON_SIZE_LARGER;
624 case NAUTILUS_ZOOM_LEVEL_LARGEST:
625 return NAUTILUS_ICON_SIZE_LARGEST;
626 }
627 g_return_val_if_reached (NAUTILUS_ICON_SIZE_STANDARD);
628 }
629
630 gint
631 nautilus_get_icon_size_for_stock_size (GtkIconSize size)
632 {
633 gint w, h;
634
635 if (gtk_icon_size_lookup (size, &w, &h)) {
636 return MAX (w, h);
637 }
638 return NAUTILUS_ICON_SIZE_STANDARD;
639 }
640
641
642 guint
643 nautilus_icon_get_emblem_size_for_icon_size (guint size)
644 {
645 if (size >= 96)
646 return 48;
647 if (size >= 64)
648 return 32;
649 if (size >= 48)
650 return 24;
651 if (size >= 24)
652 return 16;
653 if (size >= 16)
654 return 12;
655
656 return 0; /* no emblems for smaller sizes */
657 }
658
659 gboolean
660 nautilus_icon_theme_can_render (GThemedIcon *icon)
661 {
662 GtkIconTheme *icon_theme;
663 const gchar * const *names;
664 gint idx;
665
666 names = g_themed_icon_get_names (icon);
667
668 icon_theme = gtk_icon_theme_get_default ();
669
670 for (idx = 0; names[idx] != NULL; idx++) {
671 if (gtk_icon_theme_has_icon (icon_theme, names[idx])) {
672 return TRUE;
673 }
674 }
675
676 return FALSE;
677 }