1 /* Icon loading support for the Midnight Commander
3 * Copyright (C) 1998-2000 The Free Software Foundation
5 * Authors: Miguel de Icaza <miguel@nuclecu.unam.mx>
6 * Federico Mena <federico@nuclecu.unam.mx>
11 #include <sys/types.h>
21 /* What kinds of images can an icon set contain */
28 /* Information for an icon set (plain icon plus overlayed versions) */
30 GdkImlibImage
*plain
; /* Plain version */
31 GdkImlibImage
*symlink
; /* Symlink version */
32 GdkImlibImage
*stalled
; /* Stalled symlink version */
33 char *filename
; /* Filename for the plain image */
36 static int gicon_inited
; /* Has the icon system been inited? */
38 static GHashTable
*name_hash
; /* Hash from filename -> IconSet */
39 static GHashTable
*image_hash
; /* Hash from imlib image -> IconSet */
41 static GdkImlibImage
*symlink_overlay
; /* The little symlink overlay */
42 static GdkImlibImage
*stalled_overlay
; /* The little stalled symlink overlay */
44 /* Default icons, guaranteed to exist */
45 static IconSet
*iset_directory
;
46 static IconSet
*iset_dirclosed
;
47 static IconSet
*iset_executable
;
48 static IconSet
*iset_regular
;
49 static IconSet
*iset_core
;
50 static IconSet
*iset_sock
;
51 static IconSet
*iset_fifo
;
52 static IconSet
*iset_chardev
;
53 static IconSet
*iset_blockdev
;
55 /* Our UID and GID, needed to see if the user can access some files */
60 /* Whether we should always use (expensive) metadata lookups for file panels or not */
61 int we_can_afford_the_speed
= 0;
63 /* Builds a composite of the plain image and the litle symlink icon */
64 static GdkImlibImage
*
65 build_overlay (GdkImlibImage
*plain
, GdkImlibImage
*overlay
)
68 int overlay_rowstride
;
73 im
= gdk_imlib_clone_image (plain
);
75 g_return_val_if_fail(plain
, NULL
);
76 g_return_val_if_fail(overlay
, NULL
);
77 rowstride
= plain
->rgb_width
* 3;
78 overlay_rowstride
= overlay
->rgb_width
* 3;
80 dest
= im
->rgb_data
+ ((plain
->rgb_height
- overlay
->rgb_height
) * rowstride
81 + (plain
->rgb_width
- overlay
->rgb_width
) * 3);
83 src
= overlay
->rgb_data
;
85 for (y
= 0; y
< overlay
->rgb_height
; y
++) {
86 memcpy (dest
, src
, overlay_rowstride
);
89 src
+= overlay_rowstride
;
92 gdk_imlib_changed_image (im
);
96 /* Ensures that the icon set has the requested image */
98 ensure_icon_image (IconSet
*iset
, IconType type
)
100 g_assert (iset
->plain
!= NULL
);
103 case ICON_TYPE_PLAIN
:
104 /* The plain type always exists, so do nothing */
107 case ICON_TYPE_SYMLINK
:
108 iset
->symlink
= build_overlay (iset
->plain
, symlink_overlay
);
109 g_hash_table_insert (image_hash
, iset
->symlink
, iset
);
112 case ICON_TYPE_STALLED
:
113 iset
->stalled
= build_overlay (iset
->plain
, stalled_overlay
);
114 g_hash_table_insert (image_hash
, iset
->stalled
, iset
);
118 g_assert_not_reached ();
123 compute_scaled_size (int width
, int height
, int *nwidth
, int *nheight
)
125 g_return_if_fail (nwidth
!= NULL
);
126 g_return_if_fail (nheight
!= NULL
);
128 if (width
<= ICON_IMAGE_WIDTH
&& height
<= ICON_IMAGE_HEIGHT
) {
131 } else if (width
< height
) {
132 *nheight
= ICON_IMAGE_HEIGHT
;
133 *nwidth
= *nheight
* width
/ height
;
135 *nwidth
= ICON_IMAGE_WIDTH
;
136 *nheight
= *nwidth
* height
/ width
;
140 /* Returns a newly allocated, correctly scaled image */
141 static GdkImlibImage
*
142 get_scaled_image (GdkImlibImage
*orig
)
147 g_return_val_if_fail (orig
!= NULL
, NULL
);
149 compute_scaled_size (orig
->rgb_width
, orig
->rgb_height
,
151 im
= gdk_imlib_clone_scaled_image (orig
, width
, height
);
156 /* Returns the icon set corresponding to the specified image.
157 * If we create a new IconSet, iset->plain is set to a new scaled
158 * image, so _WE FREE THE IM PARAMETER_. */
160 get_icon_set_from_image (GdkImlibImage
*im
)
164 g_return_val_if_fail (im
!= NULL
, NULL
);
166 iset
= g_hash_table_lookup (image_hash
, im
);
170 iset
= g_new (IconSet
, 1);
171 iset
->plain
= get_scaled_image (im
);
172 iset
->symlink
= NULL
;
173 iset
->stalled
= NULL
;
174 iset
->filename
= NULL
;
177 /* Insert the icon information into the hash tables */
179 g_hash_table_remove (image_hash
, im
);
180 g_hash_table_insert (image_hash
, iset
->plain
, iset
);
182 gdk_imlib_destroy_image (im
);
187 /* Returns the icon set corresponding to the specified icon filename, or NULL if
188 * the file could not be loaded.
191 get_icon_set (const char *filename
)
196 iset
= g_hash_table_lookup (name_hash
, filename
);
200 im
= gdk_imlib_load_image ((char *) filename
);
204 iset
= get_icon_set_from_image (im
);
207 iset
->filename
= g_strdup (filename
);
209 /* Insert the icon information into the hash tables */
211 g_hash_table_insert (name_hash
, iset
->filename
, iset
);
216 /* Convenience function to load one of the default icons and die if this fails */
218 get_stock_icon (char *name
)
223 filename
= g_concat_dir_and_file (ICONDIR
, name
);
224 iset
= get_icon_set (filename
);
230 /* Convenience function to load one of the default overlays and die if this fails */
231 static GdkImlibImage
*
232 get_stock_overlay (char *name
)
237 filename
= g_concat_dir_and_file (ICONDIR
, name
);
238 im
= gdk_imlib_load_image (filename
);
249 * Initializes the icon module.
259 name_hash
= g_hash_table_new (g_str_hash
, g_str_equal
);
260 image_hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
262 /* Load the default icons */
264 iset_directory
= get_stock_icon ("i-directory.png");
265 iset_dirclosed
= get_stock_icon ("i-dirclosed.png");
266 iset_executable
= get_stock_icon ("i-executable.png");
267 iset_regular
= get_stock_icon ("i-regular.png");
268 iset_core
= get_stock_icon ("i-core.png");
269 iset_sock
= get_stock_icon ("i-sock.png");
270 iset_fifo
= get_stock_icon ("i-fifo.png");
271 iset_chardev
= get_stock_icon ("i-chardev.png");
272 iset_blockdev
= get_stock_icon ("i-blockdev.png");
274 /* Load the overlay icons */
276 symlink_overlay
= get_stock_overlay ("i-symlink.png");
277 stalled_overlay
= get_stock_overlay ("i-stalled.png");
279 if (!iset_directory
|| !iset_dirclosed
|| !iset_executable
||
280 !iset_regular
|| !iset_core
|| !iset_fifo
|| !iset_chardev
||
281 !iset_blockdev
|| !symlink_overlay
|| !stalled_overlay
) {
282 message (1, _("Error"), _("Default set of icons not found, check your installation"));
290 /* Tries to get an icon from the file's metadata information */
291 static GdkImlibImage
*
292 get_icon_from_metadata (char *filename
)
296 IconSet
*iset
= NULL
;
298 /* Try the inlined icon */
300 if (gnome_metadata_get (filename
, "icon-inline-png", &size
, &buf
) == 0) {
302 im
= gdk_imlib_inlined_png_to_image (buf
, size
);
305 iset
= get_icon_set_from_image (im
);
310 /* Try the non-inlined icon */
312 if (!iset
&& gnome_metadata_get (filename
, "icon-filename", &size
, &buf
) == 0) {
313 iset
= get_icon_set (buf
);
318 ensure_icon_image (iset
, ICON_TYPE_PLAIN
);
322 return NULL
; /* nothing is available */
325 /* Returns whether we are in the specified group or not */
327 we_are_in_group (gid_t gid
)
336 ngroups
= getgroups (0, NULL
);
337 if (ngroups
== -1 || ngroups
== 0)
340 groups
= g_new (gid_t
, ngroups
);
341 ngroups
= getgroups (ngroups
, groups
);
349 for (i
= 0; i
< ngroups
; i
++)
350 if (groups
[i
] == gid
)
357 /* Returns whether we can access the contents of directory specified by the file entry */
359 can_access_directory (file_entry
*fe
)
363 if (fe
->buf
.st_uid
== our_uid
)
364 test_mode
= S_IRUSR
| S_IXUSR
;
365 else if (we_are_in_group (fe
->buf
.st_gid
))
366 test_mode
= S_IRGRP
| S_IXGRP
;
368 test_mode
= S_IROTH
| S_IXOTH
;
370 return (fe
->buf
.st_mode
& test_mode
) == test_mode
;
373 /* This is the last resort for getting a file's icon. It uses the file mode
374 * bits or a hardcoded name.
377 get_default_icon (file_entry
*fe
)
379 mode_t mode
= fe
->buf
.st_mode
;
388 return iset_blockdev
;
394 return iset_executable
;
396 if (!strcmp (fe
->fname
, "core") || !strcmp (extension (fe
->fname
), "core"))
399 return iset_regular
; /* boooo */
403 * gicon_get_icon_for_file:
404 * @directory: The directory on which the file resides.
405 * @fe: The file entry that represents the file.
406 * @do_quick: Whether the function should try to use (expensive) metadata information.
408 * Returns the appropriate icon for the specified file.
410 * Return value: The icon for the specified file.
413 gicon_get_icon_for_file (char *directory
, file_entry
*fe
, gboolean do_quick
)
417 const char *mime_type
;
418 gboolean is_user_set
= FALSE
;
420 g_return_val_if_fail (directory
!= NULL
, NULL
);
421 g_return_val_if_fail (fe
!= NULL
, NULL
);
425 mode
= fe
->buf
.st_mode
;
427 /* 1. First try the user's settings */
429 if (!do_quick
|| we_can_afford_the_speed
) {
433 full_name
= g_concat_dir_and_file (directory
, fe
->fname
);
434 im
= get_icon_from_metadata (full_name
);
438 iset
= get_icon_set_from_image (im
);
445 /* 2. Before we do anything else, make sure the
446 * pointed-to file exists if a link */
448 if (S_ISLNK (mode
) && fe
->f
.stalled_link
) {
449 const char *icon_name
;
451 icon_name
= gnome_unconditional_pixmap_file ("gnome-warning.png");
453 iset
= get_icon_set (icon_name
);
459 /* 3. See if it is a directory */
461 if (S_ISDIR (mode
)) {
462 if (can_access_directory (fe
))
463 iset
= iset_directory
;
465 iset
= iset_dirclosed
;
470 /* 4. Try MIME-types */
474 full_name
= g_concat_dir_and_file (directory
, fe
->fname
);
475 mime_type
= gnome_mime_type_or_default_of_file (full_name
, NULL
);
479 mime_type
= gnome_mime_type_or_default (fe
->fname
, NULL
);
482 const char *icon_name
;
484 icon_name
= gnome_mime_get_value (mime_type
, "icon-filename");
486 iset
= get_icon_set (icon_name
);
492 /* 5. Get an icon from the file mode */
494 iset
= get_default_icon (fe
);
498 g_return_val_if_fail (iset
, NULL
);
500 if (S_ISLNK (mode
)) {
501 if (fe
->f
.link_to_dir
&& !is_user_set
)
502 iset
= iset_directory
;
504 if (fe
->f
.stalled_link
) {
505 ensure_icon_image (iset
, ICON_TYPE_STALLED
);
506 return iset
->stalled
;
508 ensure_icon_image (iset
, ICON_TYPE_SYMLINK
);
509 return iset
->symlink
;
512 ensure_icon_image (iset
, ICON_TYPE_PLAIN
);
518 * gicon_get_filename_for_icon:
519 * @image: An icon image loaded by the icon module.
521 * Queries the icon database for the icon filename that corresponds to the
524 * Return value: The filename that contains the icon for the specified image.
527 gicon_get_filename_for_icon (GdkImlibImage
*image
)
531 g_return_val_if_fail (image
!= NULL
, NULL
);
535 iset
= g_hash_table_lookup (image_hash
, image
);
536 g_assert (iset
!= NULL
);
537 return iset
->filename
;