nautilus-3.6.3/libnautilus-private/nautilus-icon-info.c

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 }