4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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)
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
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 */
31 #include <sys/param.h>
33 #include <sys/types.h>
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>
50 #include "gui_support.h"
58 #include "action.h" /* (for action_chmod) */
61 #include "gtkicontheme.h"
63 #define TYPE_NS "http://www.freedesktop.org/standards/shared-mime-info"
64 enum {SET_MEDIA
, SET_TYPE
};
66 /* Colours for file types (same order as base types) */
67 static gchar
*opt_type_colours
[][2] = {
68 {"display_err_colour", "#ff0000"},
69 {"display_unkn_colour", "#000000"},
70 {"display_dir_colour", "#000080"},
71 {"display_pipe_colour", "#444444"},
72 {"display_sock_colour", "#ff00ff"},
73 {"display_file_colour", "#000000"},
74 {"display_cdev_colour", "#000000"},
75 {"display_bdev_colour", "#000000"},
76 {"display_door_colour", "#ff00ff"},
77 {"display_exec_colour", "#006000"},
78 {"display_adir_colour", "#006000"}
80 #define NUM_TYPE_COLOURS\
81 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
83 /* Parsed colours for file types */
84 static Option o_type_colours
[NUM_TYPE_COLOURS
];
85 static GdkColor type_colours
[NUM_TYPE_COLOURS
];
87 /* Static prototypes */
88 static void load_mime_types(void);
89 static void alloc_type_colours(void);
90 static void options_changed(void);
91 static char *get_action_save_path(GtkWidget
*dialog
);
92 static MIME_type
*get_mime_type(const gchar
*type_name
, gboolean can_create
);
93 static gboolean
remove_handler_with_confirm(const guchar
*path
);
94 static void set_icon_theme(void);
95 static GList
*build_icon_theme(Option
*option
, xmlNode
*node
, guchar
*label
);
97 /* When working out the type for a file, this hash table is checked
100 static GHashTable
*literal_hash
= NULL
; /* name -> MIME-type */
102 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
103 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
104 * The hash table is consulted from each dot in the string in turn
105 * (First .ps.gz, then try .gz)
107 static GHashTable
*extension_hash
= NULL
;
109 /* The first pattern in the list which matches is used */
110 typedef struct pattern
{
111 gint len
; /* Used for sorting */
115 static GPtrArray
*glob_patterns
= NULL
; /* [Pattern] */
117 /* Hash of all allocated MIME types, indexed by "media/subtype".
118 * MIME_type structs are never freed; this table prevents memory leaks
119 * when rereading the config files.
121 static GHashTable
*type_hash
= NULL
;
123 /* Most things on Unix are text files, so this is the default type */
124 MIME_type
*text_plain
;
125 MIME_type
*inode_directory
;
126 MIME_type
*inode_mountpoint
;
127 MIME_type
*inode_pipe
;
128 MIME_type
*inode_socket
;
129 MIME_type
*inode_block_dev
;
130 MIME_type
*inode_char_dev
;
131 MIME_type
*application_executable
;
132 MIME_type
*inode_unknown
;
133 MIME_type
*inode_door
;
135 static Option o_display_colour_types
;
136 static Option o_icon_theme
;
138 static GtkIconTheme
*icon_theme
= NULL
;
144 icon_theme
= rox_icon_theme_new();
146 extension_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
147 type_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
149 text_plain
= get_mime_type("text/plain", TRUE
);
150 inode_directory
= get_mime_type("inode/directory", TRUE
);
151 inode_mountpoint
= get_mime_type("inode/mount-point", TRUE
);
152 inode_pipe
= get_mime_type("inode/fifo", TRUE
);
153 inode_socket
= get_mime_type("inode/socket", TRUE
);
154 inode_block_dev
= get_mime_type("inode/blockdevice", TRUE
);
155 inode_char_dev
= get_mime_type("inode/chardevice", TRUE
);
156 application_executable
= get_mime_type("application/x-executable", TRUE
);
157 inode_unknown
= get_mime_type("inode/unknown", TRUE
);
158 inode_door
= get_mime_type("inode/door", TRUE
);
162 option_add_string(&o_icon_theme
, "icon_theme", "ROX");
163 option_add_int(&o_display_colour_types
, "display_colour_types", TRUE
);
164 option_register_widget("icon-theme-chooser", build_icon_theme
);
166 for (i
= 0; i
< NUM_TYPE_COLOURS
; i
++)
167 option_add_string(&o_type_colours
[i
],
168 opt_type_colours
[i
][0],
169 opt_type_colours
[i
][1]);
170 alloc_type_colours();
174 option_add_notify(options_changed
);
177 /* Read-load all the glob patterns.
178 * Note: calls filer_update_all.
180 void reread_mime_files(void)
182 rox_icon_theme_rescan_if_needed(icon_theme
);
186 /* Returns the MIME_type structure for the given type name. It is looked
187 * up in type_hash and returned if found. If not found (and can_create is
188 * TRUE) then a new MIME_type is made, added to type_hash and returned.
189 * NULL is returned if type_name is not in type_hash and can_create is
190 * FALSE, or if type_name does not contain a '/' character.
192 static MIME_type
*get_mime_type(const gchar
*type_name
, gboolean can_create
)
197 mtype
= g_hash_table_lookup(type_hash
, type_name
);
198 if (mtype
|| !can_create
)
201 slash
= strchr(type_name
, '/');
202 g_return_val_if_fail(slash
!= NULL
, NULL
); /* XXX: Report nicely */
204 mtype
= g_new(MIME_type
, 1);
205 mtype
->media_type
= g_strndup(type_name
, slash
- type_name
);
206 mtype
->subtype
= g_strdup(slash
+ 1);
208 mtype
->comment
= NULL
;
210 g_hash_table_insert(type_hash
, g_strdup(type_name
), mtype
);
215 const char *basetype_name(DirItem
*item
)
217 if (item
->flags
& ITEM_FLAG_SYMLINK
)
218 return _("Sym link");
219 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
220 return _("Mount point");
221 else if (item
->flags
& ITEM_FLAG_APPDIR
)
224 switch (item
->base_type
)
230 case TYPE_CHAR_DEVICE
:
231 return _("Char dev");
232 case TYPE_BLOCK_DEVICE
:
233 return _("Block dev");
245 static void append_names(gpointer key
, gpointer value
, gpointer udata
)
247 GList
**list
=(GList
**) udata
;
249 *list
=g_list_prepend(*list
, key
);
252 /* Return list of all mime type names */
253 GList
*mime_type_name_list(void)
257 g_hash_table_foreach(type_hash
, append_names
, &list
);
258 list
=g_list_sort(list
, (GCompareFunc
) strcmp
);
263 /* MIME-type guessing */
265 /* Get the type of this file - stats the file and uses that if
266 * possible. For regular or missing files, uses the pathname.
268 MIME_type
*type_get_type(const guchar
*path
)
271 MIME_type
*type
= NULL
;
273 item
= diritem_new("");
274 diritem_restat(path
, item
, NULL
);
275 if (item
->base_type
!= TYPE_ERROR
)
276 type
= item
->mime_type
;
282 type
= type_from_path(path
);
290 /* Returns a pointer to the MIME-type.
291 * NULL if we can't think of anything.
293 MIME_type
*type_from_path(const char *path
)
295 const char *ext
, *dot
, *leafname
;
297 MIME_type
*type
= NULL
;
301 # ifdef WITH_GNOMEVFS
302 if (o_use_gnomevfs
.int_value
)
303 return get_mime_type(gnome_vfs_mime_type_from_name(path
), TRUE
);
307 leafname
= g_basename(path
);
309 type
= g_hash_table_lookup(literal_hash
, leafname
);
312 lower
= g_utf8_strdown(leafname
, -1);
313 type
= g_hash_table_lookup(literal_hash
, lower
);
319 while ((dot
= strchr(ext
, '.')))
323 type
= g_hash_table_lookup(extension_hash
, ext
);
328 type
= g_hash_table_lookup(extension_hash
,
329 lower
+ (ext
- leafname
));
334 for (i
= 0; i
< glob_patterns
->len
; i
++)
336 Pattern
*p
= glob_patterns
->pdata
[i
];
338 if (fnmatch(p
->glob
, leafname
, 0) == 0 ||
339 fnmatch(p
->glob
, lower
, 0) == 0)
352 /* Returns the file/dir in Choices for handling this type.
353 * NULL if there isn't one. g_free() the result.
355 static char *handler_for(MIME_type
*type
)
360 type_name
= g_strconcat(type
->media_type
, "_", type
->subtype
, NULL
);
361 open
= choices_find_path_load(type_name
, "MIME-types");
365 open
= choices_find_path_load(type
->media_type
, "MIME-types");
370 MIME_type
*mime_type_lookup(const char *type
)
372 return get_mime_type(type
, FALSE
);
375 /* Actions for types */
377 gboolean
type_open(const char *path
, MIME_type
*type
)
379 gchar
*argv
[] = {NULL
, NULL
, NULL
};
384 argv
[1] = (char *) path
;
386 open
= handler_for(type
);
390 if (stat(open
, &info
))
392 report_error("stat(%s): %s", open
, g_strerror(errno
));
397 if (info
.st_mode
& S_IWOTH
)
402 report_error(_("Executable '%s' is world-writeable! Refusing "
403 "to run. Please change the permissions now (this "
404 "problem may have been caused by a bug in earlier "
405 "versions of the filer).\n\n"
406 "Having (non-symlink) run actions world-writeable "
407 "means that other people who use your computer can "
408 "replace your run actions with malicious versions.\n\n"
409 "If you trust everyone who could write to these files "
410 "then you needn't worry. Otherwise, you should check, "
411 "or even just delete, all the existing run actions."),
413 choices_dir
= g_path_get_dirname(open
);
414 paths
= g_list_append(NULL
, choices_dir
);
415 action_chmod(paths
, TRUE
, _("go-w (Fix security problem)"));
422 if (S_ISDIR(info
.st_mode
))
423 argv
[0] = g_strconcat(open
, "/AppRun", NULL
);
427 retval
= rox_spawn(home_dir
, (const gchar
**) argv
) != 0;
437 /* Return the image for this type, loading it if needed.
438 * Places to check are: (eg type="text_plain", base="text")
439 * 1. <Choices>/MIME-icons/base_subtype
440 * 2. Icon theme 'mime-base:subtype'
441 * 3. Icon theme 'mime-base'
442 * 4. Unknown type icon.
444 * Note: You must g_object_unref() the image afterwards.
446 MaskedPixmap
*type_to_icon(MIME_type
*type
)
449 char *type_name
, *path
;
454 g_object_ref(im_unknown
);
459 /* Already got an image? */
462 /* Yes - don't recheck too often */
463 if (abs(now
- type
->image_time
) < 2)
465 g_object_ref(type
->image
);
468 g_object_unref(type
->image
);
472 type_name
= g_strconcat(type
->media_type
, "_", type
->subtype
,
474 path
= choices_find_path_load(type_name
, "MIME-icons");
478 type
->image
= g_fscache_lookup(pixmap_cache
, path
);
485 type_name
= g_strconcat("mime-", type
->media_type
, ":",
486 type
->subtype
, NULL
);
487 full
= rox_icon_theme_load_icon(icon_theme
, type_name
, HUGE_HEIGHT
,
492 /* Ugly hack... try for a GNOME icon */
493 type_name
= g_strconcat("gnome-mime-", type
->media_type
,
494 "-", type
->subtype
, NULL
);
495 full
= rox_icon_theme_load_icon(icon_theme
,
497 HUGE_HEIGHT
, 0, NULL
);
502 /* Try for a media type */
503 type_name
= g_strconcat("mime-", type
->media_type
, NULL
);
504 full
= rox_icon_theme_load_icon(icon_theme
,
506 HUGE_HEIGHT
, 0, NULL
);
511 type
->image
= masked_pixmap_new(full
);
512 g_object_unref(full
);
518 /* One ref from the type structure, one returned */
519 type
->image
= im_unknown
;
520 g_object_ref(im_unknown
);
523 type
->image_time
= now
;
525 g_object_ref(type
->image
);
529 GdkAtom
type_to_atom(MIME_type
*type
)
534 g_return_val_if_fail(type
!= NULL
, GDK_NONE
);
536 str
= g_strconcat(type
->media_type
, "/", type
->subtype
, NULL
);
537 retval
= gdk_atom_intern(str
, FALSE
);
543 static void show_shell_help(gpointer data
)
545 info_message(_("Enter a shell command which will load \"$@\" into "
546 "a suitable program. Eg:\n\n"
550 /* Called if the user clicks on the OK button. Returns FALSE if an error
551 * was displayed instead of performing the action.
553 static gboolean
set_shell_action(GtkWidget
*dialog
)
556 const guchar
*command
;
561 entry
= g_object_get_data(G_OBJECT(dialog
), "shell_command");
563 g_return_val_if_fail(entry
!= NULL
, FALSE
);
565 command
= gtk_entry_get_text(entry
);
567 if (!strchr(command
, '$'))
569 show_shell_help(NULL
);
573 path
= get_action_save_path(dialog
);
577 tmp
= g_strdup_printf("#! /bin/sh\nexec %s\n", command
);
580 fd
= open(path
, O_CREAT
| O_WRONLY
, 0755);
587 file
= fdopen(fd
, "w");
590 if (fwrite(tmp
, 1, len
, file
) < len
)
592 if (fclose(file
) && error
== 0)
600 report_error(g_strerror(error
));
605 gtk_widget_destroy(dialog
);
610 static void set_action_response(GtkWidget
*dialog
, gint response
, gpointer data
)
612 if (response
== GTK_RESPONSE_OK
)
613 if (!set_shell_action(dialog
))
615 gtk_widget_destroy(dialog
);
618 /* Return the path of the file in choices that handles this type and
620 * NULL if nothing is defined for it.
622 static guchar
*handler_for_radios(GObject
*dialog
)
627 radios
= g_object_get_data(G_OBJECT(dialog
), "rox-radios");
628 type
= g_object_get_data(G_OBJECT(dialog
), "mime_type");
630 g_return_val_if_fail(radios
!= NULL
, NULL
);
631 g_return_val_if_fail(type
!= NULL
, NULL
);
633 switch (radios_get_value(radios
))
636 return choices_find_path_load(type
->media_type
,
640 gchar
*tmp
, *handler
;
641 tmp
= g_strconcat(type
->media_type
, "_",
642 type
->subtype
, NULL
);
643 handler
= choices_find_path_load(tmp
, "MIME-types");
648 g_warning("Bad type");
653 static void run_action_update(gpointer data
)
657 GObject
*dialog
= G_OBJECT(data
);
659 drop_box
= g_object_get_data(dialog
, "rox-dropbox");
661 g_return_if_fail(drop_box
!= NULL
);
663 handler
= handler_for_radios(dialog
);
669 handler
= readlink_dup(old
);
676 drop_box_set_path(DROP_BOX(drop_box
), handler
);
680 static void clear_run_action(GtkWidget
*drop_box
, GtkWidget
*dialog
)
684 handler
= handler_for_radios(G_OBJECT(dialog
));
687 remove_handler_with_confirm(handler
);
689 run_action_update(dialog
);
692 /* Called when a URI list is dropped onto the box in the Set Run Action
693 * dialog. Make sure it's an application, and make that the default
696 static void drag_app_dropped(GtkWidget
*drop_box
,
702 item
= diritem_new("");
703 diritem_restat(app
, item
, NULL
);
704 if (item
->flags
& (ITEM_FLAG_APPDIR
| ITEM_FLAG_EXEC_FILE
))
708 path
= get_action_save_path(dialog
);
712 if (symlink(app
, path
))
713 delayed_error("symlink: %s",
716 destroy_on_idle(dialog
);
723 _("This is not a program! Give me an application "
729 /* Find the current command which is used to run files of this type.
730 * Returns NULL on failure. g_free() the result.
732 static guchar
*get_current_command(MIME_type
*type
)
735 char *handler
, *nl
, *data
= NULL
;
737 guchar
*command
= NULL
;
739 handler
= handler_for(type
);
742 return NULL
; /* No current handler */
744 if (stat(handler
, &info
))
745 goto out
; /* Can't stat */
747 if ((!S_ISREG(info
.st_mode
)) || info
.st_size
> 256)
748 goto out
; /* Only use small regular files */
750 if (!load_file(handler
, &data
, &len
))
751 goto out
; /* Didn't load OK */
753 if (strncmp(data
, "#! /bin/sh\nexec ", 16) != 0)
754 goto out
; /* Not one of ours */
756 nl
= strchr(data
+ 16, '\n');
758 goto out
; /* No newline! */
760 command
= g_strndup(data
+ 16, nl
- data
- 16);
767 /* Find the current command which is used to run files of this type,
768 * and return a textual description of it.
769 * g_free() the result.
771 gchar
*describe_current_command(MIME_type
*type
)
778 g_return_val_if_fail(type
!= NULL
, NULL
);
780 if (type
== application_executable
)
781 return g_strdup(_("Execute file"));
783 handler
= handler_for(type
);
786 return g_strdup(_("No run action defined"));
788 target
= readlink_dup(handler
);
791 /* Cope with relative paths (shouldn't normally be needed) */
793 if (target
[0] == '/')
802 dir
= g_path_get_dirname(handler
);
804 handler
= g_strconcat(dir
, "/", target
, NULL
);
810 if (mc_stat(handler
, &info
) !=0 )
812 desc
= g_strdup_printf(_("Error in handler %s: %s"), handler
,
817 if (S_ISDIR(info
.st_mode
))
820 uid_t dir_uid
= info
.st_uid
;
822 tmp
= make_path(handler
, "AppRun");
824 if (mc_lstat(tmp
, &info
) != 0 || info
.st_uid
!= dir_uid
825 || !(info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)))
826 desc
= g_strdup_printf(
827 _("Invalid application %s (bad AppRun)"),
829 /* Else, just report handler... */
834 /* It's not an application directory, and it's not a symlink... */
836 if (access(handler
, X_OK
) != 0)
838 desc
= g_strdup_printf(_("Non-executable %s"), handler
);
842 desc
= get_current_command(type
);
852 /* Display a dialog box allowing the user to set the default run action
855 void type_set_handler_dialog(MIME_type
*type
)
859 GtkWidget
*frame
, *entry
, *label
;
863 g_return_if_fail(type
!= NULL
);
865 dialog
= GTK_DIALOG(gtk_dialog_new());
866 gtk_dialog_set_has_separator(dialog
, FALSE
);
867 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_MOUSE
);
869 g_object_set_data(G_OBJECT(dialog
), "mime_type", type
);
871 gtk_window_set_title(GTK_WINDOW(dialog
), _("Set run action"));
873 radios
= radios_new(run_action_update
, dialog
);
874 g_object_set_data(G_OBJECT(dialog
), "rox-radios", radios
);
877 _("If a handler for the specific type isn't set up, "
878 "use this as the default."), SET_MEDIA
,
879 _("Set default for all `%s/<anything>'"),
883 _("Use this application for all files with this MIME "
885 _("Only for the type `%s' (%s/%s)"),
886 mime_type_comment(type
),
887 type
->media_type
, type
->subtype
);
889 radios_set_value(radios
, SET_TYPE
);
891 frame
= drop_box_new(_("Drop a suitable application here"));
893 g_object_set_data(G_OBJECT(dialog
), "rox-dropbox", frame
);
895 radios_pack(radios
, GTK_BOX(dialog
->vbox
));
896 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), frame
, TRUE
, TRUE
, 0);
898 g_signal_connect(frame
, "path_dropped",
899 G_CALLBACK(drag_app_dropped
), dialog
);
900 g_signal_connect(frame
, "clear",
901 G_CALLBACK(clear_run_action
), dialog
);
903 hbox
= gtk_hbox_new(FALSE
, 4);
904 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 4);
905 gtk_box_pack_start(GTK_BOX(hbox
), gtk_hseparator_new(), TRUE
, TRUE
, 0);
906 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_("OR")),
908 gtk_box_pack_start(GTK_BOX(hbox
), gtk_hseparator_new(), TRUE
, TRUE
, 0);
910 hbox
= gtk_hbox_new(FALSE
, 4);
911 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 0);
913 label
= gtk_label_new(_("Enter a shell command:")),
914 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
915 gtk_box_pack_start(GTK_BOX(hbox
), label
, TRUE
, TRUE
, 4);
917 gtk_box_pack_start(GTK_BOX(hbox
),
918 new_help_button(show_shell_help
, NULL
), FALSE
, TRUE
, 0);
920 entry
= gtk_entry_new();
921 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), entry
, FALSE
, TRUE
, 0);
922 gtk_widget_grab_focus(entry
);
923 g_object_set_data(G_OBJECT(dialog
), "shell_command", entry
);
924 gtk_entry_set_activates_default(GTK_ENTRY(entry
), TRUE
);
926 /* If possible, fill in the entry box with the current command */
927 tmp
= get_current_command(type
);
930 gtk_entry_set_text(GTK_ENTRY(entry
), tmp
);
931 gtk_editable_set_position(GTK_EDITABLE(entry
), -1);
936 gtk_entry_set_text(GTK_ENTRY(entry
), " \"$@\"");
937 gtk_editable_set_position(GTK_EDITABLE(entry
), 0);
940 gtk_dialog_add_buttons(dialog
,
941 GTK_STOCK_CANCEL
, GTK_RESPONSE_DELETE_EVENT
,
942 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
945 hbox
= gtk_hbox_new(TRUE
, 4);
946 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 0);
948 gtk_dialog_set_default_response(dialog
, GTK_RESPONSE_OK
);
950 g_signal_connect(dialog
, "response",
951 G_CALLBACK(set_action_response
), NULL
);
953 gtk_widget_show_all(GTK_WIDGET(dialog
));
956 /* path is an entry in Choices. If it's a symlink or a very small executable
957 * then just get rid of it, otherwise confirm first. It it doesn't exist,
960 * FALSE on error (abort operation).
962 static gboolean
remove_handler_with_confirm(const guchar
*path
)
966 if (lstat(path
, &info
) == 0)
968 /* A binding already exists... */
969 if (S_ISREG(info
.st_mode
) && info
.st_size
> 256)
971 if (!confirm(_("A run action already exists and is "
972 "quite a big program - are you sure "
973 "you want to delete it?"),
974 GTK_STOCK_DELETE
, NULL
))
982 report_error(_("Can't remove %s: %s"),
983 path
, g_strerror(errno
));
991 /* The user wants to set a new default action for files of this type (or just
992 * clear the action). Removes the current binding if possible and returns the
993 * path to save the new one to. NULL means cancel. g_free() the result.
995 static char *get_action_save_path(GtkWidget
*dialog
)
998 guchar
*type_name
= NULL
;
1002 g_return_val_if_fail(dialog
!= NULL
, NULL
);
1004 type
= g_object_get_data(G_OBJECT(dialog
), "mime_type");
1005 radios
= g_object_get_data(G_OBJECT(dialog
), "rox-radios");
1007 g_return_val_if_fail(radios
!= NULL
&& type
!= NULL
, NULL
);
1009 if (radios_get_value(radios
) == SET_MEDIA
)
1010 type_name
= g_strdup(type
->media_type
);
1012 type_name
= g_strconcat(type
->media_type
, "_",
1013 type
->subtype
, NULL
);
1015 path
= choices_find_path_save("", PROJECT
, FALSE
);
1019 _("Choices saving is disabled by CHOICESPATH variable"));
1024 path
= choices_find_path_save(type_name
, "MIME-types", TRUE
);
1026 if (!remove_handler_with_confirm(path
))
1033 MIME_type
*mime_type_from_base_type(int base_type
)
1039 case TYPE_DIRECTORY
:
1040 return inode_directory
;
1044 return inode_socket
;
1045 case TYPE_BLOCK_DEVICE
:
1046 return inode_block_dev
;
1047 case TYPE_CHAR_DEVICE
:
1048 return inode_char_dev
;
1052 return inode_unknown
;
1055 /* Takes the st_mode field from stat() and returns the base type.
1056 * Should not be a symlink.
1058 int mode_to_base_type(int st_mode
)
1060 if (S_ISREG(st_mode
))
1062 else if (S_ISDIR(st_mode
))
1063 return TYPE_DIRECTORY
;
1064 else if (S_ISBLK(st_mode
))
1065 return TYPE_BLOCK_DEVICE
;
1066 else if (S_ISCHR(st_mode
))
1067 return TYPE_CHAR_DEVICE
;
1068 else if (S_ISFIFO(st_mode
))
1070 else if (S_ISSOCK(st_mode
))
1072 else if (S_ISDOOR(st_mode
))
1078 /* Returns TRUE is this is something that is run by looking up its type
1079 * in MIME-types and, hence, can have its run action set.
1081 gboolean
can_set_run_action(DirItem
*item
)
1083 g_return_val_if_fail(item
!= NULL
, FALSE
);
1085 return item
->base_type
== TYPE_FILE
&&
1086 !(item
->mime_type
== application_executable
);
1089 /* Parse file type colours and allocate/free them as necessary */
1090 static void alloc_type_colours(void)
1092 gboolean success
[NUM_TYPE_COLOURS
];
1093 int change_count
= 0; /* No. needing realloc */
1095 static gboolean allocated
= FALSE
;
1098 for (i
= 0; i
< NUM_TYPE_COLOURS
; i
++)
1100 GdkColor
*c
= &type_colours
[i
];
1102 gushort g
= c
->green
;
1103 gushort b
= c
->blue
;
1105 gdk_color_parse(o_type_colours
[i
].value
, &type_colours
[i
]);
1107 if (allocated
&& (c
->red
!= r
|| c
->green
!= g
|| c
->blue
!= b
))
1111 /* Free colours if they were previously allocated and
1112 * have changed or become unneeded.
1114 if (allocated
&& (change_count
|| !o_display_colour_types
.int_value
))
1116 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1117 type_colours
, NUM_TYPE_COLOURS
);
1121 /* Allocate colours, unless they are still allocated (=> they didn't
1122 * change) or we don't want them anymore.
1123 * XXX: what should be done if allocation fails?
1125 if (!allocated
&& o_display_colour_types
.int_value
)
1127 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1128 type_colours
, NUM_TYPE_COLOURS
,
1129 FALSE
, TRUE
, success
);
1134 static void expire_timer(gpointer key
, gpointer value
, gpointer data
)
1136 MIME_type
*type
= value
;
1138 type
->image_time
= 0;
1141 static void options_changed(void)
1143 alloc_type_colours();
1144 if (o_icon_theme
.has_changed
)
1147 g_hash_table_foreach(type_hash
, expire_timer
, NULL
);
1152 /* Return a pointer to a (static) colour for this item. If colouring is
1153 * off, returns normal.
1155 GdkColor
*type_get_colour(DirItem
*item
, GdkColor
*normal
)
1157 int type
= item
->base_type
;
1159 if (!o_display_colour_types
.int_value
)
1162 if (item
->flags
& ITEM_FLAG_EXEC_FILE
)
1164 else if (item
->flags
& ITEM_FLAG_APPDIR
)
1167 g_return_val_if_fail(type
>= 0 && type
< NUM_TYPE_COLOURS
, normal
);
1169 return &type_colours
[type
];
1172 /* Process the 'Patterns' value */
1173 static void add_pattern(MIME_type
*type
, const char *pattern
, GHashTable
*globs
)
1175 if (pattern
[0] == '*' && pattern
[1] == '.' &&
1176 strpbrk(pattern
+ 2, "*?[") == NULL
)
1178 g_hash_table_insert(extension_hash
,
1179 g_strdup(pattern
+ 2),
1182 else if (strpbrk(pattern
, "*?[") == NULL
)
1183 g_hash_table_insert(literal_hash
, g_strdup(pattern
), type
);
1185 g_hash_table_insert(globs
, g_strdup(pattern
), type
);
1188 /* Load and parse this file. literal_hash and extension_hash are updated
1189 * directly. Other patterns are added to 'globs'.
1191 static void import_file(const gchar
*file
, GHashTable
*globs
)
1193 MIME_type
*type
= NULL
;
1194 GError
*error
= NULL
;
1197 if (access(file
, F_OK
) != 0)
1198 return; /* Doesn't exist. No problem. */
1200 if (!g_file_get_contents(file
, &data
, NULL
, &error
))
1202 delayed_error(_("Error loading MIME database:\n%s"),
1204 g_error_free(error
);
1210 while (line
&& *line
)
1214 nl
= strchr(line
, '\n');
1224 colon
= strchr(line
, ':');
1227 delayed_error(_("File '%s' corrupted!"), file
);
1231 name
= g_strndup(line
, colon
- line
);
1232 type
= get_mime_type(name
, TRUE
);
1235 g_warning("Invalid type in '%s'", file
);
1237 add_pattern(type
, colon
+ 1, globs
);
1246 static void add_to_glob_patterns(gpointer key
, gpointer value
, gpointer unused
)
1250 pattern
= g_new(Pattern
, 1);
1251 pattern
->glob
= g_strdup((gchar
*) key
);
1252 pattern
->type
= (MIME_type
*) value
;
1253 pattern
->len
= strlen(pattern
->glob
);
1255 g_ptr_array_add(glob_patterns
, pattern
);
1258 static gint
sort_by_strlen(gconstpointer a
, gconstpointer b
)
1260 const Pattern
*pa
= *(const Pattern
**) a
;
1261 const Pattern
*pb
= *(const Pattern
**) b
;
1263 if (pa
->len
> pb
->len
)
1265 else if (pa
->len
== pb
->len
)
1270 static char **get_xdg_data_dirs(int *n_dirs
)
1276 env
= getenv("XDG_DATA_DIRS");
1278 env
= "/usr/local/share/:/usr/share/";
1279 dirs
= g_strsplit(env
, ":", 0);
1280 g_return_val_if_fail(dirs
!= NULL
, NULL
);
1281 for (n
= 0; dirs
[n
]; n
++)
1283 for (i
= n
; i
> 0; i
--)
1284 dirs
[i
] = dirs
[i
- 1];
1285 env
= getenv("XDG_DATA_HOME");
1287 dirs
[0] = g_strdup(env
);
1289 dirs
[0] = g_build_filename(g_get_home_dir(), ".local",
1295 /* Clear all currently stored information and re-read everything.
1296 * Note: calls filer_update_all.
1298 static void load_mime_types(void)
1305 dirs
= get_xdg_data_dirs(&n_dirs
);
1306 g_return_if_fail(dirs
!= NULL
);
1310 if (lstat(make_path(home_dir
, ".mime"), &info
) == 0 &&
1311 S_ISDIR(info
.st_mode
))
1313 delayed_error(_("The ~/.mime directory has moved. "
1314 "It should now be ~/.local/share/mime. You "
1315 "should move it there (and make a symlink "
1316 "from ~/.mime to it for older applications)."));
1321 glob_patterns
= g_ptr_array_new();
1326 for (i
= glob_patterns
->len
- 1; i
>= 0; i
--)
1328 Pattern
*p
= glob_patterns
->pdata
[i
];
1332 g_ptr_array_set_size(glob_patterns
, 0);
1336 g_hash_table_destroy(literal_hash
);
1338 g_hash_table_destroy(extension_hash
);
1339 literal_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1341 extension_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1343 globs
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
1345 for (i
= n_dirs
- 1; i
>= 0; i
--)
1348 path
= g_build_filename(dirs
[i
], "mime", "globs", NULL
);
1349 import_file(path
, globs
);
1355 /* Turn the globs hash into a pointer array */
1356 g_hash_table_foreach(globs
, add_to_glob_patterns
, NULL
);
1357 g_hash_table_destroy(globs
);
1360 if (glob_patterns
->len
)
1361 g_ptr_array_sort(glob_patterns
, sort_by_strlen
);
1363 if (g_hash_table_size(extension_hash
) == 0)
1365 delayed_error(_("The standard MIME type database "
1366 "(version 0.9 or later) was not found. "
1367 "The filer will probably not show the correct "
1368 "types for different files. You should download and "
1369 "install the 'shared-mime-info-0.9' package from "
1371 "http://www.freedesktop.org/software/shared-mime-info\n\n"
1372 "If you have already installed this package, check that the "
1373 "permissions allow the files to be read (check "
1374 "/usr/local/share/mime/globs or /usr/share/mime/globs)."));
1380 /* Try to fill in 'type->comment' from this document */
1381 static void get_comment(MIME_type
*type
, const guchar
*path
)
1386 doc
= xml_cache_load(path
);
1390 node
= xml_get_section(doc
, TYPE_NS
, "comment");
1395 g_return_if_fail(type
->comment
== NULL
);
1396 val
= xmlNodeListGetString(node
->doc
, node
->xmlChildrenNode
, 1);
1397 type
->comment
= g_strdup(val
);
1401 g_object_unref(doc
);
1404 /* Fill in the comment field for this MIME type */
1405 static void find_comment(MIME_type
*type
)
1412 g_free(type
->comment
);
1413 type
->comment
= NULL
;
1416 dirs
= get_xdg_data_dirs(&n_dirs
);
1417 g_return_if_fail(dirs
!= NULL
);
1419 for (i
= 0; i
< n_dirs
; i
++)
1423 path
= g_strdup_printf("%s/mime/%s/%s.xml", dirs
[i
],
1424 type
->media_type
, type
->subtype
);
1425 get_comment(type
, path
);
1432 type
->comment
= g_strdup_printf("%s/%s", type
->media_type
,
1435 for (i
= 0; i
< n_dirs
; i
++)
1440 const char *mime_type_comment(MIME_type
*type
)
1445 return type
->comment
;
1448 static void set_icon_theme(void)
1452 const char *theme_name
= o_icon_theme
.value
;
1454 if (!theme_name
|| !*theme_name
)
1459 rox_icon_theme_set_custom_theme(icon_theme
, theme_name
);
1460 info
= rox_icon_theme_lookup_icon(icon_theme
,
1461 "mime-application:postscript",
1465 info
= rox_icon_theme_lookup_icon(icon_theme
,
1466 "gnome-mime-application-postscript",
1471 gtk_icon_info_free(info
);
1475 if (strcmp(theme_name
, "ROX") == 0)
1478 delayed_error(_("Icon theme '%s' does not contain MIME icons. "
1479 "Using ROX default theme instead."),
1485 delayed_error(_("ROX icon theme not found... installing..."));
1487 icon_home
= g_build_filename(home_dir
, ".icons", NULL
);
1488 if (!file_exists(icon_home
))
1489 mkdir(icon_home
, 0755);
1492 icon_home
= g_build_filename(home_dir
, ".icons", "ROX", NULL
);
1493 if (symlink(make_path(app_dir
, "ROX"), icon_home
))
1494 delayed_error(_("Failed to create symlink '%s':\n%s"),
1495 icon_home
, g_strerror(errno
));
1498 rox_icon_theme_rescan_if_needed(icon_theme
);
1501 static guchar
*read_theme(Option
*option
)
1503 GtkOptionMenu
*om
= GTK_OPTION_MENU(option
->widget
);
1506 item
= GTK_LABEL(GTK_BIN(om
)->child
);
1508 g_return_val_if_fail(item
!= NULL
, g_strdup("ROX"));
1510 return g_strdup(gtk_label_get_text(item
));
1513 static void update_theme(Option
*option
)
1515 GtkOptionMenu
*om
= GTK_OPTION_MENU(option
->widget
);
1520 menu
= gtk_option_menu_get_menu(om
);
1522 kids
= gtk_container_get_children(GTK_CONTAINER(menu
));
1523 for (next
= kids
; next
; next
= next
->next
, i
++)
1525 GtkLabel
*item
= GTK_LABEL(GTK_BIN(next
->data
)->child
);
1528 /* The label actually moves from the menu!! */
1530 item
= GTK_LABEL(GTK_BIN(om
)->child
);
1532 label
= gtk_label_get_text(item
);
1534 g_return_if_fail(label
!= NULL
);
1536 if (strcmp(label
, option
->value
) == 0)
1542 gtk_option_menu_set_history(om
, i
);
1544 g_warning("Theme '%s' not found", option
->value
);
1547 static void add_themes_from_dir(GPtrArray
*names
, const char *dir
)
1552 if (access(dir
, F_OK
) != 0)
1555 list
= list_dir(dir
);
1556 g_return_if_fail(list
!= NULL
);
1558 for (i
= 0; i
< list
->len
; i
++)
1562 index_path
= g_build_filename(dir
, list
->pdata
[i
],
1563 "index.theme", NULL
);
1565 if (access(index_path
, F_OK
) == 0)
1566 g_ptr_array_add(names
, list
->pdata
[i
]);
1568 g_free(list
->pdata
[i
]);
1573 g_ptr_array_free(list
, TRUE
);
1576 static GList
*build_icon_theme(Option
*option
, xmlNode
*node
, guchar
*label
)
1578 GtkWidget
*button
, *menu
, *hbox
;
1580 gchar
**theme_dirs
= NULL
;
1584 g_return_val_if_fail(option
!= NULL
, NULL
);
1585 g_return_val_if_fail(label
!= NULL
, NULL
);
1587 hbox
= gtk_hbox_new(FALSE
, 4);
1589 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_(label
)),
1592 button
= gtk_option_menu_new();
1593 gtk_box_pack_start(GTK_BOX(hbox
), button
, TRUE
, TRUE
, 0);
1595 menu
= gtk_menu_new();
1596 gtk_option_menu_set_menu(GTK_OPTION_MENU(button
), menu
);
1598 rox_icon_theme_get_search_path(icon_theme
, &theme_dirs
, &n_dirs
);
1599 names
= g_ptr_array_new();
1600 for (i
= 0; i
< n_dirs
; i
++)
1601 add_themes_from_dir(names
, theme_dirs
[i
]);
1602 g_strfreev(theme_dirs
);
1604 g_ptr_array_sort(names
, strcmp2
);
1606 for (i
= 0; i
< names
->len
; i
++)
1609 char *name
= names
->pdata
[i
];
1611 item
= gtk_menu_item_new_with_label(name
);
1612 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
1613 gtk_widget_show_all(item
);
1618 g_ptr_array_free(names
, TRUE
);
1620 option
->update_widget
= update_theme
;
1621 option
->read_widget
= read_theme
;
1622 option
->widget
= button
;
1624 gtk_signal_connect_object(GTK_OBJECT(button
), "changed",
1625 GTK_SIGNAL_FUNC(option_check_widget
),
1626 (GtkObject
*) option
);
1628 return g_list_append(NULL
, hbox
);