De-fuzzyed some msgs...
[midnight-commander.git] / gnome / gicon.c
blobdd228c80ff9fd5ea4f43d0a3a36262edf834f37e
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>
7 */
9 #include <config.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include "util.h"
14 #include "dialog.h"
16 #include <gnome.h>
17 #include "gicon.h"
18 #include "gmain.h"
21 /* What kinds of images can an icon set contain */
22 typedef enum {
23 ICON_TYPE_PLAIN,
24 ICON_TYPE_SYMLINK,
25 ICON_TYPE_STALLED
26 } IconType;
28 /* Information for an icon set (plain icon plus overlayed versions) */
29 typedef struct {
30 GdkImlibImage *plain; /* Plain version */
31 GdkImlibImage *symlink; /* Symlink version */
32 GdkImlibImage *stalled; /* Stalled symlink version */
33 char *filename; /* Filename for the plain image */
34 } IconSet;
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 */
56 static uid_t our_uid;
57 static gid_t our_gid;
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)
67 int rowstride;
68 int overlay_rowstride;
69 guchar *src, *dest;
70 int y;
71 GdkImlibImage *im;
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);
88 dest += rowstride;
89 src += overlay_rowstride;
92 gdk_imlib_changed_image (im);
93 return im;
96 /* Ensures that the icon set has the requested image */
97 static void
98 ensure_icon_image (IconSet *iset, IconType type)
100 g_assert (iset->plain != NULL);
102 switch (type) {
103 case ICON_TYPE_PLAIN:
104 /* The plain type always exists, so do nothing */
105 break;
107 case ICON_TYPE_SYMLINK:
108 iset->symlink = build_overlay (iset->plain, symlink_overlay);
109 g_hash_table_insert (image_hash, iset->symlink, iset);
110 break;
112 case ICON_TYPE_STALLED:
113 iset->stalled = build_overlay (iset->plain, stalled_overlay);
114 g_hash_table_insert (image_hash, iset->stalled, iset);
115 break;
117 default:
118 g_assert_not_reached ();
122 static void
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) {
129 *nheight = height;
130 *nwidth = width;
131 } else if (width < height) {
132 *nheight = ICON_IMAGE_HEIGHT;
133 *nwidth = *nheight * width / height;
134 } else {
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)
144 GdkImlibImage *im;
145 int width, height;
147 g_return_val_if_fail (orig != NULL, NULL);
149 compute_scaled_size (orig->rgb_width, orig->rgb_height,
150 &width, &height);
151 im = gdk_imlib_clone_scaled_image (orig, width, height);
153 return im;
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_. */
159 static IconSet *
160 get_icon_set_from_image (GdkImlibImage *im)
162 IconSet *iset;
164 g_return_val_if_fail (im != NULL, NULL);
166 iset = g_hash_table_lookup (image_hash, im);
167 if (iset)
168 return iset;
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);
184 return iset;
187 /* Returns the icon set corresponding to the specified icon filename, or NULL if
188 * the file could not be loaded.
190 static IconSet *
191 get_icon_set (const char *filename)
193 GdkImlibImage *im;
194 IconSet *iset;
196 iset = g_hash_table_lookup (name_hash, filename);
197 if (iset)
198 return iset;
200 im = gdk_imlib_load_image ((char *) filename);
201 if (!im)
202 return NULL;
204 iset = get_icon_set_from_image (im);
205 im = NULL;
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);
213 return iset;
216 /* Convenience function to load one of the default icons and die if this fails */
217 static IconSet *
218 get_stock_icon (char *name)
220 char *filename;
221 IconSet *iset;
223 filename = g_concat_dir_and_file (ICONDIR, name);
224 iset = get_icon_set (filename);
225 g_free (filename);
227 return iset;
230 /* Convenience function to load one of the default overlays and die if this fails */
231 static GdkImlibImage *
232 get_stock_overlay (char *name)
234 char *filename;
235 GdkImlibImage *im;
237 filename = g_concat_dir_and_file (ICONDIR, name);
238 im = gdk_imlib_load_image (filename);
239 g_free (filename);
241 return im;
246 * gicon_init:
247 * @void:
249 * Initializes the icon module.
251 void
252 gicon_init (void)
254 if (gicon_inited)
255 return;
257 gicon_inited = TRUE;
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"));
283 exit(1);
286 our_uid = getuid ();
287 our_gid = getgid ();
290 /* Tries to get an icon from the file's metadata information */
291 static GdkImlibImage *
292 get_icon_from_metadata (char *filename)
294 int size;
295 char *buf;
296 IconSet *iset = NULL;
298 /* Try the inlined icon */
300 if (gnome_metadata_get (filename, "icon-inline-png", &size, &buf) == 0) {
301 GdkImlibImage *im;
302 im = gdk_imlib_inlined_png_to_image (buf, size);
303 g_free (buf);
304 if (im) {
305 iset = get_icon_set_from_image (im);
306 im = NULL;
310 /* Try the non-inlined icon */
312 if (!iset && gnome_metadata_get (filename, "icon-filename", &size, &buf) == 0) {
313 iset = get_icon_set (buf);
314 g_free (buf);
317 if (iset) {
318 ensure_icon_image (iset, ICON_TYPE_PLAIN);
319 return iset->plain;
322 return NULL; /* nothing is available */
325 /* Returns whether we are in the specified group or not */
326 static int
327 we_are_in_group (gid_t gid)
329 gid_t *groups;
330 int ngroups, i;
331 int retval;
333 if (our_gid == gid)
334 return TRUE;
336 ngroups = getgroups (0, NULL);
337 if (ngroups == -1 || ngroups == 0)
338 return FALSE;
340 groups = g_new (gid_t, ngroups);
341 ngroups = getgroups (ngroups, groups);
342 if (ngroups == -1) {
343 g_free (groups);
344 return FALSE;
347 retval = FALSE;
349 for (i = 0; i < ngroups; i++)
350 if (groups[i] == gid)
351 retval = TRUE;
353 g_free (groups);
354 return retval;
357 /* Returns whether we can access the contents of directory specified by the file entry */
358 static int
359 can_access_directory (file_entry *fe)
361 mode_t test_mode;
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;
367 else
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.
376 static IconSet *
377 get_default_icon (file_entry *fe)
379 mode_t mode = fe->buf.st_mode;
381 if (S_ISSOCK (mode))
382 return iset_sock;
384 if (S_ISCHR (mode))
385 return iset_chardev;
387 if (S_ISBLK (mode))
388 return iset_blockdev;
390 if (S_ISFIFO (mode))
391 return iset_fifo;
393 if (is_exe (mode))
394 return iset_executable;
396 if (!strcmp (fe->fname, "core") || !strcmp (extension (fe->fname), "core"))
397 return iset_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.
412 GdkImlibImage *
413 gicon_get_icon_for_file (char *directory, file_entry *fe, gboolean do_quick)
415 IconSet *iset;
416 mode_t mode;
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);
423 gicon_init ();
425 mode = fe->buf.st_mode;
427 /* 1. First try the user's settings */
429 if (!do_quick || we_can_afford_the_speed) {
430 char *full_name;
431 GdkImlibImage *im;
433 full_name = g_concat_dir_and_file (directory, fe->fname);
434 im = get_icon_from_metadata (full_name);
435 g_free (full_name);
437 if (im) {
438 iset = get_icon_set_from_image (im);
439 im = NULL;
440 is_user_set = TRUE;
441 goto add_link;
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");
452 if (icon_name) {
453 iset = get_icon_set (icon_name);
454 if (iset)
455 goto add_link;
459 /* 3. See if it is a directory */
461 if (S_ISDIR (mode)) {
462 if (can_access_directory (fe))
463 iset = iset_directory;
464 else
465 iset = iset_dirclosed;
467 goto add_link;
470 /* 4. Try MIME-types */
472 if (use_magic) {
473 char *full_name;
474 full_name = g_concat_dir_and_file (directory, fe->fname);
475 mime_type = gnome_mime_type_or_default_of_file (full_name, NULL);
476 g_free (full_name);
478 else
479 mime_type = gnome_mime_type_or_default (fe->fname, NULL);
481 if (mime_type) {
482 const char *icon_name;
484 icon_name = gnome_mime_get_value (mime_type, "icon-filename");
485 if (icon_name) {
486 iset = get_icon_set (icon_name);
487 if (iset)
488 goto add_link;
492 /* 5. Get an icon from the file mode */
494 iset = get_default_icon (fe);
496 add_link:
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;
507 } else {
508 ensure_icon_image (iset, ICON_TYPE_SYMLINK);
509 return iset->symlink;
511 } else {
512 ensure_icon_image (iset, ICON_TYPE_PLAIN);
513 return iset->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
522 * specified image.
524 * Return value: The filename that contains the icon for the specified image.
526 const char *
527 gicon_get_filename_for_icon (GdkImlibImage *image)
529 IconSet *iset;
531 g_return_val_if_fail (image != NULL, NULL);
533 gicon_init ();
535 iset = g_hash_table_lookup (image_hash, image);
536 g_assert (iset != NULL);
537 return iset->filename;