r1322: Converted MaskedPixmap to GObject.
[rox-filer.git] / ROX-Filer / src / type.c
blob5132e12d5b58684fc1498e799b9a2fb89c73b750
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 #ifdef WITH_GNOMEVFS
38 # include <libgnomevfs/gnome-vfs.h>
39 # include <libgnomevfs/gnome-vfs-mime.h>
40 # include <libgnomevfs/gnome-vfs-mime-handlers.h>
41 # include <libgnomevfs/gnome-vfs-application-registry.h>
42 #endif
44 #include "global.h"
46 #include "string.h"
47 #include "fscache.h"
48 #include "main.h"
49 #include "pixmaps.h"
50 #include "run.h"
51 #include "gui_support.h"
52 #include "choices.h"
53 #include "type.h"
54 #include "support.h"
55 #include "diritem.h"
56 #include "dnd.h"
57 #include "options.h"
58 #include "filer.h"
60 #ifdef HAVE_REGEX_H
61 # define USE_REGEX 1
62 #else
63 # undef USE_REGEX
64 #endif
66 #ifdef USE_REGEX
67 /* A regexp rule, which maps matching filenames to a MIME-types */
68 typedef struct pattern {
69 MIME_type *type;
70 regex_t buffer;
71 } Pattern;
72 #endif
74 static Option o_use_gnomevfs;
76 /* Colours for file types (same order as base types) */
77 static gchar *opt_type_colours[][2] = {
78 {"display_err_colour", "#ff0000"},
79 {"display_unkn_colour", "#000000"},
80 {"display_dir_colour", "#000080"},
81 {"display_pipe_colour", "#444444"},
82 {"display_sock_colour", "#ff00ff"},
83 {"display_file_colour", "#000000"},
84 {"display_cdev_colour", "#000000"},
85 {"display_bdev_colour", "#000000"},
86 {"display_exec_colour", "#006000"},
87 {"display_adir_colour", "#006000"}
89 #define NUM_TYPE_COLOURS\
90 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
92 /* Parsed colours for file types */
93 static Option o_type_colours[NUM_TYPE_COLOURS];
94 static GdkColor type_colours[NUM_TYPE_COLOURS];
96 /* Static prototypes */
97 static void alloc_type_colours(void);
98 static const char *import_extensions(gchar *line);
99 static void import_for_dir(guchar *path);
100 char *get_action_save_path(GtkWidget *dialog);
101 static void edit_mime_types(guchar *unused);
102 static void reread_mime_files(guchar *unused);
103 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
104 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label);
105 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label);
107 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
108 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
109 * The hash table is consulted from each dot in the string in turn
110 * (First .ps.gz, then try .gz)
112 static GHashTable *extension_hash = NULL;
113 static char *current_type = NULL; /* (used while reading file) */
114 #ifdef USE_REGEX
115 static GList *patterns = NULL; /* [(regexp -> MIME type)] */
116 #endif
117 /* Hash of all allocated MIME types, indexed by "media/subtype".
118 * MIME_type structs are never freed; this table prevents memory leaks
119 * when rereading the config files.
121 static GHashTable *type_hash = NULL;
123 /* Most things on Unix are text files, so this is the default type */
124 MIME_type *text_plain;
125 MIME_type *special_directory;
126 MIME_type *special_pipe;
127 MIME_type *special_socket;
128 MIME_type *special_block_dev;
129 MIME_type *special_char_dev;
130 MIME_type *special_exec;
131 MIME_type *special_unknown;
133 static Option o_display_colour_types;
135 void type_init(void)
137 int i;
138 GPtrArray *list;
140 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
141 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
143 text_plain = get_mime_type("text/plain", TRUE);
144 special_directory = get_mime_type("special/directory", TRUE);
145 special_pipe = get_mime_type("special/pipe", TRUE);
146 special_socket = get_mime_type("special/socket", TRUE);
147 special_block_dev = get_mime_type("special/block-device", TRUE);
148 special_char_dev = get_mime_type("special/char-device", TRUE);
149 special_exec = get_mime_type("special/executable", TRUE);
150 special_unknown = get_mime_type("special/unknown", TRUE);
152 current_type = NULL;
154 list = choices_list_dirs("MIME-info");
155 for (i = list->len - 1; i >= 0; i--)
156 import_for_dir((gchar *) g_ptr_array_index(list, i));
157 choices_free_list(list);
159 option_register_widget("type-edit", build_type_edit);
160 option_register_widget("type-reread", build_type_reread);
162 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
163 option_add_int(&o_use_gnomevfs, "use_gnomevfs", TRUE);
165 for (i = 0; i < NUM_TYPE_COLOURS; i++)
166 option_add_string(&o_type_colours[i],
167 opt_type_colours[i][0],
168 opt_type_colours[i][1]);
169 alloc_type_colours();
171 option_add_notify(alloc_type_colours);
174 /* Returns the MIME_type structure for the given type name. It is looked
175 * up in type_hash and returned if found. If not found (and can_create is
176 * TRUE) then a new MIME_type is made, added to type_hash and returned.
177 * NULL is returned if type_name is not in type_hash and can_create is
178 * FALSE, or if type_name does not contain a '/' character.
180 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
182 MIME_type *mtype;
183 gchar *slash;
185 mtype = g_hash_table_lookup(type_hash, type_name);
186 if (mtype || !can_create)
187 return mtype;
189 slash = strchr(type_name, '/');
190 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
192 mtype = g_new(MIME_type, 1);
193 mtype->media_type = g_strndup(type_name, slash - type_name);
194 mtype->subtype = g_strdup(slash + 1);
195 mtype->image = NULL;
197 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
199 return mtype;
202 #ifdef USE_REGEX
203 static void pattern_delete(gpointer patt, gpointer udata)
205 Pattern *pattern = (Pattern *) patt;
207 regfree(&pattern->buffer);
209 g_free(pattern);
211 #endif
213 static gboolean extension_delete(gpointer key, gpointer value, gpointer udata)
215 /* key is a g_strdup'ed string */
216 g_free(key);
218 /* value is also preserved in type_hash, so don't delete */
220 return TRUE; /* Removes this entry */
223 /* XXX: Dup code */
224 void type_reread(void)
226 GPtrArray *list;
227 int i;
229 g_hash_table_foreach_remove(extension_hash, extension_delete, NULL);
230 #ifdef USE_REGEX
231 g_list_foreach(patterns, pattern_delete, NULL);
232 g_list_free(patterns);
233 patterns = NULL;
234 #endif
236 current_type = NULL;
238 list = choices_list_dirs("MIME-info");
239 for (i = list->len - 1; i >= 0; i--)
240 import_for_dir((gchar *) g_ptr_array_index(list, i));
241 choices_free_list(list);
243 filer_update_all();
246 /* Parse every file in 'dir' */
247 static void import_for_dir(guchar *path)
249 DIR *dir;
250 struct dirent *item;
252 dir = opendir(path);
253 if (!dir)
254 return;
256 while ((item = readdir(dir)))
258 guchar *file;
259 struct stat info;
261 if (item->d_name[0] == '.')
262 continue;
264 current_type = NULL;
265 file = make_path(path, item->d_name)->str;
267 if (stat(file, &info) == 0 && S_ISREG(info.st_mode))
268 parse_file(file, import_extensions);
271 closedir(dir);
274 /* Add one entry to the extension_hash table */
275 static void add_ext(const char *type_name, const char *ext)
277 MIME_type *new;
279 new = get_mime_type(type_name, TRUE);
281 g_return_if_fail(new != NULL);
283 g_hash_table_insert(extension_hash, g_strdup(ext), new);
286 static void add_regex(char *type_name, char *reg)
288 #ifdef USE_REGEX
289 MIME_type *new;
290 char *slash;
291 Pattern *pattern;
293 slash = strchr(type_name, '/');
294 g_return_if_fail(slash != NULL); /* XXX: Report nicely */
296 pattern = g_new0(Pattern, 1);
298 if (regcomp(&pattern->buffer, reg, REG_EXTENDED | REG_NOSUB))
300 regfree(&pattern->buffer);
301 g_free(pattern);
302 return;
305 new = get_mime_type(type_name, TRUE);
307 g_return_if_fail(new != NULL);
309 pattern->type = new;
311 patterns = g_list_prepend(patterns, pattern);
312 #endif
315 /* Parse one line from the file and add entries to extension_hash */
316 static const char *import_extensions(gchar *line)
318 if (*line == '\0' || *line == '#')
319 return NULL; /* Comment */
321 if (isspace(*line))
323 if (!current_type)
324 return _("Missing MIME-type");
325 while (*line && isspace(*line))
326 line++;
328 if (strncmp(line, "ext:", 4) == 0)
330 char *ext;
331 line += 4;
333 for (;;)
335 while (*line && isspace(*line))
336 line++;
337 if (*line == '\0')
338 break;
339 ext = line;
340 while (*line && !isspace(*line))
341 line++;
342 if (*line)
343 *line++ = '\0';
344 add_ext(current_type, ext);
347 else if (strncmp(line, "regex:", 6) == 0)
349 line += 6;
351 while (*line && isspace(*line))
352 line++;
353 if (*line)
354 add_regex(current_type, line);
356 /* else ignore */
358 else
360 char *type = line;
361 while (*line && *line != ':' && !isspace(*line))
362 line++;
363 if (*line)
364 *line++ = '\0';
365 while (*line && isspace(*line))
366 line++;
367 if (*line)
368 return _("Trailing chars after MIME-type");
369 current_type = g_strdup(type);
371 return NULL;
374 const char *basetype_name(DirItem *item)
376 if (item->flags & ITEM_FLAG_SYMLINK)
377 return _("Sym link");
378 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
379 return _("Mount point");
380 else if (item->flags & ITEM_FLAG_APPDIR)
381 return _("App dir");
383 switch (item->base_type)
385 case TYPE_FILE:
386 return _("File");
387 case TYPE_DIRECTORY:
388 return _("Dir");
389 case TYPE_CHAR_DEVICE:
390 return _("Char dev");
391 case TYPE_BLOCK_DEVICE:
392 return _("Block dev");
393 case TYPE_PIPE:
394 return _("Pipe");
395 case TYPE_SOCKET:
396 return _("Socket");
399 return _("Unknown");
402 /* MIME-type guessing */
404 /* Get the type of this file - stats the file and uses that if
405 * possible. For regular or missing files, uses the pathname.
407 MIME_type *type_get_type(const guchar *path)
409 struct stat info;
410 MIME_type *type = NULL;
411 int base = TYPE_FILE;
412 gboolean exec = FALSE;
414 if (mc_stat(path, &info) == 0)
416 base = mode_to_base_type(info.st_mode);
417 if (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
418 exec = TRUE;
421 if (base == TYPE_FILE)
422 type = type_from_path(path);
424 if (!type)
426 if (base == TYPE_FILE && exec)
427 type = special_exec;
428 else
429 type = mime_type_from_base_type(base);
432 return type;
435 /* Returns a pointer to the MIME-type.
436 * NULL if we can't think of anything.
438 MIME_type *type_from_path(const char *path)
440 const char *ext, *dot, *leafname;
441 char *lower;
442 #ifdef USE_REGEX
443 GList *patt;
444 int len;
445 #endif
447 #ifdef WITH_GNOMEVFS
448 if (o_use_gnomevfs.int_value)
449 return get_mime_type(gnome_vfs_mime_type_from_name(path), TRUE);
450 #endif
452 leafname = strrchr(path, '/');
453 if (!leafname)
454 leafname = path;
455 else
456 leafname++;
457 ext = leafname;
459 while ((dot = strchr(ext, '.')))
461 MIME_type *type;
463 ext = dot + 1;
465 type = g_hash_table_lookup(extension_hash, ext);
467 if (type)
468 return type;
470 lower = g_strdup(ext);
471 g_strdown(lower);
472 type = g_hash_table_lookup(extension_hash, lower);
473 g_free(lower);
475 if (type)
476 return type;
479 #ifdef USE_REGEX
480 len = strlen(leafname);
482 for (patt = patterns; patt; patt = patt->next)
484 Pattern *pattern = (Pattern *) patt->data;
486 if (regexec(&pattern->buffer, leafname, 0, NULL, 0) == 0)
487 return pattern->type;
489 #endif
491 return NULL;
494 /* Returns the file/dir in Choices for handling this type.
495 * NULL if there isn't one. g_free() the result.
497 static char *handler_for(MIME_type *type)
499 char *type_name;
500 char *open;
502 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
503 open = choices_find_path_load(type_name, "MIME-types");
504 g_free(type_name);
506 if (!open)
507 open = choices_find_path_load(type->media_type, "MIME-types");
509 return open;
512 /* Actions for types */
514 gboolean type_open(const char *path, MIME_type *type)
516 gchar *argv[] = {NULL, NULL, NULL};
517 char *open;
518 gboolean retval;
519 struct stat info;
521 argv[1] = (char *) path;
523 open = handler_for(type);
524 if (!open)
525 return FALSE;
527 if (stat(open, &info))
529 report_error("stat(%s): %s", open, g_strerror(errno));
530 g_free(open);
531 return FALSE;
534 if (S_ISDIR(info.st_mode))
535 argv[0] = g_strconcat(open, "/AppRun", NULL);
536 else
537 argv[0] = open;
539 retval = rox_spawn(home_dir, (const gchar **) argv);
541 if (argv[0] != open)
542 g_free(argv[0]);
544 g_free(open);
546 return retval;
549 /* Return the image for this type, loading it if needed.
550 * Places to check are: (eg type="text_plain", base="text")
551 * 1. Choices:MIME-icons/<type>
552 * 2. Choices:MIME-icons/<base>
553 * 3. Unknown type icon.
555 * Note: You must g_object_unref() the image afterwards.
557 MaskedPixmap *type_to_icon(MIME_type *type)
559 char *path;
560 char *type_name;
561 time_t now;
563 if (type == NULL)
565 g_object_ref(im_unknown);
566 return im_unknown;
569 now = time(NULL);
570 /* Already got an image? */
571 if (type->image)
573 /* Yes - don't recheck too often */
574 if (abs(now - type->image_time) < 2)
576 g_object_ref(type->image);
577 return type->image;
579 g_object_unref(type->image);
580 type->image = NULL;
583 type_name = g_strconcat(type->media_type, "_",
584 type->subtype, ".xpm", NULL);
585 path = choices_find_path_load(type_name, "MIME-icons");
586 if (!path)
588 strcpy(type_name + strlen(type->media_type), ".xpm");
589 path = choices_find_path_load(type_name, "MIME-icons");
592 g_free(type_name);
594 if (path)
596 type->image = g_fscache_lookup(pixmap_cache, path);
597 g_free(path);
600 if (!type->image)
602 /* One ref from the type structure, one returned */
603 type->image = im_unknown;
604 g_object_ref(im_unknown);
607 type->image_time = now;
609 g_object_ref(type->image);
610 return type->image;
613 GdkAtom type_to_atom(MIME_type *type)
615 char *str;
616 GdkAtom retval;
618 g_return_val_if_fail(type != NULL, GDK_NONE);
620 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
621 retval = gdk_atom_intern(str, FALSE);
622 g_free(str);
624 return retval;
627 void show_shell_help(gpointer data)
629 report_error(_("Enter a shell command which will load \"$1\" into "
630 "a suitable program. Eg:\n\n"
631 "gimp \"$1\""));
634 /* Called if the user clicks on the OK button */
635 static void set_shell_action(GtkWidget *dialog)
637 GtkEntry *entry;
638 GtkToggleButton *for_all;
639 const guchar *command;
640 gchar *tmp, *path;
641 int error = 0, len;
642 FILE *file;
644 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
645 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
646 g_return_if_fail(entry != NULL);
648 command = gtk_entry_get_text(entry);
650 if (!strchr(command, '$'))
652 show_shell_help(NULL);
653 return;
656 path = get_action_save_path(dialog);
657 if (!path)
658 return;
660 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
661 len = strlen(tmp);
663 file = fopen(path, "wb");
664 if (fwrite(tmp, 1, len, file) < len)
665 error = errno;
666 if (fclose(file) && error == 0)
667 error = errno;
668 if (chmod(path, 0777))
669 error = errno;
671 if (error)
672 report_error(g_strerror(errno));
674 g_free(tmp);
675 g_free(path);
677 gtk_widget_destroy(dialog);
680 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
682 if (response == GTK_RESPONSE_OK)
683 set_shell_action(dialog);
684 gtk_widget_destroy(dialog);
687 /* Called when a URI list is dropped onto the box in the Set Run Action
688 * dialog. Make sure it's an application, and make that the default
689 * handler.
691 void drag_app_dropped(GtkWidget *eb,
692 GdkDragContext *context,
693 gint x,
694 gint y,
695 GtkSelectionData *selection_data,
696 guint info,
697 guint32 time,
698 GtkWidget *dialog)
700 GList *uris;
701 const gchar *app = NULL;
702 DirItem *item;
704 if (!selection_data->data)
705 return; /* Timeout? */
707 uris = uri_list_to_glist(selection_data->data);
709 if (g_list_length(uris) == 1)
710 app = get_local_path((guchar *) uris->data);
711 g_list_free(uris);
713 if (!app)
715 delayed_error(
716 _("You should drop a single (local) application "
717 "onto the drop box - that application will be "
718 "used to load files of this type in future"));
719 return;
722 item = diritem_new(NULL);
723 diritem_restat(app, item);
724 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
726 guchar *path;
728 path = get_action_save_path(dialog);
730 if (path)
732 if (symlink(app, path))
733 delayed_error("symlink: %s",
734 g_strerror(errno));
735 else
736 destroy_on_idle(dialog);
739 g_free(path);
741 else
742 delayed_error(
743 _("This is not a program! Give me an application "
744 "instead!"));
746 diritem_free(item);
749 /* Find the current command which is used to run files of this type.
750 * Returns NULL on failure. g_free() the result.
752 static guchar *get_current_command(MIME_type *type)
754 struct stat info;
755 char *handler, *nl, *data = NULL;
756 long len;
757 guchar *command = NULL;
759 handler = handler_for(type);
761 if (!handler)
762 return NULL; /* No current handler */
764 if (stat(handler, &info))
765 goto out; /* Can't stat */
767 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
768 goto out; /* Only use small regular files */
770 if (!load_file(handler, &data, &len))
771 goto out; /* Didn't load OK */
773 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
774 goto out; /* Not one of ours */
776 nl = strchr(data + 16, '\n');
777 if (!nl)
778 goto out; /* No newline! */
780 command = g_strndup(data + 16, nl - data - 16);
781 out:
782 g_free(handler);
783 g_free(data);
784 return command;
787 /* Find the current command which is used to run files of this type,
788 * and return a textual description of it.
789 * g_free() the result.
791 gchar *describe_current_command(MIME_type *type)
793 char *handler;
794 char *desc = NULL;
795 struct stat info;
796 char *target;
798 g_return_val_if_fail(type != NULL, NULL);
800 if (type == special_exec)
801 return g_strdup(_("Execute file"));
803 handler = handler_for(type);
805 if (!handler)
806 return g_strdup(_("No run action defined"));
808 target = readlink_dup(handler);
809 if (target)
811 /* Cope with relative paths (shouldn't normally be needed) */
813 if (target[0] == '/')
815 g_free(handler);
816 handler = target;
818 else
820 gchar *dir;
822 dir = g_dirname(handler);
823 g_free(handler);
824 handler = g_strconcat(dir, "/", target, NULL);
825 g_free(target);
826 g_free(dir);
830 if (mc_stat(handler, &info) !=0 )
832 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
833 g_strerror(errno));
834 goto out;
837 if (S_ISDIR(info.st_mode))
839 gchar *tmp;
840 uid_t dir_uid = info.st_uid;
842 tmp = make_path(handler, "AppRun")->str;
844 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
845 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
846 desc = g_strdup_printf(
847 _("Invalid application %s (bad AppRun)"),
848 handler);
849 /* Else, just report handler... */
851 goto out;
854 /* It's not an application directory, and it's not a symlink... */
856 if (access(handler, X_OK) != 0)
858 desc = g_strdup_printf(_("Non-executable %s"), handler);
859 goto out;
862 desc = get_current_command(type);
863 out:
864 if (!desc)
865 desc = handler;
866 else
867 g_free(handler);
869 return desc;
872 /* Display a dialog box allowing the user to set the default run action
873 * for this type.
875 void type_set_handler_dialog(MIME_type *type)
877 guchar *tmp;
878 gchar *handler;
879 GtkDialog *dialog;
880 GtkWidget *frame, *entry, *label;
881 GtkWidget *radio, *eb, *hbox;
882 GtkTargetEntry targets[] = {
883 {"text/uri-list", 0, TARGET_URI_LIST},
886 g_return_if_fail(type != NULL);
888 dialog = GTK_DIALOG(gtk_dialog_new());
889 gtk_dialog_set_has_separator(dialog, FALSE);
890 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
892 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
894 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
896 tmp = g_strdup_printf(_("Set default for all `%s/<anything>'"),
897 type->media_type);
898 radio = gtk_radio_button_new_with_label(NULL, tmp);
899 g_free(tmp);
900 g_object_set_data(G_OBJECT(dialog), "set_for_all", radio);
902 tmp = g_strdup_printf(_("Only for the type `%s/%s'"), type->media_type,
903 type->subtype);
904 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
905 radio = gtk_radio_button_new_with_label(
906 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)),
907 tmp);
908 g_free(tmp);
909 gtk_box_pack_start(GTK_BOX(dialog->vbox), radio, FALSE, TRUE, 0);
910 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
912 frame = gtk_frame_new(NULL);
913 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 4);
914 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
915 eb = gtk_event_box_new();
916 gtk_container_add(GTK_CONTAINER(frame), eb);
918 gtk_container_set_border_width(GTK_CONTAINER(eb), 4);
920 handler = handler_for(type);
921 if (handler)
923 char *link;
925 link = readlink_dup(handler);
926 if (link)
928 char *msg;
930 msg = g_strdup_printf(_("Currently %s"), link);
931 gtk_tooltips_set_tip(tooltips, eb, msg, NULL);
932 g_free(link);
933 g_free(msg);
935 g_free(handler);
938 gtk_drag_dest_set(eb, GTK_DEST_DEFAULT_ALL,
939 targets, sizeof(targets) / sizeof(*targets),
940 GDK_ACTION_COPY);
941 g_signal_connect(eb, "drag_data_received",
942 G_CALLBACK(drag_app_dropped), dialog);
944 label = gtk_label_new(_("Drop a suitable\napplication here"));
945 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
946 gtk_container_add(GTK_CONTAINER(eb), label);
948 hbox = gtk_hbox_new(FALSE, 4);
949 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
950 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
951 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
952 FALSE, TRUE, 0);
953 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
955 hbox = gtk_hbox_new(FALSE, 4);
956 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
958 label = gtk_label_new(_("Enter a shell command:")),
959 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
960 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
962 gtk_box_pack_start(GTK_BOX(hbox),
963 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
965 entry = gtk_entry_new();
966 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
967 gtk_widget_grab_focus(entry);
968 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
969 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
971 /* If possible, fill in the entry box with the current command */
972 tmp = get_current_command(type);
973 if (tmp)
975 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
976 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
977 g_free(tmp);
979 else
981 gtk_entry_set_text(GTK_ENTRY(entry), " \"$1\"");
982 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
985 gtk_dialog_add_buttons(dialog,
986 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
987 GTK_STOCK_OK, GTK_RESPONSE_OK,
988 NULL);
990 hbox = gtk_hbox_new(TRUE, 4);
991 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
993 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
995 g_signal_connect(dialog, "response",
996 G_CALLBACK(set_action_response), NULL);
998 gtk_widget_show_all(GTK_WIDGET(dialog));
1001 /* The user wants to set a new default action for files of this type.
1002 * Removes the current binding if possible and returns the path to
1003 * save the new one to. NULL means cancel. g_free() the result.
1005 char *get_action_save_path(GtkWidget *dialog)
1007 guchar *path = NULL;
1008 struct stat info;
1009 guchar *type_name = NULL;
1010 MIME_type *type;
1011 GtkToggleButton *for_all;
1013 g_return_val_if_fail(dialog != NULL, NULL);
1014 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
1015 for_all = g_object_get_data(G_OBJECT(dialog), "set_for_all");
1016 g_return_val_if_fail(for_all != NULL && type != NULL, NULL);
1018 if (gtk_toggle_button_get_active(for_all))
1019 type_name = g_strdup(type->media_type);
1020 else
1021 type_name = g_strconcat(type->media_type, "_",
1022 type->subtype, NULL);
1024 path = choices_find_path_save("", PROJECT, FALSE);
1025 if (!path)
1027 report_error(
1028 _("Choices saving is disabled by CHOICESPATH variable"));
1029 goto out;
1031 g_free(path);
1033 path = choices_find_path_save(type_name, "MIME-types", TRUE);
1035 if (lstat(path, &info) == 0)
1037 /* A binding already exists... */
1038 if (S_ISREG(info.st_mode) && info.st_size > 256)
1040 if (get_choice(PROJECT,
1041 _("A run action already exists and is quite "
1042 "a big program - are you sure you want to "
1043 "delete it?"), 2, "Cancel", "Delete") != 1)
1045 g_free(path);
1046 path = NULL;
1047 goto out;
1051 if (unlink(path))
1053 report_error(_("Can't remove %s: %s"),
1054 path, g_strerror(errno));
1055 g_free(path);
1056 path = NULL;
1057 goto out;
1061 out:
1062 g_free(type_name);
1063 return path;
1066 MIME_type *mime_type_from_base_type(int base_type)
1068 switch (base_type)
1070 case TYPE_FILE:
1071 return text_plain;
1072 case TYPE_DIRECTORY:
1073 return special_directory;
1074 case TYPE_PIPE:
1075 return special_pipe;
1076 case TYPE_SOCKET:
1077 return special_socket;
1078 case TYPE_BLOCK_DEVICE:
1079 return special_block_dev;
1080 case TYPE_CHAR_DEVICE:
1081 return special_char_dev;
1083 return special_unknown;
1086 /* Takes the st_mode field from stat() and returns the base type.
1087 * Should not be a symlink.
1089 int mode_to_base_type(int st_mode)
1091 if (S_ISREG(st_mode))
1092 return TYPE_FILE;
1093 else if (S_ISDIR(st_mode))
1094 return TYPE_DIRECTORY;
1095 else if (S_ISBLK(st_mode))
1096 return TYPE_BLOCK_DEVICE;
1097 else if (S_ISCHR(st_mode))
1098 return TYPE_CHAR_DEVICE;
1099 else if (S_ISFIFO(st_mode))
1100 return TYPE_PIPE;
1101 else if (S_ISSOCK(st_mode))
1102 return TYPE_SOCKET;
1104 return TYPE_ERROR;
1107 /* Returns TRUE is this is something that is run by looking up its type
1108 * in MIME-types and, hence, can have its run action set.
1110 gboolean can_set_run_action(DirItem *item)
1112 g_return_val_if_fail(item != NULL, FALSE);
1114 return item->base_type == TYPE_FILE &&
1115 !(item->mime_type == special_exec);
1118 /* Open all <Choices>/type directories and display a message */
1119 static void open_choices_dirs(gchar *type, gchar *what)
1121 guchar *dir;
1122 GPtrArray *list;
1123 int i;
1125 dir = choices_find_path_save("", type, TRUE);
1126 g_free(dir);
1127 list = choices_list_dirs(type);
1129 for (i = list->len - 1; i >= 0; i--)
1130 filer_opendir(list->pdata[i], NULL);
1132 choices_free_list(list);
1135 /* To edit the MIME types, open a filer window for <Choices>/MIME-info */
1136 static void edit_mime_types(guchar *unused)
1138 open_choices_dirs("MIME-info", "the files defining MIME types");
1141 static void reread_mime_files(guchar *unused)
1143 type_reread();
1146 static GList *build_type_reread(Option *none, xmlNode *node, guchar *label)
1148 GtkWidget *button, *align;
1150 g_return_val_if_fail(none == NULL, NULL);
1152 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1153 button = gtk_button_new_with_label(_(label));
1154 gtk_container_add(GTK_CONTAINER(align), button);
1156 g_signal_connect_swapped(button, "clicked",
1157 G_CALLBACK(reread_mime_files), NULL);
1159 return g_list_append(NULL, align);
1162 static GList *build_type_edit(Option *none, xmlNode *node, guchar *label)
1164 GtkWidget *button, *align;
1166 g_return_val_if_fail(none == NULL, NULL);
1168 align = gtk_alignment_new(0.1, 0, 0.1, 0);
1169 button = gtk_button_new_with_label(_(label));
1170 gtk_container_add(GTK_CONTAINER(align), button);
1172 g_signal_connect_swapped(button, "clicked",
1173 G_CALLBACK(edit_mime_types), NULL);
1175 return g_list_append(NULL, align);
1178 /* Parse file type colours and allocate/free them as necessary */
1179 static void alloc_type_colours(void)
1181 gboolean success[NUM_TYPE_COLOURS];
1182 int change_count = 0; /* No. needing realloc */
1183 int i;
1184 static gboolean allocated = FALSE;
1186 /* Parse colours */
1187 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1189 GdkColor *c = &type_colours[i];
1190 gushort r = c->red;
1191 gushort g = c->green;
1192 gushort b = c->blue;
1194 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1196 if (allocated && (c->red != r || c->green != g || c->blue != b))
1197 change_count++;
1200 /* Free colours if they were previously allocated and
1201 * have changed or become unneeded.
1203 if (allocated && (change_count || !o_display_colour_types.int_value))
1205 gdk_colormap_free_colors(gdk_rgb_get_cmap(),
1206 type_colours, NUM_TYPE_COLOURS);
1207 allocated = FALSE;
1210 /* Allocate colours, unless they are still allocated (=> they didn't
1211 * change) or we don't want them anymore.
1212 * XXX: what should be done if allocation fails?
1214 if (!allocated && o_display_colour_types.int_value)
1216 gdk_colormap_alloc_colors(gdk_rgb_get_cmap(),
1217 type_colours, NUM_TYPE_COLOURS,
1218 FALSE, TRUE, success);
1219 allocated = TRUE;
1223 /* Return a pointer to a (static) colour for this item. If colouring is
1224 * off, returns normal.
1226 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1228 if (!o_display_colour_types.int_value)
1229 return normal;
1231 if (item->flags & ITEM_FLAG_EXEC_FILE)
1232 return &type_colours[8];
1233 else if (item->flags & ITEM_FLAG_APPDIR)
1234 return &type_colours[9];
1235 else
1236 return &type_colours[item->base_type];