r1088: Fixed some compile problems on Solaris (Andreas Dehmel).
[rox-filer.git] / ROX-Filer / src / type.c
blob434abd16549a909f00ce3db72f798e9144c69cc8
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>
33 #ifdef HAVE_REGEX_H
34 # include <regex.h>
35 #endif
37 #include "global.h"
39 #include "string.h"
40 #include "fscache.h"
41 #include "main.h"
42 #include "pixmaps.h"
43 #include "run.h"
44 #include "gui_support.h"
45 #include "choices.h"
46 #include "type.h"
47 #include "support.h"
48 #include "diritem.h"
49 #include "dnd.h"
50 #include "options.h"
51 #include "filer.h"
53 #ifdef HAVE_REGEX_H
54 # define USE_REGEX 1
55 #else
56 # undef USE_REGEX
57 #endif
59 #ifdef USE_REGEX
60 /* A regexp rule, which maps matching filenames to a MIME-types */
61 typedef struct pattern {
62 MIME_type *type;
63 regex_t buffer;
64 } Pattern;
65 #endif
67 static gboolean o_display_colour_types = TRUE;
69 /* Colours for file types (same order as base types) */
70 static gchar *opt_type_colours[][2] = {
71 {"display_err_colour", "#ff0000"},
72 {"display_unkn_colour", "#000000"},
73 {"display_dir_colour", "#000080"},
74 {"display_pipe_colour", "#444444"},
75 {"display_sock_colour", "#ff00ff"},
76 {"display_file_colour", "#000000"},
77 {"display_cdev_colour", "#000000"},
78 {"display_bdev_colour", "#000000"},
79 {"display_exec_colour", "#006000"},
80 {"display_adir_colour", "#006000"}
82 #define NUM_TYPE_COLOURS\
83 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
85 /* Parsed colours for file types */
86 static GdkColor type_colours[NUM_TYPE_COLOURS];
88 /* Static prototypes */
89 static void alloc_type_colours(void);
90 static char *import_extensions(guchar *line);
91 static void import_for_dir(guchar *path);
92 char *get_action_save_path(GtkWidget *dialog);
93 static void edit_mime_types(guchar *unused);
94 static void reread_mime_files(guchar *unused);
95 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
96 static GList *build_type_reread(OptionUI *ui, xmlNode *node, guchar *label);
97 static GList *build_type_edit(OptionUI *ui, xmlNode *node, guchar *label);
99 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
100 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
101 * The hash table is consulted from each dot in the string in turn
102 * (First .ps.gz, then try .gz)
104 static GHashTable *extension_hash = NULL;
105 static char *current_type = NULL; /* (used while reading file) */
106 #ifdef USE_REGEX
107 static GList *patterns = NULL; /* [(regexp -> MIME type)] */
108 #endif
109 /* Hash of all allocated MIME types, indexed by "media/subtype".
110 * MIME_type structs are never freed; this table prevents memory leaks
111 * when rereading the config files.
113 static GHashTable *type_hash = NULL;
115 /* Most things on Unix are text files, so this is the default type */
116 MIME_type *text_plain;
117 MIME_type *special_directory;
118 MIME_type *special_pipe;
119 MIME_type *special_socket;
120 MIME_type *special_block_dev;
121 MIME_type *special_char_dev;
122 MIME_type *special_exec;
123 MIME_type *special_unknown;
125 void type_init(void)
127 int i;
128 GPtrArray *list;
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 current_type = NULL;
144 list = choices_list_dirs("MIME-info");
145 for (i = list->len - 1; i >= 0; i--)
146 import_for_dir((gchar *) g_ptr_array_index(list, i));
147 choices_free_list(list);
149 option_register_widget("type-edit", build_type_edit);
150 option_register_widget("type-reread", build_type_reread);
152 option_add_int("display_colour_types", o_display_colour_types, NULL);
154 for (i = 0; i < NUM_TYPE_COLOURS; i++)
155 option_add_string(opt_type_colours[i][0],
156 opt_type_colours[i][1],
157 NULL);
158 alloc_type_colours();
160 option_add_notify(alloc_type_colours);
163 /* Returns the MIME_type structure for the given type name. It is looked
164 * up in type_hash and returned if found. If not found (and can_create is
165 * TRUE) then a new MIME_type is made, added to type_hash and returned.
166 * NULL is returned if type_name is not in type_hash and can_create is
167 * FALSE, or if type_name does not contain a '/' character.
169 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
171 MIME_type *mtype;
172 gchar *slash;
174 mtype = g_hash_table_lookup(type_hash, type_name);
175 if (mtype || !can_create)
176 return mtype;
178 slash = strchr(type_name, '/');
179 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
181 mtype = g_new(MIME_type, 1);
182 mtype->media_type = g_strndup(type_name, slash - type_name);
183 mtype->subtype = g_strdup(slash + 1);
184 mtype->image = NULL;
186 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
188 return mtype;
191 #ifdef USE_REGEX
192 static void pattern_delete(gpointer patt, gpointer udata)
194 Pattern *pattern = (Pattern *) patt;
196 regfree(&pattern->buffer);
198 g_free(pattern);
200 #endif
202 static gboolean extension_delete(gpointer key, gpointer value, gpointer udata)
204 /* key is a g_strdup'ed string */
205 g_free(key);
207 /* value is also preserved in type_hash, so don't delete */
209 return TRUE; /* Removes this entry */
212 /* XXX: Dup code */
213 void type_reread(void)
215 GPtrArray *list;
216 int i;
218 g_hash_table_foreach_remove(extension_hash, extension_delete, NULL);
219 #ifdef USE_REGEX
220 g_list_foreach(patterns, pattern_delete, NULL);
221 g_list_free(patterns);
222 patterns = NULL;
223 #endif
225 current_type = NULL;
227 list = choices_list_dirs("MIME-info");
228 for (i = list->len - 1; i >= 0; i--)
229 import_for_dir((gchar *) g_ptr_array_index(list, i));
230 choices_free_list(list);
232 filer_update_all();
235 /* Parse every file in 'dir' */
236 static void import_for_dir(guchar *path)
238 DIR *dir;
239 struct dirent *item;
241 dir = opendir(path);
242 if (!dir)
243 return;
245 while ((item = readdir(dir)))
247 guchar *file;
248 struct stat info;
250 if (item->d_name[0] == '.')
251 continue;
253 current_type = NULL;
254 file = make_path(path, item->d_name)->str;
256 if (stat(file, &info) == 0 && S_ISREG(info.st_mode))
257 parse_file(file, import_extensions);
260 closedir(dir);
263 /* Add one entry to the extension_hash table */
264 static void add_ext(char *type_name, char *ext)
266 MIME_type *new;
268 new = get_mime_type(type_name, TRUE);
270 g_return_if_fail(new != NULL);
272 g_hash_table_insert(extension_hash, g_strdup(ext), new);
275 static void add_regex(char *type_name, char *reg)
277 #ifdef USE_REGEX
278 MIME_type *new;
279 char *slash;
280 Pattern *pattern;
282 slash = strchr(type_name, '/');
283 g_return_if_fail(slash != NULL); /* XXX: Report nicely */
285 pattern = g_new0(Pattern, 1);
287 if (regcomp(&pattern->buffer, reg, REG_EXTENDED | REG_NOSUB))
289 regfree(&pattern->buffer);
290 g_free(pattern);
291 return;
294 new = get_mime_type(type_name, TRUE);
296 g_return_if_fail(new != NULL);
298 pattern->type = new;
300 patterns = g_list_prepend(patterns, pattern);
301 #endif
304 /* Parse one line from the file and add entries to extension_hash */
305 static char *import_extensions(guchar *line)
308 if (*line == '\0' || *line == '#')
309 return NULL; /* Comment */
311 if (isspace(*line))
313 if (!current_type)
314 return _("Missing MIME-type");
315 while (*line && isspace(*line))
316 line++;
318 if (strncmp(line, "ext:", 4) == 0)
320 char *ext;
321 line += 4;
323 for (;;)
325 while (*line && isspace(*line))
326 line++;
327 if (*line == '\0')
328 break;
329 ext = line;
330 while (*line && !isspace(*line))
331 line++;
332 if (*line)
333 *line++ = '\0';
334 add_ext(current_type, ext);
337 else if (strncmp(line, "regex:", 6) == 0)
339 line += 6;
341 while (*line && isspace(*line))
342 line++;
343 if (*line)
344 add_regex(current_type, line);
346 /* else ignore */
348 else
350 char *type = line;
351 while (*line && *line != ':' && !isspace(*line))
352 line++;
353 if (*line)
354 *line++ = '\0';
355 while (*line && isspace(*line))
356 line++;
357 if (*line)
358 return _("Trailing chars after MIME-type");
359 current_type = g_strdup(type);
361 return NULL;
364 char *basetype_name(DirItem *item)
366 if (item->flags & ITEM_FLAG_SYMLINK)
367 return _("Sym link");
368 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
369 return _("Mount point");
370 else if (item->flags & ITEM_FLAG_APPDIR)
371 return _("App dir");
373 switch (item->base_type)
375 case TYPE_FILE:
376 return _("File");
377 case TYPE_DIRECTORY:
378 return _("Dir");
379 case TYPE_CHAR_DEVICE:
380 return _("Char dev");
381 case TYPE_BLOCK_DEVICE:
382 return _("Block dev");
383 case TYPE_PIPE:
384 return _("Pipe");
385 case TYPE_SOCKET:
386 return _("Socket");
389 return _("Unknown");
392 /* MIME-type guessing */
394 /* Get the type of this file - stats the file and uses that if
395 * possible. For regular or missing files, uses the pathname.
397 MIME_type *type_get_type(guchar *path)
399 struct stat info;
400 MIME_type *type = NULL;
401 int base = TYPE_FILE;
402 gboolean exec = FALSE;
404 if (mc_stat(path, &info) == 0)
406 base = mode_to_base_type(info.st_mode);
407 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
408 exec = TRUE;
411 if (base == TYPE_FILE)
412 type = type_from_path(path);
414 if (!type)
416 if (base == TYPE_FILE && exec)
417 type = special_exec;
418 else
419 type = mime_type_from_base_type(base);
422 return type;
425 /* Returns a pointer to the MIME-type.
426 * NULL if we can't think of anything.
428 MIME_type *type_from_path(char *path)
430 char *ext, *dot, *lower, *leafname;
431 #ifdef USE_REGEX
432 GList *patt;
433 int len;
434 #endif
436 leafname = strrchr(path, '/');
437 if (!leafname)
438 leafname = path;
439 else
440 leafname++;
441 ext = leafname;
443 while ((dot = strchr(ext, '.')))
445 MIME_type *type;
447 ext = dot + 1;
449 type = g_hash_table_lookup(extension_hash, ext);
451 if (type)
452 return type;
454 lower = g_strdup(ext);
455 g_strdown(lower);
456 type = g_hash_table_lookup(extension_hash, lower);
457 g_free(lower);
459 if (type)
460 return type;
463 #ifdef USE_REGEX
464 len = strlen(leafname);
466 for (patt = patterns; patt; patt = patt->next)
468 Pattern *pattern = (Pattern *) patt->data;
470 if (regexec(&pattern->buffer, leafname, 0, NULL, 0) == 0)
471 return pattern->type;
473 #endif
475 return NULL;
478 /* Returns the file/dir in Choices for handling this type.
479 * NULL if there isn't one. g_free() the result.
481 static char *handler_for(MIME_type *type)
483 char *type_name;
484 char *open;
486 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
487 open = choices_find_path_load(type_name, "MIME-types");
488 g_free(type_name);
490 if (!open)
491 open = choices_find_path_load(type->media_type, "MIME-types");
493 return open;
496 /* Actions for types */
498 gboolean type_open(char *path, MIME_type *type)
500 char *argv[] = {NULL, NULL, NULL};
501 char *open;
502 gboolean retval;
503 struct stat info;
505 argv[1] = path;
507 open = handler_for(type);
508 if (!open)
509 return FALSE;
511 if (stat(open, &info))
513 report_error("stat(%s): %s", open, g_strerror(errno));
514 g_free(open);
515 return FALSE;
518 if (S_ISDIR(info.st_mode))
519 argv[0] = g_strconcat(open, "/AppRun", NULL);
520 else
521 argv[0] = open;
523 retval = rox_spawn(home_dir, argv);
525 if (argv[0] != open)
526 g_free(argv[0]);
528 g_free(open);
530 return retval;
533 /* Return the image for this type, loading it if needed.
534 * Places to check are: (eg type="text_plain", base="text")
535 * 1. Choices:MIME-icons/<type>
536 * 2. Choices:MIME-icons/<base>
537 * 3. Unknown type icon.
539 * Note: You must pixmap_unref() the image afterwards.
541 MaskedPixmap *type_to_icon(MIME_type *type)
543 char *path;
544 char *type_name;
545 time_t now;
547 if (type == NULL)
549 pixmap_ref(im_unknown);
550 return im_unknown;
553 now = time(NULL);
554 /* Already got an image? */
555 if (type->image)
557 /* Yes - don't recheck too often */
558 if (abs(now - type->image_time) < 2)
560 pixmap_ref(type->image);
561 return type->image;
563 pixmap_unref(type->image);
564 type->image = NULL;
567 type_name = g_strconcat(type->media_type, "_",
568 type->subtype, ".xpm", NULL);
569 path = choices_find_path_load(type_name, "MIME-icons");
570 if (!path)
572 strcpy(type_name + strlen(type->media_type), ".xpm");
573 path = choices_find_path_load(type_name, "MIME-icons");
576 g_free(type_name);
578 if (path)
580 type->image = g_fscache_lookup(pixmap_cache, path);
581 g_free(path);
584 if (!type->image)
586 /* One ref from the type structure, one returned */
587 type->image = im_unknown;
588 pixmap_ref(im_unknown);
591 type->image_time = now;
593 pixmap_ref(type->image);
594 return type->image;
597 GdkAtom type_to_atom(MIME_type *type)
599 char *str;
600 GdkAtom retval;
602 g_return_val_if_fail(type != NULL, GDK_NONE);
604 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
605 retval = gdk_atom_intern(str, FALSE);
606 g_free(str);
608 return retval;
611 void show_shell_help(gpointer data)
613 report_error(_("Enter a shell command which will load \"$1\" into "
614 "a suitable program. Eg:\n\n"
615 "gimp \"$1\""));
618 /* Called if the user clicks on the OK button */
619 static void set_shell_action(GtkWidget *dialog)
621 GtkEntry *entry;
622 GtkToggleButton *for_all;
623 guchar *command, *path, *tmp;
624 int error = 0, len;
625 FILE *file;
627 entry = gtk_object_get_data(GTK_OBJECT(dialog), "shell_command");
628 for_all = gtk_object_get_data(GTK_OBJECT(dialog), "set_for_all");
629 g_return_if_fail(entry != NULL);
631 command = gtk_entry_get_text(entry);
633 if (!strchr(command, '$'))
635 show_shell_help(NULL);
636 return;
639 path = get_action_save_path(dialog);
640 if (!path)
641 return;
643 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
644 len = strlen(tmp);
646 file = fopen(path, "wb");
647 if (fwrite(tmp, 1, len, file) < len)
648 error = errno;
649 if (fclose(file) && error == 0)
650 error = errno;
651 if (chmod(path, 0777))
652 error = errno;
654 if (error)
655 report_error(g_strerror(errno));
657 g_free(tmp);
658 g_free(path);
660 gtk_widget_destroy(dialog);
663 /* Called when a URI list is dropped onto the box in the Set Run Action
664 * dialog. Make sure it's an application, and make that the default
665 * handler.
667 void drag_app_dropped(GtkWidget *eb,
668 GdkDragContext *context,
669 gint x,
670 gint y,
671 GtkSelectionData *selection_data,
672 guint info,
673 guint32 time,
674 GtkWidget *dialog)
676 GList *uris;
677 guchar *app = NULL;
678 DirItem *item;
680 if (!selection_data->data)
681 return; /* Timeout? */
683 uris = uri_list_to_glist(selection_data->data);
685 if (g_list_length(uris) == 1)
686 app = get_local_path((guchar *) uris->data);
687 g_list_free(uris);
689 if (!app)
691 delayed_error(
692 _("You should drop a single (local) application "
693 "onto the drop box - that application will be "
694 "used to load files of this type in future"));
695 return;
698 item = diritem_new(NULL);
699 diritem_restat(app, item);
700 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
702 guchar *path;
704 path = get_action_save_path(dialog);
706 if (path)
708 if (symlink(app, path))
709 delayed_error("symlink: %s",
710 g_strerror(errno));
711 else
712 destroy_on_idle(dialog);
715 g_free(path);
717 else
718 delayed_error(
719 _("This is not a program! Give me an application "
720 "instead!"));
722 diritem_free(item);
725 /* Find the current command which is used to run files of this type.
726 * Returns NULL on failure. g_free() the result.
728 static guchar *get_current_command(MIME_type *type)
730 struct stat info;
731 char *handler, *nl, *data = NULL;
732 long len;
733 guchar *command = NULL;
735 handler = handler_for(type);
737 if (!handler)
738 return NULL; /* No current handler */
740 if (stat(handler, &info))
741 goto out; /* Can't stat */
743 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
744 goto out; /* Only use small regular files */
746 if (!load_file(handler, &data, &len))
747 goto out; /* Didn't load OK */
749 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
750 goto out; /* Not one of ours */
752 nl = strchr(data + 16, '\n');
753 if (!nl)
754 goto out; /* No newline! */
756 command = g_strndup(data + 16, nl - data - 16);
757 out:
758 g_free(handler);
759 g_free(data);
760 return command;
763 /* Find the current command which is used to run files of this type,
764 * and return a textual description of it.
765 * g_free() the result.
767 gchar *describe_current_command(MIME_type *type)
769 char *handler;
770 char *desc = NULL;
771 struct stat info;
772 char *target;
774 g_return_val_if_fail(type != NULL, NULL);
776 if (type == special_exec)
777 return g_strdup(_("Execute file"));
779 handler = handler_for(type);
781 if (!handler)
782 return g_strdup(_("No run action defined"));
784 target = readlink_dup(handler);
785 if (target)
787 /* Cope with relative paths (shouldn't normally be needed) */
789 if (target[0] == '/')
791 g_free(handler);
792 handler = target;
794 else
796 gchar *dir;
798 dir = g_dirname(handler);
799 g_free(handler);
800 handler = g_strconcat(dir, "/", target, NULL);
801 g_free(target);
802 g_free(dir);
806 if (mc_stat(handler, &info) !=0 )
808 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
809 g_strerror(errno));
810 goto out;
813 if (S_ISDIR(info.st_mode))
815 gchar *tmp;
816 uid_t dir_uid = info.st_uid;
818 tmp = make_path(handler, "AppRun")->str;
820 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
821 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
822 desc = g_strdup_printf(
823 _("Invalid application %s (bad AppRun)"),
824 handler);
825 /* Else, just report handler... */
827 goto out;
830 /* It's not an application directory, and it's not a symlink... */
832 if (access(handler, X_OK) != 0)
834 desc = g_strdup_printf(_("Non-executable %s"), handler);
835 goto out;
838 desc = get_current_command(type);
839 out:
840 if (!desc)
841 desc = handler;
842 else
843 g_free(handler);
845 return desc;
848 /* Display a dialog box allowing the user to set the default run action
849 * for this type.
851 void type_set_handler_dialog(MIME_type *type)
853 guchar *tmp;
854 gchar *handler;
855 GtkWidget *dialog, *vbox, *frame, *hbox, *entry, *label, *button;
856 GtkWidget *radio, *eb;
857 GtkTargetEntry targets[] = {
858 {"text/uri-list", 0, TARGET_URI_LIST},
861 g_return_if_fail(type != NULL);
863 dialog = gtk_window_new(GTK_WINDOW_DIALOG);
864 #ifdef GTK2
865 gtk_window_set_type_hint(GTK_WINDOW(dialog),
866 GDK_WINDOW_TYPE_HINT_DIALOG);
867 #endif
868 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
869 gtk_object_set_data(GTK_OBJECT(dialog), "mime_type", type);
871 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
872 gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
874 vbox = gtk_vbox_new(FALSE, 4);
875 gtk_container_add(GTK_CONTAINER(dialog), vbox);
877 tmp = g_strdup_printf(_("Set default for all `%s/<anything>'"),
878 type->media_type);
879 radio = gtk_radio_button_new_with_label(NULL, tmp);
880 g_free(tmp);
881 gtk_object_set_data(GTK_OBJECT(dialog), "set_for_all", radio);
883 tmp = g_strdup_printf(_("Only for the type `%s/%s'"), type->media_type,
884 type->subtype);
885 gtk_box_pack_start(GTK_BOX(vbox), radio, FALSE, TRUE, 0);
886 radio = gtk_radio_button_new_with_label(
887 gtk_radio_button_group(GTK_RADIO_BUTTON(radio)),
888 tmp);
889 g_free(tmp);
890 gtk_box_pack_start(GTK_BOX(vbox), radio, FALSE, TRUE, 0);
891 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
893 frame = gtk_frame_new(NULL);
894 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 4);
895 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
896 eb = gtk_event_box_new();
897 gtk_container_add(GTK_CONTAINER(frame), eb);
899 gtk_container_set_border_width(GTK_CONTAINER(eb), 4);
901 handler = handler_for(type);
902 if (handler)
904 char *link;
906 link = readlink_dup(handler);
907 if (link)
909 char *msg;
911 msg = g_strdup_printf(_("Currently %s"), link);
912 gtk_tooltips_set_tip(tooltips, eb, msg, NULL);
913 g_free(link);
914 g_free(msg);
916 g_free(handler);
919 gtk_drag_dest_set(eb, GTK_DEST_DEFAULT_ALL,
920 targets, sizeof(targets) / sizeof(*targets),
921 GDK_ACTION_COPY);
922 gtk_signal_connect(GTK_OBJECT(eb), "drag_data_received",
923 GTK_SIGNAL_FUNC(drag_app_dropped), dialog);
925 label = gtk_label_new(_("Drop a suitable\napplication here"));
926 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
927 gtk_container_add(GTK_CONTAINER(eb), label);
929 hbox = gtk_hbox_new(FALSE, 4);
930 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 4);
931 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
932 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
933 FALSE, TRUE, 0);
934 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
936 hbox = gtk_hbox_new(FALSE, 4);
937 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
939 label = gtk_label_new(_("Enter a shell command:")),
940 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
941 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
943 gtk_box_pack_start(GTK_BOX(hbox),
944 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
946 entry = gtk_entry_new();
947 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
948 gtk_widget_grab_focus(entry);
949 gtk_object_set_data(GTK_OBJECT(dialog), "shell_command", entry);
950 gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
951 GTK_SIGNAL_FUNC(set_shell_action), GTK_OBJECT(dialog));
953 hbox = gtk_hbox_new(TRUE, 4);
954 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
956 /* If possible, fill in the entry box with the current command */
957 tmp = get_current_command(type);
958 if (tmp)
960 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
961 gtk_entry_set_position(GTK_ENTRY(entry), -1);
962 g_free(tmp);
964 else
966 gtk_entry_set_text(GTK_ENTRY(entry), " \"$1\"");
967 gtk_entry_set_position(GTK_ENTRY(entry), 0);
970 button = gtk_button_new_with_label(_("OK"));
971 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
972 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
973 gtk_window_set_default(GTK_WINDOW(dialog), button);
974 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
975 GTK_SIGNAL_FUNC(set_shell_action), GTK_OBJECT(dialog));
977 button = gtk_button_new_with_label(_("Cancel"));
978 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
979 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
980 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
981 GTK_SIGNAL_FUNC(gtk_widget_destroy),
982 GTK_OBJECT(dialog));
984 gtk_widget_show_all(dialog);
987 /* The user wants to set a new default action for files of this type.
988 * Removes the current binding if possible and returns the path to
989 * save the new one to. NULL means cancel. g_free() the result.
991 char *get_action_save_path(GtkWidget *dialog)
993 guchar *path = NULL;
994 struct stat info;
995 guchar *type_name = NULL;
996 MIME_type *type;
997 GtkToggleButton *for_all;
999 g_return_val_if_fail(dialog != NULL, NULL);
1000 type = gtk_object_get_data(GTK_OBJECT(dialog), "mime_type");
1001 for_all = gtk_object_get_data(GTK_OBJECT(dialog), "set_for_all");
1002 g_return_val_if_fail(for_all != NULL && type != NULL, NULL);
1004 if (gtk_toggle_button_get_active(for_all))
1005 type_name = g_strdup(type->media_type);
1006 else
1007 type_name = g_strconcat(type->media_type, "_",
1008 type->subtype, NULL);
1010 path = choices_find_path_save("", PROJECT, FALSE);
1011 if (!path)
1013 report_error(
1014 _("Choices saving is disabled by CHOICESPATH variable"));
1015 goto out;
1017 g_free(path);
1019 path = choices_find_path_save(type_name, "MIME-types", TRUE);
1021 if (lstat(path, &info) == 0)
1023 /* A binding already exists... */
1024 if (S_ISREG(info.st_mode) && info.st_size > 256)
1026 if (get_choice(PROJECT,
1027 _("A run action already exists and is quite "
1028 "a big program - are you sure you want to "
1029 "delete it?"), 2, "Cancel", "Delete") != 1)
1031 g_free(path);
1032 path = NULL;
1033 goto out;
1037 if (unlink(path))
1039 report_error(_("Can't remove %s: %s"),
1040 path, g_strerror(errno));
1041 g_free(path);
1042 path = NULL;
1043 goto out;
1047 out:
1048 g_free(type_name);
1049 return path;
1052 MIME_type *mime_type_from_base_type(int base_type)
1054 switch (base_type)
1056 case TYPE_FILE:
1057 return text_plain;
1058 case TYPE_DIRECTORY:
1059 return special_directory;
1060 case TYPE_PIPE:
1061 return special_pipe;
1062 case TYPE_SOCKET:
1063 return special_socket;
1064 case TYPE_BLOCK_DEVICE:
1065 return special_block_dev;
1066 case TYPE_CHAR_DEVICE:
1067 return special_char_dev;
1069 return special_unknown;
1072 /* Takes the st_mode field from stat() and returns the base type.
1073 * Should not be a symlink.
1075 int mode_to_base_type(int st_mode)
1077 if (S_ISREG(st_mode))
1078 return TYPE_FILE;
1079 else if (S_ISDIR(st_mode))
1080 return TYPE_DIRECTORY;
1081 else if (S_ISBLK(st_mode))
1082 return TYPE_BLOCK_DEVICE;
1083 else if (S_ISCHR(st_mode))
1084 return TYPE_CHAR_DEVICE;
1085 else if (S_ISFIFO(st_mode))
1086 return TYPE_PIPE;
1087 else if (S_ISSOCK(st_mode))
1088 return TYPE_SOCKET;
1090 return TYPE_ERROR;
1093 /* Returns TRUE is this is something that is run by looking up its type
1094 * in MIME-types and, hence, can have its run action set.
1096 gboolean can_set_run_action(DirItem *item)
1098 g_return_val_if_fail(item != NULL, FALSE);
1100 return item->base_type == TYPE_FILE &&
1101 !(item->mime_type == special_exec);
1104 /* Open all <Choices>/type directories and display a message */
1105 static void open_choices_dirs(gchar *type, gchar *what)
1107 guchar *dir;
1108 GPtrArray *list;
1109 int i;
1111 dir = choices_find_path_save("", type, TRUE);
1112 g_free(dir);
1113 list = choices_list_dirs(type);
1115 for (i = list->len - 1; i >= 0; i--)
1116 filer_opendir(list->pdata[i], NULL);
1118 choices_free_list(list);
1121 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
1122 static void edit_mime_types(guchar *unused)
1124 open_choices_dirs("MIME-info", "the files defining MIME types");
1127 static void reread_mime_files(guchar *unused)
1129 type_reread();
1132 static GList *build_type_reread(OptionUI *none, xmlNode *node, guchar *label)
1134 GtkWidget *button, *align;
1136 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1137 button = gtk_button_new_with_label(_(label));
1138 gtk_container_add(GTK_CONTAINER(align), button);
1140 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1141 GTK_SIGNAL_FUNC(reread_mime_files), NULL);
1143 return g_list_append(NULL, align);
1146 static GList *build_type_edit(OptionUI *none, xmlNode *node, guchar *label)
1148 GtkWidget *button, *align;
1150 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1151 button = gtk_button_new_with_label(_(label));
1152 gtk_container_add(GTK_CONTAINER(align), button);
1154 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1155 GTK_SIGNAL_FUNC(edit_mime_types), NULL);
1157 return g_list_append(NULL, align);
1160 /* Parse file type colours and allocate/free them as necessary */
1161 static void alloc_type_colours(void)
1163 gboolean success[NUM_TYPE_COLOURS];
1164 int change_count = 0; /* No. needing realloc */
1165 int i;
1166 static gboolean allocated = FALSE;
1168 o_display_colour_types = option_get_int("display_colour_types");
1170 /* Parse colours */
1171 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1173 GdkColor *c = &type_colours[i];
1174 gushort r = c->red;
1175 gushort g = c->green;
1176 gushort b = c->blue;
1178 gdk_color_parse(
1179 option_get_static_string(opt_type_colours[i][0]),
1180 &type_colours[i]);
1182 if (allocated && (c->red != r || c->green != g || c->blue != b))
1183 change_count++;
1186 /* Free colours if they were previously allocated and
1187 * have changed or become unneeded.
1189 if (allocated && (change_count || !o_display_colour_types))
1191 gdk_colormap_free_colors(gdk_rgb_get_cmap(),
1192 type_colours, NUM_TYPE_COLOURS);
1193 allocated = FALSE;
1196 /* Allocate colours, unless they are still allocated (=> they didn't
1197 * change) or we don't want them anymore.
1198 * XXX: what should be done if allocation fails?
1200 if (!allocated && o_display_colour_types)
1202 gdk_colormap_alloc_colors(gdk_rgb_get_cmap(),
1203 type_colours, NUM_TYPE_COLOURS,
1204 FALSE, TRUE, success);
1205 allocated = TRUE;
1209 /* Return a pointer to a (static) colour for this item. If colouring is
1210 * off, returns normal.
1212 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1214 if (!o_display_colour_types)
1215 return normal;
1217 if (item->flags & ITEM_FLAG_EXEC_FILE)
1218 return &type_colours[8];
1219 else if (item->flags & ITEM_FLAG_APPDIR)
1220 return &type_colours[9];
1221 else
1222 return &type_colours[item->base_type];