r1473: Put all the backdrop settings in a dialog from the pinboard menu.
[rox-filer.git] / ROX-Filer / src / type.c
blob6b44ee266d2e59214166039a30f5897e19c21e0d
1 /*
2 * $Id$
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)
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>
34 #ifdef WITH_GNOMEVFS
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>
39 #endif
41 #include "global.h"
43 #include "string.h"
44 #include "fscache.h"
45 #include "main.h"
46 #include "pixmaps.h"
47 #include "run.h"
48 #include "gui_support.h"
49 #include "choices.h"
50 #include "type.h"
51 #include "support.h"
52 #include "diritem.h"
53 #include "dnd.h"
54 #include "options.h"
55 #include "filer.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
88 * first...
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 */
102 gchar *glob;
103 MIME_type *type;
104 } Pattern;
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 /* XXX: special -> inode! */
115 MIME_type *text_plain;
116 MIME_type *special_directory;
117 MIME_type *special_pipe;
118 MIME_type *special_socket;
119 MIME_type *special_block_dev;
120 MIME_type *special_char_dev;
121 MIME_type *special_exec;
122 MIME_type *special_unknown;
124 static Option o_display_colour_types;
126 void type_init(void)
128 int i;
130 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
131 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
133 text_plain = get_mime_type("text/plain", TRUE);
134 special_directory = get_mime_type("special/directory", TRUE);
135 special_pipe = get_mime_type("special/pipe", TRUE);
136 special_socket = get_mime_type("special/socket", TRUE);
137 special_block_dev = get_mime_type("special/block-device", TRUE);
138 special_char_dev = get_mime_type("special/char-device", TRUE);
139 special_exec = get_mime_type("special/executable", TRUE);
140 special_unknown = get_mime_type("special/unknown", TRUE);
142 load_mime_types();
144 option_register_widget("type-edit", build_type_edit);
145 option_register_widget("type-reread", build_type_reread);
147 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
149 for (i = 0; i < NUM_TYPE_COLOURS; i++)
150 option_add_string(&o_type_colours[i],
151 opt_type_colours[i][0],
152 opt_type_colours[i][1]);
153 alloc_type_colours();
155 option_add_notify(alloc_type_colours);
158 /* Returns the MIME_type structure for the given type name. It is looked
159 * up in type_hash and returned if found. If not found (and can_create is
160 * TRUE) then a new MIME_type is made, added to type_hash and returned.
161 * NULL is returned if type_name is not in type_hash and can_create is
162 * FALSE, or if type_name does not contain a '/' character.
164 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
166 MIME_type *mtype;
167 gchar *slash;
169 mtype = g_hash_table_lookup(type_hash, type_name);
170 if (mtype || !can_create)
171 return mtype;
173 slash = strchr(type_name, '/');
174 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
176 mtype = g_new(MIME_type, 1);
177 mtype->media_type = g_strndup(type_name, slash - type_name);
178 mtype->subtype = g_strdup(slash + 1);
179 mtype->image = NULL;
181 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
183 return mtype;
186 const char *basetype_name(DirItem *item)
188 if (item->flags & ITEM_FLAG_SYMLINK)
189 return _("Sym link");
190 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
191 return _("Mount point");
192 else if (item->flags & ITEM_FLAG_APPDIR)
193 return _("App dir");
195 switch (item->base_type)
197 case TYPE_FILE:
198 return _("File");
199 case TYPE_DIRECTORY:
200 return _("Dir");
201 case TYPE_CHAR_DEVICE:
202 return _("Char dev");
203 case TYPE_BLOCK_DEVICE:
204 return _("Block dev");
205 case TYPE_PIPE:
206 return _("Pipe");
207 case TYPE_SOCKET:
208 return _("Socket");
211 return _("Unknown");
214 /* MIME-type guessing */
216 /* Get the type of this file - stats the file and uses that if
217 * possible. For regular or missing files, uses the pathname.
219 MIME_type *type_get_type(const guchar *path)
221 struct stat info;
222 MIME_type *type = NULL;
223 int base = TYPE_FILE;
224 gboolean exec = FALSE;
226 if (mc_stat(path, &info) == 0)
228 base = mode_to_base_type(info.st_mode);
229 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
230 exec = TRUE;
233 if (base == TYPE_FILE)
234 type = type_from_path(path);
236 if (!type)
238 if (base == TYPE_FILE && exec)
239 type = special_exec;
240 else
241 type = mime_type_from_base_type(base);
244 return type;
247 /* Returns a pointer to the MIME-type.
248 * NULL if we can't think of anything.
250 MIME_type *type_from_path(const char *path)
252 const char *ext, *dot, *leafname;
253 char *lower;
254 MIME_type *type = NULL;
255 int i;
257 #if 0
258 # ifdef WITH_GNOMEVFS
259 if (o_use_gnomevfs.int_value)
260 return get_mime_type(gnome_vfs_mime_type_from_name(path), TRUE);
261 # endif
262 #endif
264 leafname = g_basename(path);
266 type = g_hash_table_lookup(literal_hash, leafname);
267 if (type)
268 return type;
269 lower = g_utf8_strdown(leafname, -1);
270 type = g_hash_table_lookup(literal_hash, lower);
271 if (type)
272 goto out;
274 ext = leafname;
276 while ((dot = strchr(ext, '.')))
278 ext = dot + 1;
280 type = g_hash_table_lookup(extension_hash, ext);
282 if (type)
283 goto out;
285 type = g_hash_table_lookup(extension_hash,
286 lower + (ext - leafname));
287 if (type)
288 goto out;
291 for (i = 0; i < glob_patterns->len; i++)
293 Pattern *p = glob_patterns->pdata[i];
295 if (fnmatch(p->glob, leafname, 0) == 0)
297 type = p->type;
298 goto out;
301 if (fnmatch(p->glob, lower, 0) == 0)
303 type = p->type;
304 goto out;
309 out:
310 g_free(lower);
312 return type;
315 /* Returns the file/dir in Choices for handling this type.
316 * NULL if there isn't one. g_free() the result.
318 static char *handler_for(MIME_type *type)
320 char *type_name;
321 char *open;
323 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
324 open = choices_find_path_load(type_name, "MIME-types");
325 g_free(type_name);
327 if (!open)
328 open = choices_find_path_load(type->media_type, "MIME-types");
330 return open;
333 /* Actions for types */
335 gboolean type_open(const char *path, MIME_type *type)
337 gchar *argv[] = {NULL, NULL, NULL};
338 char *open;
339 gboolean retval;
340 struct stat info;
342 argv[1] = (char *) path;
344 open = handler_for(type);
345 if (!open)
346 return FALSE;
348 if (stat(open, &info))
350 report_error("stat(%s): %s", open, g_strerror(errno));
351 g_free(open);
352 return FALSE;
355 if (S_ISDIR(info.st_mode))
356 argv[0] = g_strconcat(open, "/AppRun", NULL);
357 else
358 argv[0] = open;
360 retval = rox_spawn(home_dir, (const gchar **) argv);
362 if (argv[0] != open)
363 g_free(argv[0]);
365 g_free(open);
367 return retval;
370 /* Return the image for this type, loading it if needed.
371 * Places to check are: (eg type="text_plain", base="text")
372 * 1. Choices:MIME-icons/<type>
373 * 2. Choices:MIME-icons/<base>
374 * 3. Unknown type icon.
376 * Note: You must g_object_unref() the image afterwards.
378 MaskedPixmap *type_to_icon(MIME_type *type)
380 char *path;
381 char *type_name;
382 time_t now;
384 if (type == NULL)
386 g_object_ref(im_unknown);
387 return im_unknown;
390 now = time(NULL);
391 /* Already got an image? */
392 if (type->image)
394 /* Yes - don't recheck too often */
395 if (abs(now - type->image_time) < 2)
397 g_object_ref(type->image);
398 return type->image;
400 g_object_unref(type->image);
401 type->image = NULL;
404 type_name = g_strconcat(type->media_type, "_",
405 type->subtype, ".xpm", NULL);
406 path = choices_find_path_load(type_name, "MIME-icons");
407 if (!path)
409 strcpy(type_name + strlen(type->media_type), ".xpm");
410 path = choices_find_path_load(type_name, "MIME-icons");
413 g_free(type_name);
415 if (path)
417 type->image = g_fscache_lookup(pixmap_cache, path);
418 g_free(path);
421 if (!type->image)
423 /* One ref from the type structure, one returned */
424 type->image = im_unknown;
425 g_object_ref(im_unknown);
428 type->image_time = now;
430 g_object_ref(type->image);
431 return type->image;
434 GdkAtom type_to_atom(MIME_type *type)
436 char *str;
437 GdkAtom retval;
439 g_return_val_if_fail(type != NULL, GDK_NONE);
441 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
442 retval = gdk_atom_intern(str, FALSE);
443 g_free(str);
445 return retval;
448 void show_shell_help(gpointer data)
450 info_message(_("Enter a shell command which will load \"$1\" into "
451 "a suitable program. Eg:\n\n"
452 "gimp \"$1\""));
455 /* Called if the user clicks on the OK button */
456 static void set_shell_action(GtkWidget *dialog)
458 GtkEntry *entry;
459 GtkToggleButton *for_all;
460 const guchar *command;
461 gchar *tmp, *path;
462 int error = 0, len;
463 FILE *file;
465 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
466 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
467 g_return_if_fail(entry != NULL);
469 command = gtk_entry_get_text(entry);
471 if (!strchr(command, '$'))
473 show_shell_help(NULL);
474 return;
477 path = get_action_save_path(dialog);
478 if (!path)
479 return;
481 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
482 len = strlen(tmp);
484 file = fopen(path, "wb");
485 if (fwrite(tmp, 1, len, file) < len)
486 error = errno;
487 if (fclose(file) && error == 0)
488 error = errno;
489 if (chmod(path, 0777))
490 error = errno;
492 if (error)
493 report_error(g_strerror(errno));
495 g_free(tmp);
496 g_free(path);
498 gtk_widget_destroy(dialog);
501 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
503 if (response == GTK_RESPONSE_OK)
504 set_shell_action(dialog);
505 gtk_widget_destroy(dialog);
508 /* Called when a URI list is dropped onto the box in the Set Run Action
509 * dialog. Make sure it's an application, and make that the default
510 * handler.
512 static void drag_app_dropped(GtkWidget *eb,
513 GdkDragContext *context,
514 gint x,
515 gint y,
516 GtkSelectionData *selection_data,
517 guint info,
518 guint32 time,
519 GtkWidget *dialog)
521 GList *uris;
522 const gchar *app = NULL;
523 DirItem *item;
525 if (!selection_data->data)
526 return; /* Timeout? */
528 uris = uri_list_to_glist(selection_data->data);
530 if (g_list_length(uris) == 1)
531 app = get_local_path((guchar *) uris->data);
532 g_list_free(uris);
534 if (!app)
536 delayed_error(
537 _("You should drop a single (local) application "
538 "onto the drop box - that application will be "
539 "used to load files of this type in future"));
540 return;
543 item = diritem_new("");
544 diritem_restat(app, item, NULL);
545 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
547 guchar *path;
549 path = get_action_save_path(dialog);
551 if (path)
553 if (symlink(app, path))
554 delayed_error("symlink: %s",
555 g_strerror(errno));
556 else
557 destroy_on_idle(dialog);
560 g_free(path);
562 else
563 delayed_error(
564 _("This is not a program! Give me an application "
565 "instead!"));
567 diritem_free(item);
570 /* Find the current command which is used to run files of this type.
571 * Returns NULL on failure. g_free() the result.
573 static guchar *get_current_command(MIME_type *type)
575 struct stat info;
576 char *handler, *nl, *data = NULL;
577 long len;
578 guchar *command = NULL;
580 handler = handler_for(type);
582 if (!handler)
583 return NULL; /* No current handler */
585 if (stat(handler, &info))
586 goto out; /* Can't stat */
588 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
589 goto out; /* Only use small regular files */
591 if (!load_file(handler, &data, &len))
592 goto out; /* Didn't load OK */
594 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
595 goto out; /* Not one of ours */
597 nl = strchr(data + 16, '\n');
598 if (!nl)
599 goto out; /* No newline! */
601 command = g_strndup(data + 16, nl - data - 16);
602 out:
603 g_free(handler);
604 g_free(data);
605 return command;
608 /* Find the current command which is used to run files of this type,
609 * and return a textual description of it.
610 * g_free() the result.
612 gchar *describe_current_command(MIME_type *type)
614 char *handler;
615 char *desc = NULL;
616 struct stat info;
617 char *target;
619 g_return_val_if_fail(type != NULL, NULL);
621 if (type == special_exec)
622 return g_strdup(_("Execute file"));
624 handler = handler_for(type);
626 if (!handler)
627 return g_strdup(_("No run action defined"));
629 target = readlink_dup(handler);
630 if (target)
632 /* Cope with relative paths (shouldn't normally be needed) */
634 if (target[0] == '/')
636 g_free(handler);
637 handler = target;
639 else
641 gchar *dir;
643 dir = g_dirname(handler);
644 g_free(handler);
645 handler = g_strconcat(dir, "/", target, NULL);
646 g_free(target);
647 g_free(dir);
651 if (mc_stat(handler, &info) !=0 )
653 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
654 g_strerror(errno));
655 goto out;
658 if (S_ISDIR(info.st_mode))
660 gchar *tmp;
661 uid_t dir_uid = info.st_uid;
663 tmp = make_path(handler, "AppRun")->str;
665 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
666 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
667 desc = g_strdup_printf(
668 _("Invalid application %s (bad AppRun)"),
669 handler);
670 /* Else, just report handler... */
672 goto out;
675 /* It's not an application directory, and it's not a symlink... */
677 if (access(handler, X_OK) != 0)
679 desc = g_strdup_printf(_("Non-executable %s"), handler);
680 goto out;
683 desc = get_current_command(type);
684 out:
685 if (!desc)
686 desc = handler;
687 else
688 g_free(handler);
690 return desc;
693 /* Display a dialog box allowing the user to set the default run action
694 * for this type.
696 void type_set_handler_dialog(MIME_type *type)
698 guchar *tmp;
699 gchar *handler;
700 GtkDialog *dialog;
701 GtkWidget *frame, *entry, *label;
702 GtkWidget *radio, *eb, *hbox;
703 GtkTargetEntry targets[] = {
704 {"text/uri-list", 0, TARGET_URI_LIST},
707 g_return_if_fail(type != NULL);
709 dialog = GTK_DIALOG(gtk_dialog_new());
710 gtk_dialog_set_has_separator(dialog, FALSE);
711 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
713 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
715 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
717 tmp = g_strdup_printf(_("Set default for all `%s/<anything>'"),
718 type->media_type);
719 radio = gtk_radio_button_new_with_label(NULL, tmp);
720 g_free(tmp);
721 g_object_set_data(G_OBJECT(dialog), "set_for_all", radio);
723 tmp = g_strdup_printf(_("Only for the type `%s/%s'"), type->media_type,
724 type->subtype);
725 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
726 radio = gtk_radio_button_new_with_label(
727 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)),
728 tmp);
729 g_free(tmp);
730 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
731 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
733 frame = gtk_frame_new(NULL);
734 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 4);
735 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
736 eb = gtk_event_box_new();
737 gtk_container_add(GTK_CONTAINER(frame), eb);
739 gtk_container_set_border_width(GTK_CONTAINER(eb), 4);
741 handler = handler_for(type);
742 if (handler)
744 char *link;
746 link = readlink_dup(handler);
747 if (link)
749 char *msg;
751 msg = g_strdup_printf(_("Currently %s"), link);
752 gtk_tooltips_set_tip(tooltips, eb, msg, NULL);
753 g_free(link);
754 g_free(msg);
756 g_free(handler);
759 gtk_drag_dest_set(eb, GTK_DEST_DEFAULT_ALL,
760 targets, sizeof(targets) / sizeof(*targets),
761 GDK_ACTION_COPY);
762 g_signal_connect(eb, "drag_data_received",
763 G_CALLBACK(drag_app_dropped), dialog);
765 label = gtk_label_new(_("Drop a suitable\napplication here"));
766 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
767 gtk_container_add(GTK_CONTAINER(eb), label);
769 hbox = gtk_hbox_new(FALSE, 4);
770 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
771 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
772 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
773 FALSE, TRUE, 0);
774 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
776 hbox = gtk_hbox_new(FALSE, 4);
777 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
779 label = gtk_label_new(_("Enter a shell command:")),
780 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
781 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
783 gtk_box_pack_start(GTK_BOX(hbox),
784 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
786 entry = gtk_entry_new();
787 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
788 gtk_widget_grab_focus(entry);
789 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
790 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
792 /* If possible, fill in the entry box with the current command */
793 tmp = get_current_command(type);
794 if (tmp)
796 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
797 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
798 g_free(tmp);
800 else
802 gtk_entry_set_text(GTK_ENTRY(entry), " \"$1\"");
803 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
806 gtk_dialog_add_buttons(dialog,
807 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
808 GTK_STOCK_OK, GTK_RESPONSE_OK,
809 NULL);
811 hbox = gtk_hbox_new(TRUE, 4);
812 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
814 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
816 g_signal_connect(dialog, "response",
817 G_CALLBACK(set_action_response), NULL);
819 gtk_widget_show_all(GTK_WIDGET(dialog));
822 /* The user wants to set a new default action for files of this type.
823 * Removes the current binding if possible and returns the path to
824 * save the new one to. NULL means cancel. g_free() the result.
826 char *get_action_save_path(GtkWidget *dialog)
828 guchar *path = NULL;
829 struct stat info;
830 guchar *type_name = NULL;
831 MIME_type *type;
832 GtkToggleButton *for_all;
834 g_return_val_if_fail(dialog != NULL, NULL);
835 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
836 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
837 g_return_val_if_fail(for_all != NULL && type != NULL, NULL);
839 if (gtk_toggle_button_get_active(for_all))
840 type_name = g_strdup(type->media_type);
841 else
842 type_name = g_strconcat(type->media_type, "_",
843 type->subtype, NULL);
845 path = choices_find_path_save("", PROJECT, FALSE);
846 if (!path)
848 report_error(
849 _("Choices saving is disabled by CHOICESPATH variable"));
850 goto out;
852 g_free(path);
854 path = choices_find_path_save(type_name, "MIME-types", TRUE);
856 if (lstat(path, &info) == 0)
858 /* A binding already exists... */
859 if (S_ISREG(info.st_mode) && info.st_size > 256)
861 if (get_choice(PROJECT,
862 _("A run action already exists and is quite "
863 "a big program - are you sure you want to "
864 "delete it?"), 2, "Cancel", "Delete") != 1)
866 g_free(path);
867 path = NULL;
868 goto out;
872 if (unlink(path))
874 report_error(_("Can't remove %s: %s"),
875 path, g_strerror(errno));
876 g_free(path);
877 path = NULL;
878 goto out;
882 out:
883 g_free(type_name);
884 return path;
887 MIME_type *mime_type_from_base_type(int base_type)
889 switch (base_type)
891 case TYPE_FILE:
892 return text_plain;
893 case TYPE_DIRECTORY:
894 return special_directory;
895 case TYPE_PIPE:
896 return special_pipe;
897 case TYPE_SOCKET:
898 return special_socket;
899 case TYPE_BLOCK_DEVICE:
900 return special_block_dev;
901 case TYPE_CHAR_DEVICE:
902 return special_char_dev;
904 return special_unknown;
907 /* Takes the st_mode field from stat() and returns the base type.
908 * Should not be a symlink.
910 int mode_to_base_type(int st_mode)
912 if (S_ISREG(st_mode))
913 return TYPE_FILE;
914 else if (S_ISDIR(st_mode))
915 return TYPE_DIRECTORY;
916 else if (S_ISBLK(st_mode))
917 return TYPE_BLOCK_DEVICE;
918 else if (S_ISCHR(st_mode))
919 return TYPE_CHAR_DEVICE;
920 else if (S_ISFIFO(st_mode))
921 return TYPE_PIPE;
922 else if (S_ISSOCK(st_mode))
923 return TYPE_SOCKET;
925 return TYPE_ERROR;
928 /* Returns TRUE is this is something that is run by looking up its type
929 * in MIME-types and, hence, can have its run action set.
931 gboolean can_set_run_action(DirItem *item)
933 g_return_val_if_fail(item != NULL, FALSE);
935 return item->base_type == TYPE_FILE &&
936 !(item->mime_type == special_exec);
939 #if 0
940 /* Open all <Choices>/type directories and display a message */
941 static void open_choices_dirs(gchar *type, gchar *what)
943 guchar *dir;
944 GPtrArray *list;
945 int i;
947 dir = choices_find_path_save("", type, TRUE);
948 g_free(dir);
949 list = choices_list_dirs(type);
951 for (i = list->len - 1; i >= 0; i--)
952 filer_opendir(list->pdata[i], NULL);
954 choices_free_list(list);
956 #endif
958 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
959 static void edit_mime_types(guchar *unused)
961 delayed_error("XXX: This feature is currently under repair");
963 open_choices_dirs("MIME-info", "the files defining MIME types");
967 static void reread_mime_files(guchar *unused)
969 load_mime_types();
972 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label)
974 GtkWidget *button, *align;
976 g_return_val_if_fail(none == NULL, NULL);
978 align = gtk_alignment_new(0.1, 0, 0.1, 0);
979 button = gtk_button_new_with_label(_(label));
980 gtk_container_add(GTK_CONTAINER(align), button);
982 g_signal_connect_swapped(button, "clicked",
983 G_CALLBACK(reread_mime_files), NULL);
985 return g_list_append(NULL, align);
988 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label)
990 GtkWidget *button, *align;
992 g_return_val_if_fail(none == NULL, NULL);
994 align = gtk_alignment_new(0.1, 0, 0.1, 0);
995 button = gtk_button_new_with_label(_(label));
996 gtk_container_add(GTK_CONTAINER(align), button);
998 g_signal_connect_swapped(button, "clicked",
999 G_CALLBACK(edit_mime_types), NULL);
1001 return g_list_append(NULL, align);
1004 /* Parse file type colours and allocate/free them as necessary */
1005 static void alloc_type_colours(void)
1007 gboolean success[NUM_TYPE_COLOURS];
1008 int change_count = 0; /* No. needing realloc */
1009 int i;
1010 static gboolean allocated = FALSE;
1012 /* Parse colours */
1013 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1015 GdkColor *c = &type_colours[i];
1016 gushort r = c->red;
1017 gushort g = c->green;
1018 gushort b = c->blue;
1020 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1022 if (allocated && (c->red != r || c->green != g || c->blue != b))
1023 change_count++;
1026 /* Free colours if they were previously allocated and
1027 * have changed or become unneeded.
1029 if (allocated && (change_count || !o_display_colour_types.int_value))
1031 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1032 type_colours, NUM_TYPE_COLOURS);
1033 allocated = FALSE;
1036 /* Allocate colours, unless they are still allocated (=> they didn't
1037 * change) or we don't want them anymore.
1038 * XXX: what should be done if allocation fails?
1040 if (!allocated && o_display_colour_types.int_value)
1042 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1043 type_colours, NUM_TYPE_COLOURS,
1044 FALSE, TRUE, success);
1045 allocated = TRUE;
1049 /* Return a pointer to a (static) colour for this item. If colouring is
1050 * off, returns normal.
1052 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1054 if (!o_display_colour_types.int_value)
1055 return normal;
1057 if (item->flags & ITEM_FLAG_EXEC_FILE)
1058 return &type_colours[8];
1059 else if (item->flags & ITEM_FLAG_APPDIR)
1060 return &type_colours[9];
1061 else
1062 return &type_colours[item->base_type];
1065 /* Process the 'Patterns' value */
1066 static void add_patterns(MIME_type *type, gchar *patterns, GHashTable *globs)
1068 while (1)
1070 char *semi;
1072 semi = strchr(patterns, ';');
1073 if (semi)
1074 *semi = '\0';
1075 g_strstrip(patterns);
1076 if (patterns[0] == '*' && patterns[1] == '.' &&
1077 strpbrk(patterns + 2, "*?[") == NULL)
1079 g_hash_table_insert(extension_hash,
1080 g_strdup(patterns + 2), type);
1082 else if (strpbrk(patterns, "*?[") == NULL)
1083 g_hash_table_insert(literal_hash,
1084 g_strdup(patterns), type);
1085 else
1086 g_hash_table_insert(globs, g_strdup(patterns), type);
1087 if (!semi)
1088 return;
1089 patterns = semi + 1;
1093 /* Load and parse this file. literal_hash and extension_hash are updated
1094 * directly. Other patterns are added to 'globs'.
1096 static void import_file(const gchar *file, GHashTable *globs)
1098 MIME_type *type = NULL;
1099 GError *error = NULL;
1100 gchar *data, *line;
1102 if (!g_file_get_contents(file, &data, NULL, &error))
1104 delayed_error("Error loading MIME-Info database:\n%s",
1105 error->message);
1106 g_error_free(error);
1107 return;
1110 line = data;
1112 while (line && *line)
1114 char *nl;
1116 nl = strchr(line, '\n');
1117 if (!nl)
1118 break;
1119 *nl = '\0';
1121 if (*line == '[')
1123 const gchar *end;
1125 type = NULL;
1127 end = strchr(line, ']');
1128 if (!end)
1130 delayed_error(_("File '%s' corrupted!"), file);
1131 break;
1134 if (strncmp(line + 1, "MIME-Info ", 10) == 0)
1136 gchar *name;
1138 line += 11;
1139 while (*line == ' ' || *line == '\t')
1140 line++;
1141 name = g_strndup(line, end - line);
1142 g_strstrip(name);
1144 type = get_mime_type(name, TRUE);
1145 if (!type)
1147 delayed_error(
1148 _("Invalid type '%s' in '%s'"),
1149 name, file);
1150 break;
1152 g_free(name);
1155 else if (type)
1157 char *eq;
1158 eq = strchr(line, '=');
1159 if (eq)
1161 char *tmp = eq;
1163 while (tmp > line &&
1164 (tmp[-1] == ' ' || tmp[-1] == '\t'))
1165 tmp--;
1166 *tmp = '\0';
1168 eq++;
1169 while (*eq == ' ' || *eq == '\t')
1170 eq++;
1172 if (strcmp(line, "Patterns") == 0)
1173 add_patterns(type, eq, globs);
1177 line = nl + 1;
1180 g_free(data);
1183 /* Parse every .mimeinfo file in 'dir' */
1184 static void import_for_dir(guchar *path, GHashTable *globs, gboolean *freedesk)
1186 DIR *dir;
1187 struct dirent *item;
1189 dir = opendir(path);
1190 if (!dir)
1191 return;
1193 while ((item = readdir(dir)))
1195 gchar *dot;
1197 dot = strrchr(item->d_name, '.');
1198 if (!dot)
1199 continue;
1200 if (strcmp(dot + 1, "mimeinfo") != 0)
1201 continue;
1203 if (*freedesk == FALSE &&
1204 !strcmp(item->d_name, "freedesktop-shared.mimeinfo"))
1206 *freedesk = TRUE;
1209 import_file(make_path(path, item->d_name)->str, globs);
1212 closedir(dir);
1215 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1217 Pattern *pattern;
1219 pattern = g_new(Pattern, 1);
1220 pattern->glob = g_strdup((gchar *) key);
1221 pattern->type = (MIME_type *) value;
1222 pattern->len = strlen(pattern->glob);
1224 g_ptr_array_add(glob_patterns, pattern);
1227 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1229 const Pattern *pa = *(const Pattern **) a;
1230 const Pattern *pb = *(const Pattern **) b;
1232 if (pa->len > pb->len)
1233 return -1;
1234 else if (pa->len == pb->len)
1235 return 0;
1236 return 1;
1239 /* Clear all currently stored information and re-read everything */
1240 static void load_mime_types(void)
1242 gboolean got_freedesk = FALSE;
1243 GHashTable *globs;
1244 gchar *tmp;
1246 if (!glob_patterns)
1247 glob_patterns = g_ptr_array_new();
1248 else
1250 int i;
1252 for (i = glob_patterns->len - 1; i >= 0; i--)
1254 Pattern *p = glob_patterns->pdata[i];
1255 g_free(p->glob);
1256 g_free(p);
1258 g_ptr_array_set_size(glob_patterns, 0);
1261 if (literal_hash)
1262 g_hash_table_destroy(literal_hash);
1263 if (extension_hash)
1264 g_hash_table_destroy(extension_hash);
1265 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1266 g_free, NULL);
1267 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1268 g_free, NULL);
1269 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1271 import_for_dir("/usr/share/mime/mime-info", globs, &got_freedesk);
1273 import_for_dir("/usr/local/share/mime/mime-info", globs, &got_freedesk);
1275 tmp = g_strconcat(home_dir, "/.mime/mime-info", NULL);
1276 import_for_dir(tmp, globs, &got_freedesk);
1277 g_free(tmp);
1279 /* Turn the globs hash into a pointer array */
1280 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1281 g_hash_table_destroy(globs);
1283 if (glob_patterns->len)
1284 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1286 if (!got_freedesk)
1288 delayed_error(_("The standard MIME type database was not "
1289 "found. The filer will probably not show the correct "
1290 "types for different files. You should download and "
1291 "install the 'Common types package' from here:\n"
1292 "http://www.freedesktop.org/standards/shared-mime-info.html"));
1295 filer_update_all();