r1221: Merged OptionUI into Option, since it's public now.
[rox-filer.git] / ROX-Filer / src / type.c
blobadd1d2ed753f48ddae40eb1d3114bef8d342d38e
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 /* Colours for file types (same order as base types) */
68 static gchar *opt_type_colours[][2] = {
69 {"display_err_colour", "#ff0000"},
70 {"display_unkn_colour", "#000000"},
71 {"display_dir_colour", "#000080"},
72 {"display_pipe_colour", "#444444"},
73 {"display_sock_colour", "#ff00ff"},
74 {"display_file_colour", "#000000"},
75 {"display_cdev_colour", "#000000"},
76 {"display_bdev_colour", "#000000"},
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 alloc_type_colours(void);
89 static char *import_extensions(guchar *line);
90 static void import_for_dir(guchar *path);
91 char *get_action_save_path(GtkWidget *dialog);
92 static void edit_mime_types(guchar *unused);
93 static void reread_mime_files(guchar *unused);
94 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
95 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label);
96 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label);
98 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
99 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
100 * The hash table is consulted from each dot in the string in turn
101 * (First .ps.gz, then try .gz)
103 static GHashTable *extension_hash = NULL;
104 static char *current_type = NULL; /* (used while reading file) */
105 #ifdef USE_REGEX
106 static GList *patterns = NULL; /* [(regexp -> MIME type)] */
107 #endif
108 /* Hash of all allocated MIME types, indexed by "media/subtype".
109 * MIME_type structs are never freed; this table prevents memory leaks
110 * when rereading the config files.
112 static GHashTable *type_hash = NULL;
114 /* Most things on Unix are text files, so this is the default type */
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;
129 GPtrArray *list;
131 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
132 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
134 text_plain = get_mime_type("text/plain", TRUE);
135 special_directory = get_mime_type("special/directory", TRUE);
136 special_pipe = get_mime_type("special/pipe", TRUE);
137 special_socket = get_mime_type("special/socket", TRUE);
138 special_block_dev = get_mime_type("special/block-device", TRUE);
139 special_char_dev = get_mime_type("special/char-device", TRUE);
140 special_exec = get_mime_type("special/executable", TRUE);
141 special_unknown = get_mime_type("special/unknown", TRUE);
143 current_type = NULL;
145 list = choices_list_dirs("MIME-info");
146 for (i = list->len - 1; i >= 0; i--)
147 import_for_dir((gchar *) g_ptr_array_index(list, i));
148 choices_free_list(list);
150 option_register_widget("type-edit", build_type_edit);
151 option_register_widget("type-reread", build_type_reread);
153 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
155 for (i = 0; i < NUM_TYPE_COLOURS; i++)
156 option_add_string(&o_type_colours[i],
157 opt_type_colours[i][0],
158 opt_type_colours[i][1]);
159 alloc_type_colours();
161 option_add_notify(alloc_type_colours);
164 /* Returns the MIME_type structure for the given type name. It is looked
165 * up in type_hash and returned if found. If not found (and can_create is
166 * TRUE) then a new MIME_type is made, added to type_hash and returned.
167 * NULL is returned if type_name is not in type_hash and can_create is
168 * FALSE, or if type_name does not contain a '/' character.
170 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
172 MIME_type *mtype;
173 gchar *slash;
175 mtype = g_hash_table_lookup(type_hash, type_name);
176 if (mtype || !can_create)
177 return mtype;
179 slash = strchr(type_name, '/');
180 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
182 mtype = g_new(MIME_type, 1);
183 mtype->media_type = g_strndup(type_name, slash - type_name);
184 mtype->subtype = g_strdup(slash + 1);
185 mtype->image = NULL;
187 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
189 return mtype;
192 #ifdef USE_REGEX
193 static void pattern_delete(gpointer patt, gpointer udata)
195 Pattern *pattern = (Pattern *) patt;
197 regfree(&pattern->buffer);
199 g_free(pattern);
201 #endif
203 static gboolean extension_delete(gpointer key, gpointer value, gpointer udata)
205 /* key is a g_strdup'ed string */
206 g_free(key);
208 /* value is also preserved in type_hash, so don't delete */
210 return TRUE; /* Removes this entry */
213 /* XXX: Dup code */
214 void type_reread(void)
216 GPtrArray *list;
217 int i;
219 g_hash_table_foreach_remove(extension_hash, extension_delete, NULL);
220 #ifdef USE_REGEX
221 g_list_foreach(patterns, pattern_delete, NULL);
222 g_list_free(patterns);
223 patterns = NULL;
224 #endif
226 current_type = NULL;
228 list = choices_list_dirs("MIME-info");
229 for (i = list->len - 1; i >= 0; i--)
230 import_for_dir((gchar *) g_ptr_array_index(list, i));
231 choices_free_list(list);
233 filer_update_all();
236 /* Parse every file in 'dir' */
237 static void import_for_dir(guchar *path)
239 DIR *dir;
240 struct dirent *item;
242 dir = opendir(path);
243 if (!dir)
244 return;
246 while ((item = readdir(dir)))
248 guchar *file;
249 struct stat info;
251 if (item->d_name[0] == '.')
252 continue;
254 current_type = NULL;
255 file = make_path(path, item->d_name)->str;
257 if (stat(file, &info) == 0 && S_ISREG(info.st_mode))
258 parse_file(file, import_extensions);
261 closedir(dir);
264 /* Add one entry to the extension_hash table */
265 static void add_ext(char *type_name, char *ext)
267 MIME_type *new;
269 new = get_mime_type(type_name, TRUE);
271 g_return_if_fail(new != NULL);
273 g_hash_table_insert(extension_hash, g_strdup(ext), new);
276 static void add_regex(char *type_name, char *reg)
278 #ifdef USE_REGEX
279 MIME_type *new;
280 char *slash;
281 Pattern *pattern;
283 slash = strchr(type_name, '/');
284 g_return_if_fail(slash != NULL); /* XXX: Report nicely */
286 pattern = g_new0(Pattern, 1);
288 if (regcomp(&pattern->buffer, reg, REG_EXTENDED | REG_NOSUB))
290 regfree(&pattern->buffer);
291 g_free(pattern);
292 return;
295 new = get_mime_type(type_name, TRUE);
297 g_return_if_fail(new != NULL);
299 pattern->type = new;
301 patterns = g_list_prepend(patterns, pattern);
302 #endif
305 /* Parse one line from the file and add entries to extension_hash */
306 static char *import_extensions(guchar *line)
309 if (*line == '\0' || *line == '#')
310 return NULL; /* Comment */
312 if (isspace(*line))
314 if (!current_type)
315 return _("Missing MIME-type");
316 while (*line && isspace(*line))
317 line++;
319 if (strncmp(line, "ext:", 4) == 0)
321 char *ext;
322 line += 4;
324 for (;;)
326 while (*line && isspace(*line))
327 line++;
328 if (*line == '\0')
329 break;
330 ext = line;
331 while (*line && !isspace(*line))
332 line++;
333 if (*line)
334 *line++ = '\0';
335 add_ext(current_type, ext);
338 else if (strncmp(line, "regex:", 6) == 0)
340 line += 6;
342 while (*line && isspace(*line))
343 line++;
344 if (*line)
345 add_regex(current_type, line);
347 /* else ignore */
349 else
351 char *type = line;
352 while (*line && *line != ':' && !isspace(*line))
353 line++;
354 if (*line)
355 *line++ = '\0';
356 while (*line && isspace(*line))
357 line++;
358 if (*line)
359 return _("Trailing chars after MIME-type");
360 current_type = g_strdup(type);
362 return NULL;
365 char *basetype_name(DirItem *item)
367 if (item->flags & ITEM_FLAG_SYMLINK)
368 return _("Sym link");
369 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
370 return _("Mount point");
371 else if (item->flags & ITEM_FLAG_APPDIR)
372 return _("App dir");
374 switch (item->base_type)
376 case TYPE_FILE:
377 return _("File");
378 case TYPE_DIRECTORY:
379 return _("Dir");
380 case TYPE_CHAR_DEVICE:
381 return _("Char dev");
382 case TYPE_BLOCK_DEVICE:
383 return _("Block dev");
384 case TYPE_PIPE:
385 return _("Pipe");
386 case TYPE_SOCKET:
387 return _("Socket");
390 return _("Unknown");
393 /* MIME-type guessing */
395 /* Get the type of this file - stats the file and uses that if
396 * possible. For regular or missing files, uses the pathname.
398 MIME_type *type_get_type(guchar *path)
400 struct stat info;
401 MIME_type *type = NULL;
402 int base = TYPE_FILE;
403 gboolean exec = FALSE;
405 if (mc_stat(path, &info) == 0)
407 base = mode_to_base_type(info.st_mode);
408 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
409 exec = TRUE;
412 if (base == TYPE_FILE)
413 type = type_from_path(path);
415 if (!type)
417 if (base == TYPE_FILE && exec)
418 type = special_exec;
419 else
420 type = mime_type_from_base_type(base);
423 return type;
426 /* Returns a pointer to the MIME-type.
427 * NULL if we can't think of anything.
429 MIME_type *type_from_path(char *path)
431 char *ext, *dot, *lower, *leafname;
432 #ifdef USE_REGEX
433 GList *patt;
434 int len;
435 #endif
437 leafname = strrchr(path, '/');
438 if (!leafname)
439 leafname = path;
440 else
441 leafname++;
442 ext = leafname;
444 while ((dot = strchr(ext, '.')))
446 MIME_type *type;
448 ext = dot + 1;
450 type = g_hash_table_lookup(extension_hash, ext);
452 if (type)
453 return type;
455 lower = g_strdup(ext);
456 g_strdown(lower);
457 type = g_hash_table_lookup(extension_hash, lower);
458 g_free(lower);
460 if (type)
461 return type;
464 #ifdef USE_REGEX
465 len = strlen(leafname);
467 for (patt = patterns; patt; patt = patt->next)
469 Pattern *pattern = (Pattern *) patt->data;
471 if (regexec(&pattern->buffer, leafname, 0, NULL, 0) == 0)
472 return pattern->type;
474 #endif
476 return NULL;
479 /* Returns the file/dir in Choices for handling this type.
480 * NULL if there isn't one. g_free() the result.
482 static char *handler_for(MIME_type *type)
484 char *type_name;
485 char *open;
487 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
488 open = choices_find_path_load(type_name, "MIME-types");
489 g_free(type_name);
491 if (!open)
492 open = choices_find_path_load(type->media_type, "MIME-types");
494 return open;
497 /* Actions for types */
499 gboolean type_open(char *path, MIME_type *type)
501 char *argv[] = {NULL, NULL, NULL};
502 char *open;
503 gboolean retval;
504 struct stat info;
506 argv[1] = path;
508 open = handler_for(type);
509 if (!open)
510 return FALSE;
512 if (stat(open, &info))
514 report_error("stat(%s): %s", open, g_strerror(errno));
515 g_free(open);
516 return FALSE;
519 if (S_ISDIR(info.st_mode))
520 argv[0] = g_strconcat(open, "/AppRun", NULL);
521 else
522 argv[0] = open;
524 retval = rox_spawn(home_dir, argv);
526 if (argv[0] != open)
527 g_free(argv[0]);
529 g_free(open);
531 return retval;
534 /* Return the image for this type, loading it if needed.
535 * Places to check are: (eg type="text_plain", base="text")
536 * 1. Choices:MIME-icons/<type>
537 * 2. Choices:MIME-icons/<base>
538 * 3. Unknown type icon.
540 * Note: You must pixmap_unref() the image afterwards.
542 MaskedPixmap *type_to_icon(MIME_type *type)
544 char *path;
545 char *type_name;
546 time_t now;
548 if (type == NULL)
550 pixmap_ref(im_unknown);
551 return im_unknown;
554 now = time(NULL);
555 /* Already got an image? */
556 if (type->image)
558 /* Yes - don't recheck too often */
559 if (abs(now - type->image_time) < 2)
561 pixmap_ref(type->image);
562 return type->image;
564 pixmap_unref(type->image);
565 type->image = NULL;
568 type_name = g_strconcat(type->media_type, "_",
569 type->subtype, ".xpm", NULL);
570 path = choices_find_path_load(type_name, "MIME-icons");
571 if (!path)
573 strcpy(type_name + strlen(type->media_type), ".xpm");
574 path = choices_find_path_load(type_name, "MIME-icons");
577 g_free(type_name);
579 if (path)
581 type->image = g_fscache_lookup(pixmap_cache, path);
582 g_free(path);
585 if (!type->image)
587 /* One ref from the type structure, one returned */
588 type->image = im_unknown;
589 pixmap_ref(im_unknown);
592 type->image_time = now;
594 pixmap_ref(type->image);
595 return type->image;
598 GdkAtom type_to_atom(MIME_type *type)
600 char *str;
601 GdkAtom retval;
603 g_return_val_if_fail(type != NULL, GDK_NONE);
605 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
606 retval = gdk_atom_intern(str, FALSE);
607 g_free(str);
609 return retval;
612 void show_shell_help(gpointer data)
614 report_error(_("Enter a shell command which will load \"$1\" into "
615 "a suitable program. Eg:\n\n"
616 "gimp \"$1\""));
619 /* Called if the user clicks on the OK button */
620 static void set_shell_action(GtkWidget *dialog)
622 GtkEntry *entry;
623 GtkToggleButton *for_all;
624 guchar *command, *path, *tmp;
625 int error = 0, len;
626 FILE *file;
628 entry = gtk_object_get_data(GTK_OBJECT(dialog), "shell_command");
629 for_all = gtk_object_get_data(GTK_OBJECT(dialog), "set_for_all");
630 g_return_if_fail(entry != NULL);
632 command = gtk_entry_get_text(entry);
634 if (!strchr(command, '$'))
636 show_shell_help(NULL);
637 return;
640 path = get_action_save_path(dialog);
641 if (!path)
642 return;
644 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
645 len = strlen(tmp);
647 file = fopen(path, "wb");
648 if (fwrite(tmp, 1, len, file) < len)
649 error = errno;
650 if (fclose(file) && error == 0)
651 error = errno;
652 if (chmod(path, 0777))
653 error = errno;
655 if (error)
656 report_error(g_strerror(errno));
658 g_free(tmp);
659 g_free(path);
661 gtk_widget_destroy(dialog);
664 /* Called when a URI list is dropped onto the box in the Set Run Action
665 * dialog. Make sure it's an application, and make that the default
666 * handler.
668 void drag_app_dropped(GtkWidget *eb,
669 GdkDragContext *context,
670 gint x,
671 gint y,
672 GtkSelectionData *selection_data,
673 guint info,
674 guint32 time,
675 GtkWidget *dialog)
677 GList *uris;
678 guchar *app = NULL;
679 DirItem *item;
681 if (!selection_data->data)
682 return; /* Timeout? */
684 uris = uri_list_to_glist(selection_data->data);
686 if (g_list_length(uris) == 1)
687 app = get_local_path((guchar *) uris->data);
688 g_list_free(uris);
690 if (!app)
692 delayed_error(
693 _("You should drop a single (local) application "
694 "onto the drop box - that application will be "
695 "used to load files of this type in future"));
696 return;
699 item = diritem_new(NULL);
700 diritem_restat(app, item);
701 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
703 guchar *path;
705 path = get_action_save_path(dialog);
707 if (path)
709 if (symlink(app, path))
710 delayed_error("symlink: %s",
711 g_strerror(errno));
712 else
713 destroy_on_idle(dialog);
716 g_free(path);
718 else
719 delayed_error(
720 _("This is not a program! Give me an application "
721 "instead!"));
723 diritem_free(item);
726 /* Find the current command which is used to run files of this type.
727 * Returns NULL on failure. g_free() the result.
729 static guchar *get_current_command(MIME_type *type)
731 struct stat info;
732 char *handler, *nl, *data = NULL;
733 long len;
734 guchar *command = NULL;
736 handler = handler_for(type);
738 if (!handler)
739 return NULL; /* No current handler */
741 if (stat(handler, &info))
742 goto out; /* Can't stat */
744 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
745 goto out; /* Only use small regular files */
747 if (!load_file(handler, &data, &len))
748 goto out; /* Didn't load OK */
750 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
751 goto out; /* Not one of ours */
753 nl = strchr(data + 16, '\n');
754 if (!nl)
755 goto out; /* No newline! */
757 command = g_strndup(data + 16, nl - data - 16);
758 out:
759 g_free(handler);
760 g_free(data);
761 return command;
764 /* Find the current command which is used to run files of this type,
765 * and return a textual description of it.
766 * g_free() the result.
768 gchar *describe_current_command(MIME_type *type)
770 char *handler;
771 char *desc = NULL;
772 struct stat info;
773 char *target;
775 g_return_val_if_fail(type != NULL, NULL);
777 if (type == special_exec)
778 return g_strdup(_("Execute file"));
780 handler = handler_for(type);
782 if (!handler)
783 return g_strdup(_("No run action defined"));
785 target = readlink_dup(handler);
786 if (target)
788 /* Cope with relative paths (shouldn't normally be needed) */
790 if (target[0] == '/')
792 g_free(handler);
793 handler = target;
795 else
797 gchar *dir;
799 dir = g_dirname(handler);
800 g_free(handler);
801 handler = g_strconcat(dir, "/", target, NULL);
802 g_free(target);
803 g_free(dir);
807 if (mc_stat(handler, &info) !=0 )
809 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
810 g_strerror(errno));
811 goto out;
814 if (S_ISDIR(info.st_mode))
816 gchar *tmp;
817 uid_t dir_uid = info.st_uid;
819 tmp = make_path(handler, "AppRun")->str;
821 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
822 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
823 desc = g_strdup_printf(
824 _("Invalid application %s (bad AppRun)"),
825 handler);
826 /* Else, just report handler... */
828 goto out;
831 /* It's not an application directory, and it's not a symlink... */
833 if (access(handler, X_OK) != 0)
835 desc = g_strdup_printf(_("Non-executable %s"), handler);
836 goto out;
839 desc = get_current_command(type);
840 out:
841 if (!desc)
842 desc = handler;
843 else
844 g_free(handler);
846 return desc;
849 /* Display a dialog box allowing the user to set the default run action
850 * for this type.
852 void type_set_handler_dialog(MIME_type *type)
854 guchar *tmp;
855 gchar *handler;
856 GtkWidget *dialog, *vbox, *frame, *hbox, *entry, *label, *button;
857 GtkWidget *radio, *eb;
858 GtkTargetEntry targets[] = {
859 {"text/uri-list", 0, TARGET_URI_LIST},
862 g_return_if_fail(type != NULL);
864 dialog = gtk_window_new(GTK_WINDOW_DIALOG);
865 #ifdef GTK2
866 gtk_window_set_type_hint(GTK_WINDOW(dialog),
867 GDK_WINDOW_TYPE_HINT_DIALOG);
868 #endif
869 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
870 gtk_object_set_data(GTK_OBJECT(dialog), "mime_type", type);
872 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
873 gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
875 vbox = gtk_vbox_new(FALSE, 4);
876 gtk_container_add(GTK_CONTAINER(dialog), vbox);
878 tmp = g_strdup_printf(_("Set default for all `%s/<anything>'"),
879 type->media_type);
880 radio = gtk_radio_button_new_with_label(NULL, tmp);
881 g_free(tmp);
882 gtk_object_set_data(GTK_OBJECT(dialog), "set_for_all", radio);
884 tmp = g_strdup_printf(_("Only for the type `%s/%s'"), type->media_type,
885 type->subtype);
886 gtk_box_pack_start(GTK_BOX(vbox), radio, FALSE, TRUE, 0);
887 radio = gtk_radio_button_new_with_label(
888 gtk_radio_button_group(GTK_RADIO_BUTTON(radio)),
889 tmp);
890 g_free(tmp);
891 gtk_box_pack_start(GTK_BOX(vbox), radio, FALSE, TRUE, 0);
892 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
894 frame = gtk_frame_new(NULL);
895 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 4);
896 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
897 eb = gtk_event_box_new();
898 gtk_container_add(GTK_CONTAINER(frame), eb);
900 gtk_container_set_border_width(GTK_CONTAINER(eb), 4);
902 handler = handler_for(type);
903 if (handler)
905 char *link;
907 link = readlink_dup(handler);
908 if (link)
910 char *msg;
912 msg = g_strdup_printf(_("Currently %s"), link);
913 gtk_tooltips_set_tip(tooltips, eb, msg, NULL);
914 g_free(link);
915 g_free(msg);
917 g_free(handler);
920 gtk_drag_dest_set(eb, GTK_DEST_DEFAULT_ALL,
921 targets, sizeof(targets) / sizeof(*targets),
922 GDK_ACTION_COPY);
923 gtk_signal_connect(GTK_OBJECT(eb), "drag_data_received",
924 GTK_SIGNAL_FUNC(drag_app_dropped), dialog);
926 label = gtk_label_new(_("Drop a suitable\napplication here"));
927 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
928 gtk_container_add(GTK_CONTAINER(eb), label);
930 hbox = gtk_hbox_new(FALSE, 4);
931 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 4);
932 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
933 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
934 FALSE, TRUE, 0);
935 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
937 hbox = gtk_hbox_new(FALSE, 4);
938 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
940 label = gtk_label_new(_("Enter a shell command:")),
941 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
942 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
944 gtk_box_pack_start(GTK_BOX(hbox),
945 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
947 entry = gtk_entry_new();
948 gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 0);
949 gtk_widget_grab_focus(entry);
950 gtk_object_set_data(GTK_OBJECT(dialog), "shell_command", entry);
951 gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
952 GTK_SIGNAL_FUNC(set_shell_action), GTK_OBJECT(dialog));
954 hbox = gtk_hbox_new(TRUE, 4);
955 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
957 /* If possible, fill in the entry box with the current command */
958 tmp = get_current_command(type);
959 if (tmp)
961 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
962 gtk_entry_set_position(GTK_ENTRY(entry), -1);
963 g_free(tmp);
965 else
967 gtk_entry_set_text(GTK_ENTRY(entry), " \"$1\"");
968 gtk_entry_set_position(GTK_ENTRY(entry), 0);
971 button = gtk_button_new_with_label(_("OK"));
972 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
973 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
974 gtk_window_set_default(GTK_WINDOW(dialog), button);
975 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
976 GTK_SIGNAL_FUNC(set_shell_action), GTK_OBJECT(dialog));
978 button = gtk_button_new_with_label(_("Cancel"));
979 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
980 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
981 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
982 GTK_SIGNAL_FUNC(gtk_widget_destroy),
983 GTK_OBJECT(dialog));
985 gtk_widget_show_all(dialog);
988 /* The user wants to set a new default action for files of this type.
989 * Removes the current binding if possible and returns the path to
990 * save the new one to. NULL means cancel. g_free() the result.
992 char *get_action_save_path(GtkWidget *dialog)
994 guchar *path = NULL;
995 struct stat info;
996 guchar *type_name = NULL;
997 MIME_type *type;
998 GtkToggleButton *for_all;
1000 g_return_val_if_fail(dialog != NULL, NULL);
1001 type = gtk_object_get_data(GTK_OBJECT(dialog), "mime_type");
1002 for_all = gtk_object_get_data(GTK_OBJECT(dialog), "set_for_all");
1003 g_return_val_if_fail(for_all != NULL && type != NULL, NULL);
1005 if (gtk_toggle_button_get_active(for_all))
1006 type_name = g_strdup(type->media_type);
1007 else
1008 type_name = g_strconcat(type->media_type, "_",
1009 type->subtype, NULL);
1011 path = choices_find_path_save("", PROJECT, FALSE);
1012 if (!path)
1014 report_error(
1015 _("Choices saving is disabled by CHOICESPATH variable"));
1016 goto out;
1018 g_free(path);
1020 path = choices_find_path_save(type_name, "MIME-types", TRUE);
1022 if (lstat(path, &info) == 0)
1024 /* A binding already exists... */
1025 if (S_ISREG(info.st_mode) && info.st_size > 256)
1027 if (get_choice(PROJECT,
1028 _("A run action already exists and is quite "
1029 "a big program - are you sure you want to "
1030 "delete it?"), 2, "Cancel", "Delete") != 1)
1032 g_free(path);
1033 path = NULL;
1034 goto out;
1038 if (unlink(path))
1040 report_error(_("Can't remove %s: %s"),
1041 path, g_strerror(errno));
1042 g_free(path);
1043 path = NULL;
1044 goto out;
1048 out:
1049 g_free(type_name);
1050 return path;
1053 MIME_type *mime_type_from_base_type(int base_type)
1055 switch (base_type)
1057 case TYPE_FILE:
1058 return text_plain;
1059 case TYPE_DIRECTORY:
1060 return special_directory;
1061 case TYPE_PIPE:
1062 return special_pipe;
1063 case TYPE_SOCKET:
1064 return special_socket;
1065 case TYPE_BLOCK_DEVICE:
1066 return special_block_dev;
1067 case TYPE_CHAR_DEVICE:
1068 return special_char_dev;
1070 return special_unknown;
1073 /* Takes the st_mode field from stat() and returns the base type.
1074 * Should not be a symlink.
1076 int mode_to_base_type(int st_mode)
1078 if (S_ISREG(st_mode))
1079 return TYPE_FILE;
1080 else if (S_ISDIR(st_mode))
1081 return TYPE_DIRECTORY;
1082 else if (S_ISBLK(st_mode))
1083 return TYPE_BLOCK_DEVICE;
1084 else if (S_ISCHR(st_mode))
1085 return TYPE_CHAR_DEVICE;
1086 else if (S_ISFIFO(st_mode))
1087 return TYPE_PIPE;
1088 else if (S_ISSOCK(st_mode))
1089 return TYPE_SOCKET;
1091 return TYPE_ERROR;
1094 /* Returns TRUE is this is something that is run by looking up its type
1095 * in MIME-types and, hence, can have its run action set.
1097 gboolean can_set_run_action(DirItem *item)
1099 g_return_val_if_fail(item != NULL, FALSE);
1101 return item->base_type == TYPE_FILE &&
1102 !(item->mime_type == special_exec);
1105 /* Open all <Choices>/type directories and display a message */
1106 static void open_choices_dirs(gchar *type, gchar *what)
1108 guchar *dir;
1109 GPtrArray *list;
1110 int i;
1112 dir = choices_find_path_save("", type, TRUE);
1113 g_free(dir);
1114 list = choices_list_dirs(type);
1116 for (i = list->len - 1; i >= 0; i--)
1117 filer_opendir(list->pdata[i], NULL);
1119 choices_free_list(list);
1122 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
1123 static void edit_mime_types(guchar *unused)
1125 open_choices_dirs("MIME-info", "the files defining MIME types");
1128 static void reread_mime_files(guchar *unused)
1130 type_reread();
1133 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label)
1135 GtkWidget *button, *align;
1137 g_return_val_if_fail(none == NULL, NULL);
1139 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1140 button = gtk_button_new_with_label(_(label));
1141 gtk_container_add(GTK_CONTAINER(align), button);
1143 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1144 GTK_SIGNAL_FUNC(reread_mime_files), NULL);
1146 return g_list_append(NULL, align);
1149 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label)
1151 GtkWidget *button, *align;
1153 g_return_val_if_fail(none == NULL, NULL);
1155 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1156 button = gtk_button_new_with_label(_(label));
1157 gtk_container_add(GTK_CONTAINER(align), button);
1159 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1160 GTK_SIGNAL_FUNC(edit_mime_types), NULL);
1162 return g_list_append(NULL, align);
1165 /* Parse file type colours and allocate/free them as necessary */
1166 static void alloc_type_colours(void)
1168 gboolean success[NUM_TYPE_COLOURS];
1169 int change_count = 0; /* No. needing realloc */
1170 int i;
1171 static gboolean allocated = FALSE;
1173 /* Parse colours */
1174 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1176 GdkColor *c = &type_colours[i];
1177 gushort r = c->red;
1178 gushort g = c->green;
1179 gushort b = c->blue;
1181 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1183 if (allocated && (c->red != r || c->green != g || c->blue != b))
1184 change_count++;
1187 /* Free colours if they were previously allocated and
1188 * have changed or become unneeded.
1190 if (allocated && (change_count || !o_display_colour_types.int_value))
1192 gdk_colormap_free_colors(gdk_rgb_get_cmap(),
1193 type_colours, NUM_TYPE_COLOURS);
1194 allocated = FALSE;
1197 /* Allocate colours, unless they are still allocated (=> they didn't
1198 * change) or we don't want them anymore.
1199 * XXX: what should be done if allocation fails?
1201 if (!allocated && o_display_colour_types.int_value)
1203 gdk_colormap_alloc_colors(gdk_rgb_get_cmap(),
1204 type_colours, NUM_TYPE_COLOURS,
1205 FALSE, TRUE, success);
1206 allocated = TRUE;
1210 /* Return a pointer to a (static) colour for this item. If colouring is
1211 * off, returns normal.
1213 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1215 if (!o_display_colour_types.int_value)
1216 return normal;
1218 if (item->flags & ITEM_FLAG_EXEC_FILE)
1219 return &type_colours[8];
1220 else if (item->flags & ITEM_FLAG_APPDIR)
1221 return &type_colours[9];
1222 else
1223 return &type_colours[item->base_type];