r3815: Removed copy of GTK icon theme system. We now depend on GTK 2.4, which
[rox-filer.git] / ROX-Filer / src / type.c
blobf0c89608ef229400e609ee4b451cb115b5604bd1
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* type.c - code for dealing with filetypes */
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <time.h>
31 #include <sys/param.h>
32 #include <fnmatch.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
36 #ifdef WITH_GNOMEVFS
37 # include <libgnomevfs/gnome-vfs.h>
38 # include <libgnomevfs/gnome-vfs-mime.h>
39 # include <libgnomevfs/gnome-vfs-mime-handlers.h>
40 # include <libgnomevfs/gnome-vfs-application-registry.h>
41 #endif
43 #include "global.h"
45 #include "string.h"
46 #include "fscache.h"
47 #include "main.h"
48 #include "pixmaps.h"
49 #include "run.h"
50 #include "gui_support.h"
51 #include "choices.h"
52 #include "type.h"
53 #include "support.h"
54 #include "diritem.h"
55 #include "dnd.h"
56 #include "options.h"
57 #include "filer.h"
58 #include "action.h" /* (for action_chmod) */
59 #include "xml.h"
60 #include "dropbox.h"
62 #define TYPE_NS "http://www.freedesktop.org/standards/shared-mime-info"
63 enum {SET_MEDIA, SET_TYPE};
65 /* Colours for file types (same order as base types) */
66 static gchar *opt_type_colours[][2] = {
67 {"display_err_colour", "#ff0000"},
68 {"display_unkn_colour", "#000000"},
69 {"display_dir_colour", "#000080"},
70 {"display_pipe_colour", "#444444"},
71 {"display_sock_colour", "#ff00ff"},
72 {"display_file_colour", "#000000"},
73 {"display_cdev_colour", "#000000"},
74 {"display_bdev_colour", "#000000"},
75 {"display_door_colour", "#ff00ff"},
76 {"display_exec_colour", "#006000"},
77 {"display_adir_colour", "#006000"}
79 #define NUM_TYPE_COLOURS\
80 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
82 /* Parsed colours for file types */
83 static Option o_type_colours[NUM_TYPE_COLOURS];
84 static GdkColor type_colours[NUM_TYPE_COLOURS];
86 /* Static prototypes */
87 static void load_mime_types(void);
88 static void alloc_type_colours(void);
89 static void options_changed(void);
90 static char *get_action_save_path(GtkWidget *dialog);
91 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
92 static gboolean remove_handler_with_confirm(const guchar *path);
93 static void set_icon_theme(void);
94 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label);
96 /* When working out the type for a file, this hash table is checked
97 * first...
99 static GHashTable *literal_hash = NULL; /* name -> MIME-type */
101 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
102 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
103 * The hash table is consulted from each dot in the string in turn
104 * (First .ps.gz, then try .gz)
106 static GHashTable *extension_hash = NULL;
108 /* The first pattern in the list which matches is used */
109 typedef struct pattern {
110 gint len; /* Used for sorting */
111 gchar *glob;
112 MIME_type *type;
113 } Pattern;
114 static GPtrArray *glob_patterns = NULL; /* [Pattern] */
116 /* Hash of all allocated MIME types, indexed by "media/subtype".
117 * MIME_type structs are never freed; this table prevents memory leaks
118 * when rereading the config files.
120 static GHashTable *type_hash = NULL;
122 /* Most things on Unix are text files, so this is the default type */
123 MIME_type *text_plain;
124 MIME_type *inode_directory;
125 MIME_type *inode_mountpoint;
126 MIME_type *inode_pipe;
127 MIME_type *inode_socket;
128 MIME_type *inode_block_dev;
129 MIME_type *inode_char_dev;
130 MIME_type *application_executable;
131 MIME_type *inode_unknown;
132 MIME_type *inode_door;
134 static Option o_display_colour_types;
135 static Option o_icon_theme;
137 static GtkIconTheme *icon_theme = NULL;
139 void type_init(void)
141 int i;
143 icon_theme = gtk_icon_theme_new();
145 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
146 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
148 text_plain = get_mime_type("text/plain", TRUE);
149 inode_directory = get_mime_type("inode/directory", TRUE);
150 inode_mountpoint = get_mime_type("inode/mount-point", TRUE);
151 inode_pipe = get_mime_type("inode/fifo", TRUE);
152 inode_socket = get_mime_type("inode/socket", TRUE);
153 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
154 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
155 application_executable = get_mime_type("application/x-executable", TRUE);
156 inode_unknown = get_mime_type("inode/unknown", TRUE);
157 inode_door = get_mime_type("inode/door", TRUE);
159 load_mime_types();
161 option_add_string(&o_icon_theme, "icon_theme", "ROX");
162 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
163 option_register_widget("icon-theme-chooser", build_icon_theme);
165 for (i = 0; i < NUM_TYPE_COLOURS; i++)
166 option_add_string(&o_type_colours[i],
167 opt_type_colours[i][0],
168 opt_type_colours[i][1]);
169 alloc_type_colours();
171 set_icon_theme();
173 option_add_notify(options_changed);
176 /* Read-load all the glob patterns.
177 * Note: calls filer_update_all.
179 void reread_mime_files(void)
181 gtk_icon_theme_rescan_if_needed(icon_theme);
182 load_mime_types();
185 /* Returns the MIME_type structure for the given type name. It is looked
186 * up in type_hash and returned if found. If not found (and can_create is
187 * TRUE) then a new MIME_type is made, added to type_hash and returned.
188 * NULL is returned if type_name is not in type_hash and can_create is
189 * FALSE, or if type_name does not contain a '/' character.
191 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
193 MIME_type *mtype;
194 gchar *slash;
196 mtype = g_hash_table_lookup(type_hash, type_name);
197 if (mtype || !can_create)
198 return mtype;
200 slash = strchr(type_name, '/');
201 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
203 mtype = g_new(MIME_type, 1);
204 mtype->media_type = g_strndup(type_name, slash - type_name);
205 mtype->subtype = g_strdup(slash + 1);
206 mtype->image = NULL;
207 mtype->comment = NULL;
209 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
211 return mtype;
214 /* NULL if we don't know / don't support contents checking */
215 MIME_type *mime_type_from_contents(const char *path)
217 MIME_type *type = NULL;
218 #ifdef HAVE_GNOME_VFS
219 char *type_name;
221 type_name = gnome_vfs_get_mime_type(path);
222 if (type_name)
224 type = get_mime_type(type_name, TRUE);
225 free(type_name);
227 #endif
228 return type;
231 const char *basetype_name(DirItem *item)
233 if (item->flags & ITEM_FLAG_SYMLINK)
234 return _("Sym link");
235 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
236 return _("Mount point");
237 else if (item->flags & ITEM_FLAG_APPDIR)
238 return _("App dir");
240 switch (item->base_type)
242 case TYPE_FILE:
243 return _("File");
244 case TYPE_DIRECTORY:
245 return _("Dir");
246 case TYPE_CHAR_DEVICE:
247 return _("Char dev");
248 case TYPE_BLOCK_DEVICE:
249 return _("Block dev");
250 case TYPE_PIPE:
251 return _("Pipe");
252 case TYPE_SOCKET:
253 return _("Socket");
254 case TYPE_DOOR:
255 return _("Door");
258 return _("Unknown");
261 static void append_names(gpointer key, gpointer value, gpointer udata)
263 GList **list = (GList **) udata;
265 *list = g_list_prepend(*list, key);
268 /* Return list of all mime type names. Caller must free the list
269 * but NOT the strings it contains (which are never freed).
271 GList *mime_type_name_list(void)
273 GList *list = NULL;
275 g_hash_table_foreach(type_hash, append_names, &list);
276 list = g_list_sort(list, (GCompareFunc) strcmp);
278 return list;
281 /* MIME-type guessing */
283 /* Get the type of this file - stats the file and uses that if
284 * possible. For regular or missing files, uses the pathname.
286 MIME_type *type_get_type(const guchar *path)
288 DirItem *item;
289 MIME_type *type = NULL;
291 item = diritem_new("");
292 diritem_restat(path, item, NULL);
293 if (item->base_type != TYPE_ERROR)
294 type = item->mime_type;
295 diritem_free(item);
297 if (type)
298 return type;
300 type = type_from_path(path);
302 if (!type)
303 return text_plain;
305 return type;
308 /* Returns a pointer to the MIME-type.
309 * NULL if we can't think of anything.
311 MIME_type *type_from_path(const char *path)
313 const char *ext, *dot, *leafname;
314 char *lower;
315 MIME_type *type = NULL;
316 int i;
318 leafname = g_basename(path);
320 type = g_hash_table_lookup(literal_hash, leafname);
321 if (type)
322 return type;
323 lower = g_utf8_strdown(leafname, -1);
324 type = g_hash_table_lookup(literal_hash, lower);
325 if (type)
326 goto out;
328 ext = leafname;
330 while ((dot = strchr(ext, '.')))
332 ext = dot + 1;
334 type = g_hash_table_lookup(extension_hash, ext);
336 if (type)
337 goto out;
339 type = g_hash_table_lookup(extension_hash,
340 lower + (ext - leafname));
341 if (type)
342 goto out;
345 for (i = 0; i < glob_patterns->len; i++)
347 Pattern *p = glob_patterns->pdata[i];
349 if (fnmatch(p->glob, leafname, 0) == 0 ||
350 fnmatch(p->glob, lower, 0) == 0)
352 type = p->type;
353 goto out;
357 out:
358 g_free(lower);
360 return type;
363 /* Returns the file/dir in Choices for handling this type.
364 * NULL if there isn't one. g_free() the result.
366 static char *handler_for(MIME_type *type)
368 char *type_name;
369 char *open;
371 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
372 open = choices_find_path_load(type_name, "MIME-types");
373 g_free(type_name);
375 if (!open)
376 open = choices_find_path_load(type->media_type, "MIME-types");
378 return open;
381 MIME_type *mime_type_lookup(const char *type)
383 return get_mime_type(type, TRUE);
386 /* Actions for types */
388 gboolean type_open(const char *path, MIME_type *type)
390 gchar *argv[] = {NULL, NULL, NULL};
391 char *open;
392 gboolean retval;
393 struct stat info;
395 argv[1] = (char *) path;
397 open = handler_for(type);
398 if (!open)
399 return FALSE;
401 if (stat(open, &info))
403 report_error("stat(%s): %s", open, g_strerror(errno));
404 g_free(open);
405 return FALSE;
408 if (info.st_mode & S_IWOTH)
410 gchar *choices_dir;
411 GList *paths;
413 report_error(_("Executable '%s' is world-writeable! Refusing "
414 "to run. Please change the permissions now (this "
415 "problem may have been caused by a bug in earlier "
416 "versions of the filer).\n\n"
417 "Having (non-symlink) run actions world-writeable "
418 "means that other people who use your computer can "
419 "replace your run actions with malicious versions.\n\n"
420 "If you trust everyone who could write to these files "
421 "then you needn't worry. Otherwise, you should check, "
422 "or even just delete, all the existing run actions."),
423 open);
424 choices_dir = g_path_get_dirname(open);
425 paths = g_list_append(NULL, choices_dir);
426 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
427 g_free(choices_dir);
428 g_list_free(paths);
429 g_free(open);
430 return TRUE;
433 if (S_ISDIR(info.st_mode))
434 argv[0] = g_strconcat(open, "/AppRun", NULL);
435 else
436 argv[0] = open;
438 retval = rox_spawn(home_dir, (const gchar **) argv) != 0;
440 if (argv[0] != open)
441 g_free(argv[0]);
443 g_free(open);
445 return retval;
448 /* Return the image for this type, loading it if needed.
449 * Places to check are: (eg type="text_plain", base="text")
450 * 1. <Choices>/MIME-icons/base_subtype
451 * 2. Icon theme 'mime-base:subtype'
452 * 3. Icon theme 'mime-base'
453 * 4. Unknown type icon.
455 * Note: You must g_object_unref() the image afterwards.
457 MaskedPixmap *type_to_icon(MIME_type *type)
459 GdkPixbuf *full;
460 char *type_name, *path;
461 time_t now;
463 if (type == NULL)
465 g_object_ref(im_unknown);
466 return im_unknown;
469 now = time(NULL);
470 /* Already got an image? */
471 if (type->image)
473 /* Yes - don't recheck too often */
474 if (abs(now - type->image_time) < 2)
476 g_object_ref(type->image);
477 return type->image;
479 g_object_unref(type->image);
480 type->image = NULL;
483 type_name = g_strconcat(type->media_type, "_", type->subtype,
484 ".png", NULL);
485 path = choices_find_path_load(type_name, "MIME-icons");
486 g_free(type_name);
487 if (path)
489 type->image = g_fscache_lookup(pixmap_cache, path);
490 g_free(path);
493 if (type->image)
494 goto out;
496 type_name = g_strconcat("mime-", type->media_type, ":",
497 type->subtype, NULL);
498 full = gtk_icon_theme_load_icon(icon_theme, type_name, HUGE_HEIGHT,
499 0, NULL);
500 g_free(type_name);
501 if (!full)
503 /* Ugly hack... try for a GNOME icon */
504 type_name = g_strconcat("gnome-mime-", type->media_type,
505 "-", type->subtype, NULL);
506 full = gtk_icon_theme_load_icon(icon_theme,
507 type_name,
508 HUGE_HEIGHT, 0, NULL);
509 g_free(type_name);
511 if (!full)
513 /* Try for a media type */
514 type_name = g_strconcat("mime-", type->media_type, NULL);
515 full = gtk_icon_theme_load_icon(icon_theme,
516 type_name,
517 HUGE_HEIGHT, 0, NULL);
518 g_free(type_name);
520 if (full)
522 type->image = masked_pixmap_new(full);
523 g_object_unref(full);
526 out:
527 if (!type->image)
529 /* One ref from the type structure, one returned */
530 type->image = im_unknown;
531 g_object_ref(im_unknown);
534 type->image_time = now;
536 g_object_ref(type->image);
537 return type->image;
540 GdkAtom type_to_atom(MIME_type *type)
542 char *str;
543 GdkAtom retval;
545 g_return_val_if_fail(type != NULL, GDK_NONE);
547 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
548 retval = gdk_atom_intern(str, FALSE);
549 g_free(str);
551 return retval;
554 static void show_shell_help(gpointer data)
556 info_message(_("Enter a shell command which will load \"$@\" into "
557 "a suitable program. Eg:\n\n"
558 "gimp \"$@\""));
561 /* Called if the user clicks on the OK button. Returns FALSE if an error
562 * was displayed instead of performing the action.
564 static gboolean set_shell_action(GtkWidget *dialog)
566 GtkEntry *entry;
567 const guchar *command;
568 gchar *tmp, *path;
569 int error = 0, len;
570 int fd;
572 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
574 g_return_val_if_fail(entry != NULL, FALSE);
576 command = gtk_entry_get_text(entry);
578 if (!strchr(command, '$'))
580 show_shell_help(NULL);
581 return FALSE;
584 path = get_action_save_path(dialog);
585 if (!path)
586 return FALSE;
588 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
589 len = strlen(tmp);
591 fd = open(path, O_CREAT | O_WRONLY, 0755);
592 if (fd == -1)
593 error = errno;
594 else
596 FILE *file;
598 file = fdopen(fd, "w");
599 if (file)
601 if (fwrite(tmp, 1, len, file) < len)
602 error = errno;
603 if (fclose(file) && error == 0)
604 error = errno;
606 else
607 error = errno;
610 if (error)
611 report_error(g_strerror(error));
613 g_free(tmp);
614 g_free(path);
616 gtk_widget_destroy(dialog);
618 return TRUE;
621 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
623 if (response == GTK_RESPONSE_OK)
624 if (!set_shell_action(dialog))
625 return;
626 gtk_widget_destroy(dialog);
629 /* Return the path of the file in choices that handles this type and
630 * radio setting.
631 * NULL if nothing is defined for it.
633 static guchar *handler_for_radios(GObject *dialog)
635 Radios *radios;
636 MIME_type *type;
638 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
639 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
641 g_return_val_if_fail(radios != NULL, NULL);
642 g_return_val_if_fail(type != NULL, NULL);
644 switch (radios_get_value(radios))
646 case SET_MEDIA:
647 return choices_find_path_load(type->media_type,
648 "MIME-types");
649 case SET_TYPE:
651 gchar *tmp, *handler;
652 tmp = g_strconcat(type->media_type, "_",
653 type->subtype, NULL);
654 handler = choices_find_path_load(tmp, "MIME-types");
655 g_free(tmp);
656 return handler;
658 default:
659 g_warning("Bad type");
660 return NULL;
664 static void run_action_update(gpointer data)
666 guchar *handler;
667 DropBox *drop_box;
668 GObject *dialog = G_OBJECT(data);
670 drop_box = g_object_get_data(dialog, "rox-dropbox");
672 g_return_if_fail(drop_box != NULL);
674 handler = handler_for_radios(dialog);
676 if (handler)
678 char *old = handler;
680 handler = readlink_dup(old);
681 if (handler)
682 g_free(old);
683 else
684 handler = old;
687 drop_box_set_path(DROP_BOX(drop_box), handler);
688 g_free(handler);
691 static void clear_run_action(GtkWidget *drop_box, GtkWidget *dialog)
693 guchar *handler;
695 handler = handler_for_radios(G_OBJECT(dialog));
697 if (handler)
698 remove_handler_with_confirm(handler);
700 run_action_update(dialog);
703 /* Called when a URI list is dropped onto the box in the Set Run Action
704 * dialog. Make sure it's an application, and make that the default
705 * handler.
707 static void drag_app_dropped(GtkWidget *drop_box,
708 const guchar *app,
709 GtkWidget *dialog)
711 DirItem *item;
713 item = diritem_new("");
714 diritem_restat(app, item, NULL);
715 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
717 guchar *path;
719 path = get_action_save_path(dialog);
721 if (path)
723 if (symlink(app, path))
724 delayed_error("symlink: %s",
725 g_strerror(errno));
726 else
727 destroy_on_idle(dialog);
729 g_free(path);
732 else
733 delayed_error(
734 _("This is not a program! Give me an application "
735 "instead!"));
737 diritem_free(item);
740 /* Find the current command which is used to run files of this type.
741 * Returns NULL on failure. g_free() the result.
743 static guchar *get_current_command(MIME_type *type)
745 struct stat info;
746 char *handler, *nl, *data = NULL;
747 long len;
748 guchar *command = NULL;
750 handler = handler_for(type);
752 if (!handler)
753 return NULL; /* No current handler */
755 if (stat(handler, &info))
756 goto out; /* Can't stat */
758 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
759 goto out; /* Only use small regular files */
761 if (!load_file(handler, &data, &len))
762 goto out; /* Didn't load OK */
764 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
765 goto out; /* Not one of ours */
767 nl = strchr(data + 16, '\n');
768 if (!nl)
769 goto out; /* No newline! */
771 command = g_strndup(data + 16, nl - data - 16);
772 out:
773 g_free(handler);
774 g_free(data);
775 return command;
778 /* Find the current command which is used to run files of this type,
779 * and return a textual description of it.
780 * g_free() the result.
782 gchar *describe_current_command(MIME_type *type)
784 char *handler;
785 char *desc = NULL;
786 struct stat info;
787 char *target;
789 g_return_val_if_fail(type != NULL, NULL);
791 if (type == application_executable)
792 return g_strdup(_("Execute file"));
794 handler = handler_for(type);
796 if (!handler)
797 return g_strdup(_("No run action defined"));
799 target = readlink_dup(handler);
800 if (target)
802 /* Cope with relative paths (shouldn't normally be needed) */
804 if (target[0] == '/')
806 g_free(handler);
807 handler = target;
809 else
811 gchar *dir;
813 dir = g_path_get_dirname(handler);
814 g_free(handler);
815 handler = g_strconcat(dir, "/", target, NULL);
816 g_free(target);
817 g_free(dir);
821 if (mc_stat(handler, &info) !=0 )
823 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
824 g_strerror(errno));
825 goto out;
828 if (S_ISDIR(info.st_mode))
830 const guchar *tmp;
831 uid_t dir_uid = info.st_uid;
833 tmp = make_path(handler, "AppRun");
835 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
836 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
837 desc = g_strdup_printf(
838 _("Invalid application %s (bad AppRun)"),
839 handler);
840 /* Else, just report handler... */
842 goto out;
845 /* It's not an application directory, and it's not a symlink... */
847 if (access(handler, X_OK) != 0)
849 desc = g_strdup_printf(_("Non-executable %s"), handler);
850 goto out;
853 desc = get_current_command(type);
854 out:
855 if (!desc)
856 desc = handler;
857 else
858 g_free(handler);
860 return desc;
863 /* Display a dialog box allowing the user to set the default run action
864 * for this type.
866 void type_set_handler_dialog(MIME_type *type)
868 guchar *tmp;
869 GtkDialog *dialog;
870 GtkWidget *frame, *entry, *label, *button;
871 GtkWidget *hbox;
872 Radios *radios;
874 g_return_if_fail(type != NULL);
876 dialog = GTK_DIALOG(gtk_dialog_new());
877 gtk_dialog_set_has_separator(dialog, FALSE);
878 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
880 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
882 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
884 radios = radios_new(run_action_update, dialog);
885 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
887 radios_add(radios,
888 _("If a handler for the specific type isn't set up, "
889 "use this as the default."), SET_MEDIA,
890 _("Set default for all `%s/<anything>'"),
891 type->media_type);
893 radios_add(radios,
894 _("Use this application for all files with this MIME "
895 "type."), SET_TYPE,
896 _("Only for the type `%s' (%s/%s)"),
897 mime_type_comment(type),
898 type->media_type, type->subtype);
900 radios_set_value(radios, SET_TYPE);
902 frame = drop_box_new(_("Drop a suitable application here"));
904 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
906 radios_pack(radios, GTK_BOX(dialog->vbox));
907 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 0);
909 g_signal_connect(frame, "path_dropped",
910 G_CALLBACK(drag_app_dropped), dialog);
911 g_signal_connect(frame, "clear",
912 G_CALLBACK(clear_run_action), dialog);
914 hbox = gtk_hbox_new(FALSE, 4);
915 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
916 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
917 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
918 FALSE, TRUE, 0);
919 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
921 hbox = gtk_hbox_new(FALSE, 4);
922 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
924 label = gtk_label_new(_("Enter a shell command:")),
925 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
926 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
928 gtk_box_pack_start(GTK_BOX(hbox),
929 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
931 entry = gtk_entry_new();
932 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
933 gtk_widget_grab_focus(entry);
934 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
935 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
937 /* If possible, fill in the entry box with the current command */
938 tmp = get_current_command(type);
939 if (tmp)
941 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
942 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
943 g_free(tmp);
945 else
947 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
948 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
951 gtk_dialog_add_button(dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL);
953 button = button_new_mixed(GTK_STOCK_OK, _("_Use Command"));
954 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
955 gtk_dialog_add_action_widget(dialog, button, GTK_RESPONSE_OK);
957 hbox = gtk_hbox_new(TRUE, 4);
958 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
960 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
962 g_signal_connect(dialog, "response",
963 G_CALLBACK(set_action_response), NULL);
965 gtk_widget_show_all(GTK_WIDGET(dialog));
968 /* path is an entry in Choices. If it's a symlink or a very small executable
969 * then just get rid of it, otherwise confirm first. It it doesn't exist,
970 * do nothing.
972 * FALSE on error (abort operation).
974 static gboolean remove_handler_with_confirm(const guchar *path)
976 struct stat info;
978 if (lstat(path, &info) == 0)
980 /* A binding already exists... */
981 if (S_ISREG(info.st_mode) && info.st_size > 256)
983 if (!confirm(_("A run action already exists and is "
984 "quite a big program - are you sure "
985 "you want to delete it?"),
986 GTK_STOCK_DELETE, NULL))
988 return FALSE;
992 if (unlink(path))
994 report_error(_("Can't remove %s: %s"),
995 path, g_strerror(errno));
996 return FALSE;
1000 return TRUE;
1003 /* The user wants to set a new default action for files of this type (or just
1004 * clear the action). Removes the current binding if possible and returns the
1005 * path to save the new one to. NULL means cancel. g_free() the result.
1007 static char *get_action_save_path(GtkWidget *dialog)
1009 guchar *path = NULL;
1010 guchar *type_name = NULL;
1011 MIME_type *type;
1012 Radios *radios;
1014 g_return_val_if_fail(dialog != NULL, NULL);
1016 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
1017 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
1019 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
1021 if (radios_get_value(radios) == SET_MEDIA)
1022 type_name = g_strdup(type->media_type);
1023 else
1024 type_name = g_strconcat(type->media_type, "_",
1025 type->subtype, NULL);
1027 path = choices_find_path_save("", PROJECT, FALSE);
1028 if (!path)
1030 report_error(
1031 _("Choices saving is disabled by CHOICESPATH variable"));
1032 goto out;
1034 g_free(path);
1036 path = choices_find_path_save(type_name, "MIME-types", TRUE);
1038 if (!remove_handler_with_confirm(path))
1039 null_g_free(&path);
1040 out:
1041 g_free(type_name);
1042 return path;
1045 MIME_type *mime_type_from_base_type(int base_type)
1047 switch (base_type)
1049 case TYPE_FILE:
1050 return text_plain;
1051 case TYPE_DIRECTORY:
1052 return inode_directory;
1053 case TYPE_PIPE:
1054 return inode_pipe;
1055 case TYPE_SOCKET:
1056 return inode_socket;
1057 case TYPE_BLOCK_DEVICE:
1058 return inode_block_dev;
1059 case TYPE_CHAR_DEVICE:
1060 return inode_char_dev;
1061 case TYPE_DOOR:
1062 return inode_door;
1064 return inode_unknown;
1067 /* Takes the st_mode field from stat() and returns the base type.
1068 * Should not be a symlink.
1070 int mode_to_base_type(int st_mode)
1072 if (S_ISREG(st_mode))
1073 return TYPE_FILE;
1074 else if (S_ISDIR(st_mode))
1075 return TYPE_DIRECTORY;
1076 else if (S_ISBLK(st_mode))
1077 return TYPE_BLOCK_DEVICE;
1078 else if (S_ISCHR(st_mode))
1079 return TYPE_CHAR_DEVICE;
1080 else if (S_ISFIFO(st_mode))
1081 return TYPE_PIPE;
1082 else if (S_ISSOCK(st_mode))
1083 return TYPE_SOCKET;
1084 else if (S_ISDOOR(st_mode))
1085 return TYPE_DOOR;
1087 return TYPE_ERROR;
1090 /* Returns TRUE is this is something that is run by looking up its type
1091 * in MIME-types and, hence, can have its run action set.
1093 gboolean can_set_run_action(DirItem *item)
1095 g_return_val_if_fail(item != NULL, FALSE);
1097 return item->base_type == TYPE_FILE &&
1098 !(item->mime_type == application_executable);
1101 /* Parse file type colours and allocate/free them as necessary */
1102 static void alloc_type_colours(void)
1104 gboolean success[NUM_TYPE_COLOURS];
1105 int change_count = 0; /* No. needing realloc */
1106 int i;
1107 static gboolean allocated = FALSE;
1109 /* Parse colours */
1110 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1112 GdkColor *c = &type_colours[i];
1113 gushort r = c->red;
1114 gushort g = c->green;
1115 gushort b = c->blue;
1117 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1119 if (allocated && (c->red != r || c->green != g || c->blue != b))
1120 change_count++;
1123 /* Free colours if they were previously allocated and
1124 * have changed or become unneeded.
1126 if (allocated && (change_count || !o_display_colour_types.int_value))
1128 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1129 type_colours, NUM_TYPE_COLOURS);
1130 allocated = FALSE;
1133 /* Allocate colours, unless they are still allocated (=> they didn't
1134 * change) or we don't want them anymore.
1135 * XXX: what should be done if allocation fails?
1137 if (!allocated && o_display_colour_types.int_value)
1139 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1140 type_colours, NUM_TYPE_COLOURS,
1141 FALSE, TRUE, success);
1142 allocated = TRUE;
1146 static void expire_timer(gpointer key, gpointer value, gpointer data)
1148 MIME_type *type = value;
1150 type->image_time = 0;
1153 static void options_changed(void)
1155 alloc_type_colours();
1156 if (o_icon_theme.has_changed)
1158 set_icon_theme();
1159 g_hash_table_foreach(type_hash, expire_timer, NULL);
1160 full_refresh();
1164 /* Return a pointer to a (static) colour for this item. If colouring is
1165 * off, returns normal.
1167 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1169 int type = item->base_type;
1171 if (!o_display_colour_types.int_value)
1172 return normal;
1174 if (item->flags & ITEM_FLAG_EXEC_FILE &&
1175 item->mime_type == application_executable)
1176 type = TYPE_EXEC;
1177 else if (item->flags & ITEM_FLAG_APPDIR)
1178 type = TYPE_APPDIR;
1180 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1182 return &type_colours[type];
1185 /* Process the 'Patterns' value */
1186 static void add_pattern(MIME_type *type, const char *pattern, GHashTable *globs)
1188 if (pattern[0] == '*' && pattern[1] == '.' &&
1189 strpbrk(pattern + 2, "*?[") == NULL)
1191 g_hash_table_insert(extension_hash,
1192 g_strdup(pattern + 2),
1193 type);
1195 else if (strpbrk(pattern, "*?[") == NULL)
1196 g_hash_table_insert(literal_hash, g_strdup(pattern), type);
1197 else
1198 g_hash_table_insert(globs, g_strdup(pattern), type);
1201 /* Load and parse this file. literal_hash and extension_hash are updated
1202 * directly. Other patterns are added to 'globs'.
1204 static void import_file(const gchar *file, GHashTable *globs)
1206 MIME_type *type = NULL;
1207 GError *error = NULL;
1208 gchar *data, *line;
1210 if (access(file, F_OK) != 0)
1211 return; /* Doesn't exist. No problem. */
1213 if (!g_file_get_contents(file, &data, NULL, &error))
1215 delayed_error(_("Error loading MIME database:\n%s"),
1216 error->message);
1217 g_error_free(error);
1218 return;
1221 line = data;
1223 while (line && *line)
1225 char *nl;
1227 nl = strchr(line, '\n');
1228 if (!nl)
1229 break;
1230 *nl = '\0';
1232 if (*line != '#')
1234 const gchar *colon;
1235 gchar *name;
1237 colon = strchr(line, ':');
1238 if (!colon)
1240 delayed_error(_("File '%s' corrupted!"), file);
1241 break;
1244 name = g_strndup(line, colon - line);
1245 type = get_mime_type(name, TRUE);
1246 g_free(name);
1247 if (!type)
1248 g_warning("Invalid type in '%s'", file);
1250 add_pattern(type, colon + 1, globs);
1253 line = nl + 1;
1256 g_free(data);
1259 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1261 Pattern *pattern;
1263 pattern = g_new(Pattern, 1);
1264 pattern->glob = g_strdup((gchar *) key);
1265 pattern->type = (MIME_type *) value;
1266 pattern->len = strlen(pattern->glob);
1268 g_ptr_array_add(glob_patterns, pattern);
1271 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1273 const Pattern *pa = *(const Pattern **) a;
1274 const Pattern *pb = *(const Pattern **) b;
1276 if (pa->len > pb->len)
1277 return -1;
1278 else if (pa->len == pb->len)
1279 return 0;
1280 return 1;
1283 static char **get_xdg_data_dirs(int *n_dirs)
1285 const char *env;
1286 char **dirs;
1287 int i, n;
1289 env = getenv("XDG_DATA_DIRS");
1290 if (!env)
1291 env = "/usr/local/share/:/usr/share/";
1292 dirs = g_strsplit(env, ":", 0);
1293 g_return_val_if_fail(dirs != NULL, NULL);
1294 for (n = 0; dirs[n]; n++)
1296 for (i = n; i > 0; i--)
1297 dirs[i] = dirs[i - 1];
1298 env = getenv("XDG_DATA_HOME");
1299 if (env)
1300 dirs[0] = g_strdup(env);
1301 else
1302 dirs[0] = g_build_filename(g_get_home_dir(), ".local",
1303 "share", NULL);
1304 *n_dirs = n + 1;
1305 return dirs;
1308 /* Clear all currently stored information and re-read everything.
1309 * Note: calls filer_update_all.
1311 static void load_mime_types(void)
1313 GHashTable *globs;
1314 char **dirs;
1315 int n_dirs;
1316 int i;
1318 dirs = get_xdg_data_dirs(&n_dirs);
1319 g_return_if_fail(dirs != NULL);
1322 struct stat info;
1323 if (lstat(make_path(home_dir, ".mime"), &info) == 0 &&
1324 S_ISDIR(info.st_mode))
1326 delayed_error(_("The ~/.mime directory has moved. "
1327 "It should now be ~/.local/share/mime. You "
1328 "should move it there (and make a symlink "
1329 "from ~/.mime to it for older applications)."));
1333 if (!glob_patterns)
1334 glob_patterns = g_ptr_array_new();
1335 else
1337 int i;
1339 for (i = glob_patterns->len - 1; i >= 0; i--)
1341 Pattern *p = glob_patterns->pdata[i];
1342 g_free(p->glob);
1343 g_free(p);
1345 g_ptr_array_set_size(glob_patterns, 0);
1348 if (literal_hash)
1349 g_hash_table_destroy(literal_hash);
1350 if (extension_hash)
1351 g_hash_table_destroy(extension_hash);
1352 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1353 g_free, NULL);
1354 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1355 g_free, NULL);
1356 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1358 for (i = n_dirs - 1; i >= 0; i--)
1360 char *path;
1361 path = g_build_filename(dirs[i], "mime", "globs", NULL);
1362 import_file(path, globs);
1363 g_free(path);
1364 g_free(dirs[i]);
1366 g_free(dirs);
1368 /* Turn the globs hash into a pointer array */
1369 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1370 g_hash_table_destroy(globs);
1371 globs = NULL;
1373 if (glob_patterns->len)
1374 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1376 if (g_hash_table_size(extension_hash) == 0)
1378 delayed_error(_("The standard MIME type database "
1379 "(version 0.9 or later) was not found. "
1380 "The filer will probably not show the correct "
1381 "types for different files. You should download and "
1382 "install the 'shared-mime-info-0.9' package from "
1383 "here:\n"
1384 "http://www.freedesktop.org/software/shared-mime-info\n\n"
1385 "If you have already installed this package, check that the "
1386 "permissions allow the files to be read (check "
1387 "/usr/local/share/mime/globs or /usr/share/mime/globs)."));
1390 filer_update_all();
1393 /* Try to fill in 'type->comment' from this document */
1394 static void get_comment(MIME_type *type, const guchar *path)
1396 xmlNode *node;
1397 XMLwrapper *doc;
1399 doc = xml_cache_load(path);
1400 if (!doc)
1401 return;
1403 node = xml_get_section(doc, TYPE_NS, "comment");
1405 if (node)
1407 char *val;
1408 g_return_if_fail(type->comment == NULL);
1409 val= xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
1410 type->comment = g_strdup(val);
1411 xmlFree(val);
1414 g_object_unref(doc);
1417 /* Fill in the comment field for this MIME type */
1418 static void find_comment(MIME_type *type)
1420 char **dirs;
1421 int i, n_dirs;
1423 if (type->comment)
1425 g_free(type->comment);
1426 type->comment = NULL;
1429 dirs = get_xdg_data_dirs(&n_dirs);
1430 g_return_if_fail(dirs != NULL);
1432 for (i = 0; i < n_dirs; i++)
1434 guchar *path;
1436 path = g_strdup_printf("%s/mime/%s/%s.xml", dirs[i],
1437 type->media_type, type->subtype);
1438 get_comment(type, path);
1439 g_free(path);
1440 if (type->comment)
1441 break;
1444 if (!type->comment)
1445 type->comment = g_strdup_printf("%s/%s", type->media_type,
1446 type->subtype);
1448 for (i = 0; i < n_dirs; i++)
1449 g_free(dirs[i]);
1450 g_free(dirs);
1453 const char *mime_type_comment(MIME_type *type)
1455 if (!type->comment)
1456 find_comment(type);
1458 return type->comment;
1461 static void set_icon_theme(void)
1463 GtkIconInfo *info;
1464 char *icon_home;
1465 const char *theme_name = o_icon_theme.value;
1467 if (!theme_name || !*theme_name)
1468 theme_name = "ROX";
1470 while (1)
1472 gtk_icon_theme_set_custom_theme(icon_theme, theme_name);
1473 info = gtk_icon_theme_lookup_icon(icon_theme,
1474 "mime-application:postscript",
1475 ICON_HEIGHT, 0);
1476 if (!info)
1478 info = gtk_icon_theme_lookup_icon(icon_theme,
1479 "gnome-mime-application-postscript",
1480 ICON_HEIGHT, 0);
1482 if (info)
1484 gtk_icon_info_free(info);
1485 return;
1488 if (strcmp(theme_name, "ROX") == 0)
1489 break;
1491 delayed_error(_("Icon theme '%s' does not contain MIME icons. "
1492 "Using ROX default theme instead."),
1493 theme_name);
1495 theme_name = "ROX";
1498 icon_home = g_build_filename(home_dir, ".icons", NULL);
1499 if (!file_exists(icon_home))
1500 mkdir(icon_home, 0755);
1501 g_free(icon_home);
1503 icon_home = g_build_filename(home_dir, ".icons", "ROX", NULL);
1504 if (symlink(make_path(app_dir, "ROX"), icon_home))
1505 delayed_error(_("Failed to create symlink '%s':\n%s\n\n"
1506 "(this may mean that the ROX theme already exists there, but "
1507 "the 'mime-application:postscript' icon couldn't be loaded for "
1508 "some reason)"), icon_home, g_strerror(errno));
1509 g_free(icon_home);
1511 gtk_icon_theme_rescan_if_needed(icon_theme);
1514 static guchar *read_theme(Option *option)
1516 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1517 GtkLabel *item;
1519 item = GTK_LABEL(GTK_BIN(om)->child);
1521 g_return_val_if_fail(item != NULL, g_strdup("ROX"));
1523 return g_strdup(gtk_label_get_text(item));
1526 static void update_theme(Option *option)
1528 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1529 GtkWidget *menu;
1530 GList *kids, *next;
1531 int i = 0;
1533 menu = gtk_option_menu_get_menu(om);
1535 kids = gtk_container_get_children(GTK_CONTAINER(menu));
1536 for (next = kids; next; next = next->next, i++)
1538 GtkLabel *item = GTK_LABEL(GTK_BIN(next->data)->child);
1539 const gchar *label;
1541 /* The label actually moves from the menu!! */
1542 if (!item)
1543 item = GTK_LABEL(GTK_BIN(om)->child);
1545 label = gtk_label_get_text(item);
1547 g_return_if_fail(label != NULL);
1549 if (strcmp(label, option->value) == 0)
1550 break;
1552 g_list_free(kids);
1554 if (next)
1555 gtk_option_menu_set_history(om, i);
1556 else
1557 g_warning("Theme '%s' not found", option->value);
1560 static void add_themes_from_dir(GPtrArray *names, const char *dir)
1562 GPtrArray *list;
1563 int i;
1565 if (access(dir, F_OK) != 0)
1566 return;
1568 list = list_dir(dir);
1569 g_return_if_fail(list != NULL);
1571 for (i = 0; i < list->len; i++)
1573 char *index_path;
1575 index_path = g_build_filename(dir, list->pdata[i],
1576 "index.theme", NULL);
1578 if (access(index_path, F_OK) == 0)
1579 g_ptr_array_add(names, list->pdata[i]);
1580 else
1581 g_free(list->pdata[i]);
1583 g_free(index_path);
1586 g_ptr_array_free(list, TRUE);
1589 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label)
1591 GtkWidget *button, *menu, *hbox;
1592 GPtrArray *names;
1593 gchar **theme_dirs = NULL;
1594 gint n_dirs = 0;
1595 int i;
1597 g_return_val_if_fail(option != NULL, NULL);
1598 g_return_val_if_fail(label != NULL, NULL);
1600 hbox = gtk_hbox_new(FALSE, 4);
1602 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(label)),
1603 FALSE, TRUE, 0);
1605 button = gtk_option_menu_new();
1606 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
1608 menu = gtk_menu_new();
1609 gtk_option_menu_set_menu(GTK_OPTION_MENU(button), menu);
1611 gtk_icon_theme_get_search_path(icon_theme, &theme_dirs, &n_dirs);
1612 names = g_ptr_array_new();
1613 for (i = 0; i < n_dirs; i++)
1614 add_themes_from_dir(names, theme_dirs[i]);
1615 g_strfreev(theme_dirs);
1617 g_ptr_array_sort(names, strcmp2);
1619 for (i = 0; i < names->len; i++)
1621 GtkWidget *item;
1622 char *name = names->pdata[i];
1624 item = gtk_menu_item_new_with_label(name);
1625 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1626 gtk_widget_show_all(item);
1628 g_free(name);
1631 g_ptr_array_free(names, TRUE);
1633 option->update_widget = update_theme;
1634 option->read_widget = read_theme;
1635 option->widget = button;
1637 gtk_signal_connect_object(GTK_OBJECT(button), "changed",
1638 GTK_SIGNAL_FUNC(option_check_widget),
1639 (GtkObject *) option);
1641 return g_list_append(NULL, hbox);