4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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>
35 # include <libgnomevfs/gnome-vfs.h>
36 # include <libgnomevfs/gnome-vfs-mime.h>
37 # include <libgnomevfs/gnome-vfs-mime-handlers.h>
38 # include <libgnomevfs/gnome-vfs-application-registry.h>
48 #include "gui_support.h"
57 /* Colours for file types (same order as base types) */
58 static gchar
*opt_type_colours
[][2] = {
59 {"display_err_colour", "#ff0000"},
60 {"display_unkn_colour", "#000000"},
61 {"display_dir_colour", "#000080"},
62 {"display_pipe_colour", "#444444"},
63 {"display_sock_colour", "#ff00ff"},
64 {"display_file_colour", "#000000"},
65 {"display_cdev_colour", "#000000"},
66 {"display_bdev_colour", "#000000"},
67 {"display_exec_colour", "#006000"},
68 {"display_adir_colour", "#006000"}
70 #define NUM_TYPE_COLOURS\
71 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
73 /* Parsed colours for file types */
74 static Option o_type_colours
[NUM_TYPE_COLOURS
];
75 static GdkColor type_colours
[NUM_TYPE_COLOURS
];
77 /* Static prototypes */
78 static void load_mime_types(void);
79 static void alloc_type_colours(void);
80 char *get_action_save_path(GtkWidget
*dialog
);
81 static void edit_mime_types(guchar
*unused
);
82 static void reread_mime_files(guchar
*unused
);
83 static MIME_type
*get_mime_type(const gchar
*type_name
, gboolean can_create
);
84 static GList
*build_type_reread(Option
*none
, xmlNode
*node
, guchar
*label
);
85 static GList
*build_type_edit(Option
*none
, xmlNode
*node
, guchar
*label
);
87 /* When working out the type for a file, this hash table is checked
90 static GHashTable
*literal_hash
= NULL
; /* name -> MIME-type */
92 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
93 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
94 * The hash table is consulted from each dot in the string in turn
95 * (First .ps.gz, then try .gz)
97 static GHashTable
*extension_hash
= NULL
;
99 /* The first pattern in the list which matches is used */
100 typedef struct pattern
{
101 gint len
; /* Used for sorting */
105 static GPtrArray
*glob_patterns
= NULL
; /* [Pattern] */
107 /* Hash of all allocated MIME types, indexed by "media/subtype".
108 * MIME_type structs are never freed; this table prevents memory leaks
109 * when rereading the config files.
111 static GHashTable
*type_hash
= NULL
;
113 /* Most things on Unix are text files, so this is the default type */
114 MIME_type
*text_plain
;
115 MIME_type
*inode_directory
;
116 MIME_type
*inode_pipe
;
117 MIME_type
*inode_socket
;
118 MIME_type
*inode_block_dev
;
119 MIME_type
*inode_char_dev
;
120 MIME_type
*application_executable
;
121 MIME_type
*inode_unknown
;
123 static Option o_display_colour_types
;
129 extension_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
130 type_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
132 text_plain
= get_mime_type("text/plain", TRUE
);
133 inode_directory
= get_mime_type("inode/directory", TRUE
);
134 inode_pipe
= get_mime_type("inode/fifo", TRUE
);
135 inode_socket
= get_mime_type("inode/socket", TRUE
);
136 inode_block_dev
= get_mime_type("inode/blockdevice", TRUE
);
137 inode_char_dev
= get_mime_type("inode/chardevice", TRUE
);
138 application_executable
= get_mime_type("application/x-executable", TRUE
);
139 inode_unknown
= get_mime_type("inode/unknown", TRUE
);
143 option_register_widget("type-edit", build_type_edit
);
144 option_register_widget("type-reread", build_type_reread
);
146 option_add_int(&o_display_colour_types
, "display_colour_types", TRUE
);
148 for (i
= 0; i
< NUM_TYPE_COLOURS
; i
++)
149 option_add_string(&o_type_colours
[i
],
150 opt_type_colours
[i
][0],
151 opt_type_colours
[i
][1]);
152 alloc_type_colours();
154 option_add_notify(alloc_type_colours
);
157 /* Returns the MIME_type structure for the given type name. It is looked
158 * up in type_hash and returned if found. If not found (and can_create is
159 * TRUE) then a new MIME_type is made, added to type_hash and returned.
160 * NULL is returned if type_name is not in type_hash and can_create is
161 * FALSE, or if type_name does not contain a '/' character.
163 static MIME_type
*get_mime_type(const gchar
*type_name
, gboolean can_create
)
168 mtype
= g_hash_table_lookup(type_hash
, type_name
);
169 if (mtype
|| !can_create
)
172 slash
= strchr(type_name
, '/');
173 g_return_val_if_fail(slash
!= NULL
, NULL
); /* XXX: Report nicely */
175 mtype
= g_new(MIME_type
, 1);
176 mtype
->media_type
= g_strndup(type_name
, slash
- type_name
);
177 mtype
->subtype
= g_strdup(slash
+ 1);
180 g_hash_table_insert(type_hash
, g_strdup(type_name
), mtype
);
185 const char *basetype_name(DirItem
*item
)
187 if (item
->flags
& ITEM_FLAG_SYMLINK
)
188 return _("Sym link");
189 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
190 return _("Mount point");
191 else if (item
->flags
& ITEM_FLAG_APPDIR
)
194 switch (item
->base_type
)
200 case TYPE_CHAR_DEVICE
:
201 return _("Char dev");
202 case TYPE_BLOCK_DEVICE
:
203 return _("Block dev");
213 /* MIME-type guessing */
215 /* Get the type of this file - stats the file and uses that if
216 * possible. For regular or missing files, uses the pathname.
218 MIME_type
*type_get_type(const guchar
*path
)
221 MIME_type
*type
= NULL
;
222 int base
= TYPE_FILE
;
223 gboolean exec
= FALSE
;
225 if (mc_stat(path
, &info
) == 0)
227 base
= mode_to_base_type(info
.st_mode
);
228 if (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
232 if (base
== TYPE_FILE
)
233 type
= type_from_path(path
);
237 if (base
== TYPE_FILE
&& exec
)
238 type
= application_executable
;
240 type
= mime_type_from_base_type(base
);
246 /* Returns a pointer to the MIME-type.
247 * NULL if we can't think of anything.
249 MIME_type
*type_from_path(const char *path
)
251 const char *ext
, *dot
, *leafname
;
253 MIME_type
*type
= NULL
;
257 # ifdef WITH_GNOMEVFS
258 if (o_use_gnomevfs
.int_value
)
259 return get_mime_type(gnome_vfs_mime_type_from_name(path
), TRUE
);
263 leafname
= g_basename(path
);
265 type
= g_hash_table_lookup(literal_hash
, leafname
);
268 lower
= g_utf8_strdown(leafname
, -1);
269 type
= g_hash_table_lookup(literal_hash
, lower
);
275 while ((dot
= strchr(ext
, '.')))
279 type
= g_hash_table_lookup(extension_hash
, ext
);
284 type
= g_hash_table_lookup(extension_hash
,
285 lower
+ (ext
- leafname
));
290 for (i
= 0; i
< glob_patterns
->len
; i
++)
292 Pattern
*p
= glob_patterns
->pdata
[i
];
294 if (fnmatch(p
->glob
, leafname
, 0) == 0 ||
295 fnmatch(p
->glob
, lower
, 0) == 0)
308 /* Returns the file/dir in Choices for handling this type.
309 * NULL if there isn't one. g_free() the result.
311 static char *handler_for(MIME_type
*type
)
316 type_name
= g_strconcat(type
->media_type
, "_", type
->subtype
, NULL
);
317 open
= choices_find_path_load(type_name
, "MIME-types");
321 open
= choices_find_path_load(type
->media_type
, "MIME-types");
326 /* Actions for types */
328 gboolean
type_open(const char *path
, MIME_type
*type
)
330 gchar
*argv
[] = {NULL
, NULL
, NULL
};
335 argv
[1] = (char *) path
;
337 open
= handler_for(type
);
341 if (stat(open
, &info
))
343 report_error("stat(%s): %s", open
, g_strerror(errno
));
348 if (S_ISDIR(info
.st_mode
))
349 argv
[0] = g_strconcat(open
, "/AppRun", NULL
);
353 retval
= rox_spawn(home_dir
, (const gchar
**) argv
);
363 /* Return the image for this type, loading it if needed.
364 * Places to check are: (eg type="text_plain", base="text")
365 * 1. Choices:MIME-icons/<type>
366 * 2. Choices:MIME-icons/<base>
367 * 3. Unknown type icon.
369 * Note: You must g_object_unref() the image afterwards.
371 MaskedPixmap
*type_to_icon(MIME_type
*type
)
379 g_object_ref(im_unknown
);
384 /* Already got an image? */
387 /* Yes - don't recheck too often */
388 if (abs(now
- type
->image_time
) < 2)
390 g_object_ref(type
->image
);
393 g_object_unref(type
->image
);
397 type_name
= g_strconcat(type
->media_type
, "_",
398 type
->subtype
, ".png", NULL
);
399 path
= choices_find_path_load(type_name
, "MIME-icons");
402 strcpy(type_name
+ strlen(type
->media_type
), ".png");
403 path
= choices_find_path_load(type_name
, "MIME-icons");
410 type
->image
= g_fscache_lookup(pixmap_cache
, path
);
416 /* One ref from the type structure, one returned */
417 type
->image
= im_unknown
;
418 g_object_ref(im_unknown
);
421 type
->image_time
= now
;
423 g_object_ref(type
->image
);
427 GdkAtom
type_to_atom(MIME_type
*type
)
432 g_return_val_if_fail(type
!= NULL
, GDK_NONE
);
434 str
= g_strconcat(type
->media_type
, "/", type
->subtype
, NULL
);
435 retval
= gdk_atom_intern(str
, FALSE
);
441 static void show_shell_help(gpointer data
)
443 info_message(_("Enter a shell command which will load \"$1\" into "
444 "a suitable program. Eg:\n\n"
448 /* Called if the user clicks on the OK button */
449 static void set_shell_action(GtkWidget
*dialog
)
452 GtkToggleButton
*for_all
;
453 const guchar
*command
;
458 entry
= g_object_get_data(G_OBJECT(dialog
), "shell_command");
459 for_all
= g_object_get_data(G_OBJECT(dialog
), "set_for_all");
460 g_return_if_fail(entry
!= NULL
);
462 command
= gtk_entry_get_text(entry
);
464 if (!strchr(command
, '$'))
466 show_shell_help(NULL
);
470 path
= get_action_save_path(dialog
);
474 tmp
= g_strdup_printf("#! /bin/sh\nexec %s\n", command
);
477 file
= fopen(path
, "wb");
478 if (fwrite(tmp
, 1, len
, file
) < len
)
480 if (fclose(file
) && error
== 0)
482 if (chmod(path
, 0755))
486 report_error(g_strerror(errno
));
491 gtk_widget_destroy(dialog
);
494 static void set_action_response(GtkWidget
*dialog
, gint response
, gpointer data
)
496 if (response
== GTK_RESPONSE_OK
)
497 set_shell_action(dialog
);
498 gtk_widget_destroy(dialog
);
501 /* Called when a URI list is dropped onto the box in the Set Run Action
502 * dialog. Make sure it's an application, and make that the default
505 static void drag_app_dropped(GtkWidget
*eb
,
506 GdkDragContext
*context
,
509 GtkSelectionData
*selection_data
,
515 const gchar
*app
= NULL
;
518 if (!selection_data
->data
)
519 return; /* Timeout? */
521 uris
= uri_list_to_glist(selection_data
->data
);
523 if (g_list_length(uris
) == 1)
524 app
= get_local_path((guchar
*) uris
->data
);
530 _("You should drop a single (local) application "
531 "onto the drop box - that application will be "
532 "used to load files of this type in future"));
536 item
= diritem_new("");
537 diritem_restat(app
, item
, NULL
);
538 if (item
->flags
& (ITEM_FLAG_APPDIR
| ITEM_FLAG_EXEC_FILE
))
542 path
= get_action_save_path(dialog
);
546 if (symlink(app
, path
))
547 delayed_error("symlink: %s",
550 destroy_on_idle(dialog
);
557 _("This is not a program! Give me an application "
563 /* Find the current command which is used to run files of this type.
564 * Returns NULL on failure. g_free() the result.
566 static guchar
*get_current_command(MIME_type
*type
)
569 char *handler
, *nl
, *data
= NULL
;
571 guchar
*command
= NULL
;
573 handler
= handler_for(type
);
576 return NULL
; /* No current handler */
578 if (stat(handler
, &info
))
579 goto out
; /* Can't stat */
581 if ((!S_ISREG(info
.st_mode
)) || info
.st_size
> 256)
582 goto out
; /* Only use small regular files */
584 if (!load_file(handler
, &data
, &len
))
585 goto out
; /* Didn't load OK */
587 if (strncmp(data
, "#! /bin/sh\nexec ", 16) != 0)
588 goto out
; /* Not one of ours */
590 nl
= strchr(data
+ 16, '\n');
592 goto out
; /* No newline! */
594 command
= g_strndup(data
+ 16, nl
- data
- 16);
601 /* Find the current command which is used to run files of this type,
602 * and return a textual description of it.
603 * g_free() the result.
605 gchar
*describe_current_command(MIME_type
*type
)
612 g_return_val_if_fail(type
!= NULL
, NULL
);
614 if (type
== application_executable
)
615 return g_strdup(_("Execute file"));
617 handler
= handler_for(type
);
620 return g_strdup(_("No run action defined"));
622 target
= readlink_dup(handler
);
625 /* Cope with relative paths (shouldn't normally be needed) */
627 if (target
[0] == '/')
636 dir
= g_dirname(handler
);
638 handler
= g_strconcat(dir
, "/", target
, NULL
);
644 if (mc_stat(handler
, &info
) !=0 )
646 desc
= g_strdup_printf(_("Error in handler %s: %s"), handler
,
651 if (S_ISDIR(info
.st_mode
))
654 uid_t dir_uid
= info
.st_uid
;
656 tmp
= make_path(handler
, "AppRun")->str
;
658 if (mc_lstat(tmp
, &info
) != 0 || info
.st_uid
!= dir_uid
659 || !(info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)))
660 desc
= g_strdup_printf(
661 _("Invalid application %s (bad AppRun)"),
663 /* Else, just report handler... */
668 /* It's not an application directory, and it's not a symlink... */
670 if (access(handler
, X_OK
) != 0)
672 desc
= g_strdup_printf(_("Non-executable %s"), handler
);
676 desc
= get_current_command(type
);
686 /* Display a dialog box allowing the user to set the default run action
689 void type_set_handler_dialog(MIME_type
*type
)
694 GtkWidget
*frame
, *entry
, *label
;
695 GtkWidget
*radio
, *eb
, *hbox
;
696 GtkTargetEntry targets
[] = {
697 {"text/uri-list", 0, TARGET_URI_LIST
},
700 g_return_if_fail(type
!= NULL
);
702 dialog
= GTK_DIALOG(gtk_dialog_new());
703 gtk_dialog_set_has_separator(dialog
, FALSE
);
704 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_MOUSE
);
706 g_object_set_data(G_OBJECT(dialog
), "mime_type", type
);
708 gtk_window_set_title(GTK_WINDOW(dialog
), _("Set run action"));
710 tmp
= g_strdup_printf(_("Set default for all `%s/<anything>'"),
712 radio
= gtk_radio_button_new_with_label(NULL
, tmp
);
714 g_object_set_data(G_OBJECT(dialog
), "set_for_all", radio
);
716 tmp
= g_strdup_printf(_("Only for the type `%s/%s'"), type
->media_type
,
718 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), radio
, FALSE
, TRUE
, 0);
719 radio
= gtk_radio_button_new_with_label(
720 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio
)),
723 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), radio
, FALSE
, TRUE
, 0);
724 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio
), TRUE
);
726 frame
= gtk_frame_new(NULL
);
727 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), frame
, TRUE
, TRUE
, 4);
728 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
729 eb
= gtk_event_box_new();
730 gtk_container_add(GTK_CONTAINER(frame
), eb
);
732 gtk_container_set_border_width(GTK_CONTAINER(eb
), 4);
734 handler
= handler_for(type
);
739 link
= readlink_dup(handler
);
744 msg
= g_strdup_printf(_("Currently %s"), link
);
745 gtk_tooltips_set_tip(tooltips
, eb
, msg
, NULL
);
752 gtk_drag_dest_set(eb
, GTK_DEST_DEFAULT_ALL
,
753 targets
, sizeof(targets
) / sizeof(*targets
),
755 g_signal_connect(eb
, "drag_data_received",
756 G_CALLBACK(drag_app_dropped
), dialog
);
758 label
= gtk_label_new(_("Drop a suitable\napplication here"));
759 gtk_misc_set_padding(GTK_MISC(label
), 10, 20);
760 gtk_container_add(GTK_CONTAINER(eb
), label
);
762 hbox
= gtk_hbox_new(FALSE
, 4);
763 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 4);
764 gtk_box_pack_start(GTK_BOX(hbox
), gtk_hseparator_new(), TRUE
, TRUE
, 0);
765 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_("OR")),
767 gtk_box_pack_start(GTK_BOX(hbox
), gtk_hseparator_new(), TRUE
, TRUE
, 0);
769 hbox
= gtk_hbox_new(FALSE
, 4);
770 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 0);
772 label
= gtk_label_new(_("Enter a shell command:")),
773 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
774 gtk_box_pack_start(GTK_BOX(hbox
), label
, TRUE
, TRUE
, 4);
776 gtk_box_pack_start(GTK_BOX(hbox
),
777 new_help_button(show_shell_help
, NULL
), FALSE
, TRUE
, 0);
779 entry
= gtk_entry_new();
780 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), entry
, FALSE
, TRUE
, 0);
781 gtk_widget_grab_focus(entry
);
782 g_object_set_data(G_OBJECT(dialog
), "shell_command", entry
);
783 gtk_entry_set_activates_default(GTK_ENTRY(entry
), TRUE
);
785 /* If possible, fill in the entry box with the current command */
786 tmp
= get_current_command(type
);
789 gtk_entry_set_text(GTK_ENTRY(entry
), tmp
);
790 gtk_editable_set_position(GTK_EDITABLE(entry
), -1);
795 gtk_entry_set_text(GTK_ENTRY(entry
), " \"$1\"");
796 gtk_editable_set_position(GTK_EDITABLE(entry
), 0);
799 gtk_dialog_add_buttons(dialog
,
800 GTK_STOCK_CANCEL
, GTK_RESPONSE_DELETE_EVENT
,
801 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
804 hbox
= gtk_hbox_new(TRUE
, 4);
805 gtk_box_pack_start(GTK_BOX(dialog
->vbox
), hbox
, FALSE
, TRUE
, 0);
807 gtk_dialog_set_default_response(dialog
, GTK_RESPONSE_OK
);
809 g_signal_connect(dialog
, "response",
810 G_CALLBACK(set_action_response
), NULL
);
812 gtk_widget_show_all(GTK_WIDGET(dialog
));
815 /* The user wants to set a new default action for files of this type.
816 * Removes the current binding if possible and returns the path to
817 * save the new one to. NULL means cancel. g_free() the result.
819 char *get_action_save_path(GtkWidget
*dialog
)
823 guchar
*type_name
= NULL
;
825 GtkToggleButton
*for_all
;
827 g_return_val_if_fail(dialog
!= NULL
, NULL
);
828 type
= g_object_get_data(G_OBJECT(dialog
), "mime_type");
829 for_all
= g_object_get_data(G_OBJECT(dialog
), "set_for_all");
830 g_return_val_if_fail(for_all
!= NULL
&& type
!= NULL
, NULL
);
832 if (gtk_toggle_button_get_active(for_all
))
833 type_name
= g_strdup(type
->media_type
);
835 type_name
= g_strconcat(type
->media_type
, "_",
836 type
->subtype
, NULL
);
838 path
= choices_find_path_save("", PROJECT
, FALSE
);
842 _("Choices saving is disabled by CHOICESPATH variable"));
847 path
= choices_find_path_save(type_name
, "MIME-types", TRUE
);
849 if (lstat(path
, &info
) == 0)
851 /* A binding already exists... */
852 if (S_ISREG(info
.st_mode
) && info
.st_size
> 256)
854 if (get_choice(PROJECT
,
855 _("A run action already exists and is quite "
856 "a big program - are you sure you want to "
858 _("Cancel"), _("Delete")) != 1)
868 report_error(_("Can't remove %s: %s"),
869 path
, g_strerror(errno
));
881 MIME_type
*mime_type_from_base_type(int base_type
)
888 return inode_directory
;
893 case TYPE_BLOCK_DEVICE
:
894 return inode_block_dev
;
895 case TYPE_CHAR_DEVICE
:
896 return inode_char_dev
;
898 return inode_unknown
;
901 /* Takes the st_mode field from stat() and returns the base type.
902 * Should not be a symlink.
904 int mode_to_base_type(int st_mode
)
906 if (S_ISREG(st_mode
))
908 else if (S_ISDIR(st_mode
))
909 return TYPE_DIRECTORY
;
910 else if (S_ISBLK(st_mode
))
911 return TYPE_BLOCK_DEVICE
;
912 else if (S_ISCHR(st_mode
))
913 return TYPE_CHAR_DEVICE
;
914 else if (S_ISFIFO(st_mode
))
916 else if (S_ISSOCK(st_mode
))
922 /* Returns TRUE is this is something that is run by looking up its type
923 * in MIME-types and, hence, can have its run action set.
925 gboolean
can_set_run_action(DirItem
*item
)
927 g_return_val_if_fail(item
!= NULL
, FALSE
);
929 return item
->base_type
== TYPE_FILE
&&
930 !(item
->mime_type
== application_executable
);
933 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
934 static void edit_mime_types(guchar
*unused
)
939 mkdir(make_path(home_dir
, ".mime")->str
, 0700);
940 path
= make_path(home_dir
, ".mime/mime-info")->str
;
942 filer_opendir(path
, NULL
);
944 path
= "/usr/local/share/mime/mime-info";
945 if (mc_stat(path
, &info
) == 0)
946 filer_opendir(path
, NULL
);
948 path
= "/usr/share/mime/mime-info";
949 if (mc_stat(path
, &info
) == 0)
950 filer_opendir(path
, NULL
);
953 static void reread_mime_files(guchar
*unused
)
958 static GList
*build_type_reread(Option
*none
, xmlNode
*node
, guchar
*label
)
960 GtkWidget
*button
, *align
;
962 g_return_val_if_fail(none
== NULL
, NULL
);
964 align
= gtk_alignment_new(0.1, 0, 0.1, 0);
965 button
= gtk_button_new_with_label(_(label
));
966 gtk_container_add(GTK_CONTAINER(align
), button
);
968 g_signal_connect_swapped(button
, "clicked",
969 G_CALLBACK(reread_mime_files
), NULL
);
971 return g_list_append(NULL
, align
);
974 static GList
*build_type_edit(Option
*none
, xmlNode
*node
, guchar
*label
)
976 GtkWidget
*button
, *align
;
978 g_return_val_if_fail(none
== NULL
, NULL
);
980 align
= gtk_alignment_new(0.1, 0, 0.1, 0);
981 button
= gtk_button_new_with_label(_(label
));
982 gtk_container_add(GTK_CONTAINER(align
), button
);
984 g_signal_connect_swapped(button
, "clicked",
985 G_CALLBACK(edit_mime_types
), NULL
);
987 return g_list_append(NULL
, align
);
990 /* Parse file type colours and allocate/free them as necessary */
991 static void alloc_type_colours(void)
993 gboolean success
[NUM_TYPE_COLOURS
];
994 int change_count
= 0; /* No. needing realloc */
996 static gboolean allocated
= FALSE
;
999 for (i
= 0; i
< NUM_TYPE_COLOURS
; i
++)
1001 GdkColor
*c
= &type_colours
[i
];
1003 gushort g
= c
->green
;
1004 gushort b
= c
->blue
;
1006 gdk_color_parse(o_type_colours
[i
].value
, &type_colours
[i
]);
1008 if (allocated
&& (c
->red
!= r
|| c
->green
!= g
|| c
->blue
!= b
))
1012 /* Free colours if they were previously allocated and
1013 * have changed or become unneeded.
1015 if (allocated
&& (change_count
|| !o_display_colour_types
.int_value
))
1017 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1018 type_colours
, NUM_TYPE_COLOURS
);
1022 /* Allocate colours, unless they are still allocated (=> they didn't
1023 * change) or we don't want them anymore.
1024 * XXX: what should be done if allocation fails?
1026 if (!allocated
&& o_display_colour_types
.int_value
)
1028 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1029 type_colours
, NUM_TYPE_COLOURS
,
1030 FALSE
, TRUE
, success
);
1035 /* Return a pointer to a (static) colour for this item. If colouring is
1036 * off, returns normal.
1038 GdkColor
*type_get_colour(DirItem
*item
, GdkColor
*normal
)
1040 if (!o_display_colour_types
.int_value
)
1043 if (item
->flags
& ITEM_FLAG_EXEC_FILE
)
1044 return &type_colours
[8];
1045 else if (item
->flags
& ITEM_FLAG_APPDIR
)
1046 return &type_colours
[9];
1048 return &type_colours
[item
->base_type
];
1051 /* Process the 'Patterns' value */
1052 static void add_patterns(MIME_type
*type
, gchar
*patterns
, GHashTable
*globs
)
1058 semi
= strchr(patterns
, ';');
1061 g_strstrip(patterns
);
1062 if (patterns
[0] == '*' && patterns
[1] == '.' &&
1063 strpbrk(patterns
+ 2, "*?[") == NULL
)
1065 g_hash_table_insert(extension_hash
,
1066 g_strdup(patterns
+ 2), type
);
1068 else if (strpbrk(patterns
, "*?[") == NULL
)
1069 g_hash_table_insert(literal_hash
,
1070 g_strdup(patterns
), type
);
1072 g_hash_table_insert(globs
, g_strdup(patterns
), type
);
1075 patterns
= semi
+ 1;
1079 /* Load and parse this file. literal_hash and extension_hash are updated
1080 * directly. Other patterns are added to 'globs'.
1082 static void import_file(const gchar
*file
, GHashTable
*globs
)
1084 MIME_type
*type
= NULL
;
1085 GError
*error
= NULL
;
1088 if (!g_file_get_contents(file
, &data
, NULL
, &error
))
1090 delayed_error("Error loading MIME-Info database:\n%s",
1092 g_error_free(error
);
1098 while (line
&& *line
)
1102 nl
= strchr(line
, '\n');
1113 end
= strchr(line
, ']');
1116 delayed_error(_("File '%s' corrupted!"), file
);
1120 if (strncmp(line
+ 1, "MIME-Info ", 10) == 0)
1125 while (*line
== ' ' || *line
== '\t')
1127 name
= g_strndup(line
, end
- line
);
1130 type
= get_mime_type(name
, TRUE
);
1134 _("Invalid type '%s' in '%s'"),
1144 eq
= strchr(line
, '=');
1149 while (tmp
> line
&&
1150 (tmp
[-1] == ' ' || tmp
[-1] == '\t'))
1155 while (*eq
== ' ' || *eq
== '\t')
1158 if (strcmp(line
, "Patterns") == 0)
1159 add_patterns(type
, eq
, globs
);
1169 /* Parse every .mimeinfo file in 'dir' */
1170 static void import_for_dir(guchar
*path
, GHashTable
*globs
, gboolean
*freedesk
)
1173 struct dirent
*item
;
1175 dir
= opendir(path
);
1179 while ((item
= readdir(dir
)))
1183 dot
= strrchr(item
->d_name
, '.');
1186 if (strcmp(dot
+ 1, "mimeinfo") != 0)
1189 if (*freedesk
== FALSE
&&
1190 !strcmp(item
->d_name
, "freedesktop-shared.mimeinfo"))
1195 import_file(make_path(path
, item
->d_name
)->str
, globs
);
1198 struct timeval start
, end
;
1199 g_print("[ %s ]\n", item
->d_name
);
1200 gettimeofday(&start
, NULL
);
1201 import_file(make_path(path
, item
->d_name
)->str
, globs
);
1202 gettimeofday(&end
, NULL
);
1204 g_print("Delay = %lf s\n",
1205 (end
.tv_sec
+ ((double) end
.tv_usec
) / 1000000) -
1206 (start
.tv_sec
+ ((double) start
.tv_usec
) / 1000000));
1214 static void add_to_glob_patterns(gpointer key
, gpointer value
, gpointer unused
)
1218 pattern
= g_new(Pattern
, 1);
1219 pattern
->glob
= g_strdup((gchar
*) key
);
1220 pattern
->type
= (MIME_type
*) value
;
1221 pattern
->len
= strlen(pattern
->glob
);
1223 g_ptr_array_add(glob_patterns
, pattern
);
1226 static gint
sort_by_strlen(gconstpointer a
, gconstpointer b
)
1228 const Pattern
*pa
= *(const Pattern
**) a
;
1229 const Pattern
*pb
= *(const Pattern
**) b
;
1231 if (pa
->len
> pb
->len
)
1233 else if (pa
->len
== pb
->len
)
1238 /* Clear all currently stored information and re-read everything */
1239 static void load_mime_types(void)
1241 gboolean got_freedesk
= FALSE
;
1246 glob_patterns
= g_ptr_array_new();
1251 for (i
= glob_patterns
->len
- 1; i
>= 0; i
--)
1253 Pattern
*p
= glob_patterns
->pdata
[i
];
1257 g_ptr_array_set_size(glob_patterns
, 0);
1261 g_hash_table_destroy(literal_hash
);
1263 g_hash_table_destroy(extension_hash
);
1264 literal_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1266 extension_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1268 globs
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
1270 import_for_dir("/usr/share/mime/mime-info", globs
, &got_freedesk
);
1272 import_for_dir("/usr/local/share/mime/mime-info", globs
, &got_freedesk
);
1274 tmp
= g_strconcat(home_dir
, "/.mime/mime-info", NULL
);
1275 import_for_dir(tmp
, globs
, &got_freedesk
);
1278 /* Turn the globs hash into a pointer array */
1279 g_hash_table_foreach(globs
, add_to_glob_patterns
, NULL
);
1280 g_hash_table_destroy(globs
);
1282 if (glob_patterns
->len
)
1283 g_ptr_array_sort(glob_patterns
, sort_by_strlen
);
1287 delayed_error(_("The standard MIME type database was not "
1288 "found. The filer will probably not show the correct "
1289 "types for different files. You should download and "
1290 "install the 'Common types package' from here:\n"
1291 "http://www.freedesktop.org/standards/shared-mime-info.html"));