r3768: Updated years.
[rox-filer.git] / ROX-Filer / src / type.c
blobe865b1ff76109d3fa5d5f3bf19a9adbe50450f94
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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>
33 #include <sys/types.h>
34 #include <fcntl.h>
36 #ifdef WITH_GNOMEVFS
37 # include <libgnomevfs/gnome-vfs.h>
38 # include <libgnomevfs/gnome-vfs-mime.h>
39 # include <libgnomevfs/gnome-vfs-mime-handlers.h>
40 # include <libgnomevfs/gnome-vfs-application-registry.h>
41 #endif
43 #include "global.h"
45 #include "string.h"
46 #include "fscache.h"
47 #include "main.h"
48 #include "pixmaps.h"
49 #include "run.h"
50 #include "gui_support.h"
51 #include "choices.h"
52 #include "type.h"
53 #include "support.h"
54 #include "diritem.h"
55 #include "dnd.h"
56 #include "options.h"
57 #include "filer.h"
58 #include "action.h" /* (for action_chmod) */
59 #include "xml.h"
60 #include "dropbox.h"
61 #include "gtkicontheme.h"
63 #define TYPE_NS "http://www.freedesktop.org/standards/shared-mime-info"
64 enum {SET_MEDIA, SET_TYPE};
66 /* Colours for file types (same order as base types) */
67 static gchar *opt_type_colours[][2] = {
68 {"display_err_colour", "#ff0000"},
69 {"display_unkn_colour", "#000000"},
70 {"display_dir_colour", "#000080"},
71 {"display_pipe_colour", "#444444"},
72 {"display_sock_colour", "#ff00ff"},
73 {"display_file_colour", "#000000"},
74 {"display_cdev_colour", "#000000"},
75 {"display_bdev_colour", "#000000"},
76 {"display_door_colour", "#ff00ff"},
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 load_mime_types(void);
89 static void alloc_type_colours(void);
90 static void options_changed(void);
91 static char *get_action_save_path(GtkWidget *dialog);
92 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
93 static gboolean remove_handler_with_confirm(const guchar *path);
94 static void set_icon_theme(void);
95 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label);
97 /* When working out the type for a file, this hash table is checked
98 * first...
100 static GHashTable *literal_hash = NULL; /* name -> MIME-type */
102 /* Maps extensions to MIME_types (eg 'png'-> MIME_type *).
103 * Extensions may contain dots; 'tar.gz' matches '*.tar.gz', etc.
104 * The hash table is consulted from each dot in the string in turn
105 * (First .ps.gz, then try .gz)
107 static GHashTable *extension_hash = NULL;
109 /* The first pattern in the list which matches is used */
110 typedef struct pattern {
111 gint len; /* Used for sorting */
112 gchar *glob;
113 MIME_type *type;
114 } Pattern;
115 static GPtrArray *glob_patterns = NULL; /* [Pattern] */
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 *inode_directory;
126 MIME_type *inode_mountpoint;
127 MIME_type *inode_pipe;
128 MIME_type *inode_socket;
129 MIME_type *inode_block_dev;
130 MIME_type *inode_char_dev;
131 MIME_type *application_executable;
132 MIME_type *inode_unknown;
133 MIME_type *inode_door;
135 static Option o_display_colour_types;
136 static Option o_icon_theme;
138 static RoxIconTheme *icon_theme = NULL;
140 void type_init(void)
142 int i;
144 icon_theme = rox_icon_theme_new();
146 extension_hash = g_hash_table_new(g_str_hash, g_str_equal);
147 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
149 text_plain = get_mime_type("text/plain", TRUE);
150 inode_directory = get_mime_type("inode/directory", TRUE);
151 inode_mountpoint = get_mime_type("inode/mount-point", TRUE);
152 inode_pipe = get_mime_type("inode/fifo", TRUE);
153 inode_socket = get_mime_type("inode/socket", TRUE);
154 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
155 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
156 application_executable = get_mime_type("application/x-executable", TRUE);
157 inode_unknown = get_mime_type("inode/unknown", TRUE);
158 inode_door = get_mime_type("inode/door", TRUE);
160 load_mime_types();
162 option_add_string(&o_icon_theme, "icon_theme", "ROX");
163 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
164 option_register_widget("icon-theme-chooser", build_icon_theme);
166 for (i = 0; i < NUM_TYPE_COLOURS; i++)
167 option_add_string(&o_type_colours[i],
168 opt_type_colours[i][0],
169 opt_type_colours[i][1]);
170 alloc_type_colours();
172 set_icon_theme();
174 option_add_notify(options_changed);
177 /* Read-load all the glob patterns.
178 * Note: calls filer_update_all.
180 void reread_mime_files(void)
182 rox_icon_theme_rescan_if_needed(icon_theme);
183 load_mime_types();
186 /* Returns the MIME_type structure for the given type name. It is looked
187 * up in type_hash and returned if found. If not found (and can_create is
188 * TRUE) then a new MIME_type is made, added to type_hash and returned.
189 * NULL is returned if type_name is not in type_hash and can_create is
190 * FALSE, or if type_name does not contain a '/' character.
192 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
194 MIME_type *mtype;
195 gchar *slash;
197 mtype = g_hash_table_lookup(type_hash, type_name);
198 if (mtype || !can_create)
199 return mtype;
201 slash = strchr(type_name, '/');
202 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
204 mtype = g_new(MIME_type, 1);
205 mtype->media_type = g_strndup(type_name, slash - type_name);
206 mtype->subtype = g_strdup(slash + 1);
207 mtype->image = NULL;
208 mtype->comment = NULL;
210 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
212 return mtype;
215 /* NULL if we don't know / don't support contents checking */
216 MIME_type *mime_type_from_contents(const char *path)
218 MIME_type *type = NULL;
219 #ifdef HAVE_GNOME_VFS
220 char *type_name;
222 type_name = gnome_vfs_get_mime_type(path);
223 if (type_name)
225 type = get_mime_type(type_name, TRUE);
226 free(type_name);
228 #endif
229 return type;
232 const char *basetype_name(DirItem *item)
234 if (item->flags & ITEM_FLAG_SYMLINK)
235 return _("Sym link");
236 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
237 return _("Mount point");
238 else if (item->flags & ITEM_FLAG_APPDIR)
239 return _("App dir");
241 switch (item->base_type)
243 case TYPE_FILE:
244 return _("File");
245 case TYPE_DIRECTORY:
246 return _("Dir");
247 case TYPE_CHAR_DEVICE:
248 return _("Char dev");
249 case TYPE_BLOCK_DEVICE:
250 return _("Block dev");
251 case TYPE_PIPE:
252 return _("Pipe");
253 case TYPE_SOCKET:
254 return _("Socket");
255 case TYPE_DOOR:
256 return _("Door");
259 return _("Unknown");
262 static void append_names(gpointer key, gpointer value, gpointer udata)
264 GList **list = (GList **) udata;
266 *list = g_list_prepend(*list, key);
269 /* Return list of all mime type names. Caller must free the list
270 * but NOT the strings it contains (which are never freed).
272 GList *mime_type_name_list(void)
274 GList *list = NULL;
276 g_hash_table_foreach(type_hash, append_names, &list);
277 list = g_list_sort(list, (GCompareFunc) strcmp);
279 return list;
282 /* MIME-type guessing */
284 /* Get the type of this file - stats the file and uses that if
285 * possible. For regular or missing files, uses the pathname.
287 MIME_type *type_get_type(const guchar *path)
289 DirItem *item;
290 MIME_type *type = NULL;
292 item = diritem_new("");
293 diritem_restat(path, item, NULL);
294 if (item->base_type != TYPE_ERROR)
295 type = item->mime_type;
296 diritem_free(item);
298 if (type)
299 return type;
301 type = type_from_path(path);
303 if (!type)
304 return text_plain;
306 return type;
309 /* Returns a pointer to the MIME-type.
310 * NULL if we can't think of anything.
312 MIME_type *type_from_path(const char *path)
314 const char *ext, *dot, *leafname;
315 char *lower;
316 MIME_type *type = NULL;
317 int i;
319 leafname = g_basename(path);
321 type = g_hash_table_lookup(literal_hash, leafname);
322 if (type)
323 return type;
324 lower = g_utf8_strdown(leafname, -1);
325 type = g_hash_table_lookup(literal_hash, lower);
326 if (type)
327 goto out;
329 ext = leafname;
331 while ((dot = strchr(ext, '.')))
333 ext = dot + 1;
335 type = g_hash_table_lookup(extension_hash, ext);
337 if (type)
338 goto out;
340 type = g_hash_table_lookup(extension_hash,
341 lower + (ext - leafname));
342 if (type)
343 goto out;
346 for (i = 0; i < glob_patterns->len; i++)
348 Pattern *p = glob_patterns->pdata[i];
350 if (fnmatch(p->glob, leafname, 0) == 0 ||
351 fnmatch(p->glob, lower, 0) == 0)
353 type = p->type;
354 goto out;
358 out:
359 g_free(lower);
361 return type;
364 /* Returns the file/dir in Choices for handling this type.
365 * NULL if there isn't one. g_free() the result.
367 static char *handler_for(MIME_type *type)
369 char *type_name;
370 char *open;
372 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
373 open = choices_find_path_load(type_name, "MIME-types");
374 g_free(type_name);
376 if (!open)
377 open = choices_find_path_load(type->media_type, "MIME-types");
379 return open;
382 MIME_type *mime_type_lookup(const char *type)
384 return get_mime_type(type, TRUE);
387 /* Actions for types */
389 gboolean type_open(const char *path, MIME_type *type)
391 gchar *argv[] = {NULL, NULL, NULL};
392 char *open;
393 gboolean retval;
394 struct stat info;
396 argv[1] = (char *) path;
398 open = handler_for(type);
399 if (!open)
400 return FALSE;
402 if (stat(open, &info))
404 report_error("stat(%s): %s", open, g_strerror(errno));
405 g_free(open);
406 return FALSE;
409 if (info.st_mode & S_IWOTH)
411 gchar *choices_dir;
412 GList *paths;
414 report_error(_("Executable '%s' is world-writeable! Refusing "
415 "to run. Please change the permissions now (this "
416 "problem may have been caused by a bug in earlier "
417 "versions of the filer).\n\n"
418 "Having (non-symlink) run actions world-writeable "
419 "means that other people who use your computer can "
420 "replace your run actions with malicious versions.\n\n"
421 "If you trust everyone who could write to these files "
422 "then you needn't worry. Otherwise, you should check, "
423 "or even just delete, all the existing run actions."),
424 open);
425 choices_dir = g_path_get_dirname(open);
426 paths = g_list_append(NULL, choices_dir);
427 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
428 g_free(choices_dir);
429 g_list_free(paths);
430 g_free(open);
431 return TRUE;
434 if (S_ISDIR(info.st_mode))
435 argv[0] = g_strconcat(open, "/AppRun", NULL);
436 else
437 argv[0] = open;
439 retval = rox_spawn(home_dir, (const gchar **) argv) != 0;
441 if (argv[0] != open)
442 g_free(argv[0]);
444 g_free(open);
446 return retval;
449 /* Return the image for this type, loading it if needed.
450 * Places to check are: (eg type="text_plain", base="text")
451 * 1. <Choices>/MIME-icons/base_subtype
452 * 2. Icon theme 'mime-base:subtype'
453 * 3. Icon theme 'mime-base'
454 * 4. Unknown type icon.
456 * Note: You must g_object_unref() the image afterwards.
458 MaskedPixmap *type_to_icon(MIME_type *type)
460 GdkPixbuf *full;
461 char *type_name, *path;
462 time_t now;
464 if (type == NULL)
466 g_object_ref(im_unknown);
467 return im_unknown;
470 now = time(NULL);
471 /* Already got an image? */
472 if (type->image)
474 /* Yes - don't recheck too often */
475 if (abs(now - type->image_time) < 2)
477 g_object_ref(type->image);
478 return type->image;
480 g_object_unref(type->image);
481 type->image = NULL;
484 type_name = g_strconcat(type->media_type, "_", type->subtype,
485 ".png", NULL);
486 path = choices_find_path_load(type_name, "MIME-icons");
487 g_free(type_name);
488 if (path)
490 type->image = g_fscache_lookup(pixmap_cache, path);
491 g_free(path);
494 if (type->image)
495 goto out;
497 type_name = g_strconcat("mime-", type->media_type, ":",
498 type->subtype, NULL);
499 full = rox_icon_theme_load_icon(icon_theme, type_name, HUGE_HEIGHT,
500 0, NULL);
501 g_free(type_name);
502 if (!full)
504 /* Ugly hack... try for a GNOME icon */
505 type_name = g_strconcat("gnome-mime-", type->media_type,
506 "-", type->subtype, NULL);
507 full = rox_icon_theme_load_icon(icon_theme,
508 type_name,
509 HUGE_HEIGHT, 0, NULL);
510 g_free(type_name);
512 if (!full)
514 /* Try for a media type */
515 type_name = g_strconcat("mime-", type->media_type, NULL);
516 full = rox_icon_theme_load_icon(icon_theme,
517 type_name,
518 HUGE_HEIGHT, 0, NULL);
519 g_free(type_name);
521 if (full)
523 type->image = masked_pixmap_new(full);
524 g_object_unref(full);
527 out:
528 if (!type->image)
530 /* One ref from the type structure, one returned */
531 type->image = im_unknown;
532 g_object_ref(im_unknown);
535 type->image_time = now;
537 g_object_ref(type->image);
538 return type->image;
541 GdkAtom type_to_atom(MIME_type *type)
543 char *str;
544 GdkAtom retval;
546 g_return_val_if_fail(type != NULL, GDK_NONE);
548 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
549 retval = gdk_atom_intern(str, FALSE);
550 g_free(str);
552 return retval;
555 static void show_shell_help(gpointer data)
557 info_message(_("Enter a shell command which will load \"$@\" into "
558 "a suitable program. Eg:\n\n"
559 "gimp \"$@\""));
562 /* Called if the user clicks on the OK button. Returns FALSE if an error
563 * was displayed instead of performing the action.
565 static gboolean set_shell_action(GtkWidget *dialog)
567 GtkEntry *entry;
568 const guchar *command;
569 gchar *tmp, *path;
570 int error = 0, len;
571 int fd;
573 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
575 g_return_val_if_fail(entry != NULL, FALSE);
577 command = gtk_entry_get_text(entry);
579 if (!strchr(command, '$'))
581 show_shell_help(NULL);
582 return FALSE;
585 path = get_action_save_path(dialog);
586 if (!path)
587 return FALSE;
589 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
590 len = strlen(tmp);
592 fd = open(path, O_CREAT | O_WRONLY, 0755);
593 if (fd == -1)
594 error = errno;
595 else
597 FILE *file;
599 file = fdopen(fd, "w");
600 if (file)
602 if (fwrite(tmp, 1, len, file) < len)
603 error = errno;
604 if (fclose(file) && error == 0)
605 error = errno;
607 else
608 error = errno;
611 if (error)
612 report_error(g_strerror(error));
614 g_free(tmp);
615 g_free(path);
617 gtk_widget_destroy(dialog);
619 return TRUE;
622 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
624 if (response == GTK_RESPONSE_OK)
625 if (!set_shell_action(dialog))
626 return;
627 gtk_widget_destroy(dialog);
630 /* Return the path of the file in choices that handles this type and
631 * radio setting.
632 * NULL if nothing is defined for it.
634 static guchar *handler_for_radios(GObject *dialog)
636 Radios *radios;
637 MIME_type *type;
639 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
640 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
642 g_return_val_if_fail(radios != NULL, NULL);
643 g_return_val_if_fail(type != NULL, NULL);
645 switch (radios_get_value(radios))
647 case SET_MEDIA:
648 return choices_find_path_load(type->media_type,
649 "MIME-types");
650 case SET_TYPE:
652 gchar *tmp, *handler;
653 tmp = g_strconcat(type->media_type, "_",
654 type->subtype, NULL);
655 handler = choices_find_path_load(tmp, "MIME-types");
656 g_free(tmp);
657 return handler;
659 default:
660 g_warning("Bad type");
661 return NULL;
665 static void run_action_update(gpointer data)
667 guchar *handler;
668 DropBox *drop_box;
669 GObject *dialog = G_OBJECT(data);
671 drop_box = g_object_get_data(dialog, "rox-dropbox");
673 g_return_if_fail(drop_box != NULL);
675 handler = handler_for_radios(dialog);
677 if (handler)
679 char *old = handler;
681 handler = readlink_dup(old);
682 if (handler)
683 g_free(old);
684 else
685 handler = old;
688 drop_box_set_path(DROP_BOX(drop_box), handler);
689 g_free(handler);
692 static void clear_run_action(GtkWidget *drop_box, GtkWidget *dialog)
694 guchar *handler;
696 handler = handler_for_radios(G_OBJECT(dialog));
698 if (handler)
699 remove_handler_with_confirm(handler);
701 run_action_update(dialog);
704 /* Called when a URI list is dropped onto the box in the Set Run Action
705 * dialog. Make sure it's an application, and make that the default
706 * handler.
708 static void drag_app_dropped(GtkWidget *drop_box,
709 const guchar *app,
710 GtkWidget *dialog)
712 DirItem *item;
714 item = diritem_new("");
715 diritem_restat(app, item, NULL);
716 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
718 guchar *path;
720 path = get_action_save_path(dialog);
722 if (path)
724 if (symlink(app, path))
725 delayed_error("symlink: %s",
726 g_strerror(errno));
727 else
728 destroy_on_idle(dialog);
730 g_free(path);
733 else
734 delayed_error(
735 _("This is not a program! Give me an application "
736 "instead!"));
738 diritem_free(item);
741 /* Find the current command which is used to run files of this type.
742 * Returns NULL on failure. g_free() the result.
744 static guchar *get_current_command(MIME_type *type)
746 struct stat info;
747 char *handler, *nl, *data = NULL;
748 long len;
749 guchar *command = NULL;
751 handler = handler_for(type);
753 if (!handler)
754 return NULL; /* No current handler */
756 if (stat(handler, &info))
757 goto out; /* Can't stat */
759 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
760 goto out; /* Only use small regular files */
762 if (!load_file(handler, &data, &len))
763 goto out; /* Didn't load OK */
765 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
766 goto out; /* Not one of ours */
768 nl = strchr(data + 16, '\n');
769 if (!nl)
770 goto out; /* No newline! */
772 command = g_strndup(data + 16, nl - data - 16);
773 out:
774 g_free(handler);
775 g_free(data);
776 return command;
779 /* Find the current command which is used to run files of this type,
780 * and return a textual description of it.
781 * g_free() the result.
783 gchar *describe_current_command(MIME_type *type)
785 char *handler;
786 char *desc = NULL;
787 struct stat info;
788 char *target;
790 g_return_val_if_fail(type != NULL, NULL);
792 if (type == application_executable)
793 return g_strdup(_("Execute file"));
795 handler = handler_for(type);
797 if (!handler)
798 return g_strdup(_("No run action defined"));
800 target = readlink_dup(handler);
801 if (target)
803 /* Cope with relative paths (shouldn't normally be needed) */
805 if (target[0] == '/')
807 g_free(handler);
808 handler = target;
810 else
812 gchar *dir;
814 dir = g_path_get_dirname(handler);
815 g_free(handler);
816 handler = g_strconcat(dir, "/", target, NULL);
817 g_free(target);
818 g_free(dir);
822 if (mc_stat(handler, &info) !=0 )
824 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
825 g_strerror(errno));
826 goto out;
829 if (S_ISDIR(info.st_mode))
831 const guchar *tmp;
832 uid_t dir_uid = info.st_uid;
834 tmp = make_path(handler, "AppRun");
836 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
837 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
838 desc = g_strdup_printf(
839 _("Invalid application %s (bad AppRun)"),
840 handler);
841 /* Else, just report handler... */
843 goto out;
846 /* It's not an application directory, and it's not a symlink... */
848 if (access(handler, X_OK) != 0)
850 desc = g_strdup_printf(_("Non-executable %s"), handler);
851 goto out;
854 desc = get_current_command(type);
855 out:
856 if (!desc)
857 desc = handler;
858 else
859 g_free(handler);
861 return desc;
864 /* Display a dialog box allowing the user to set the default run action
865 * for this type.
867 void type_set_handler_dialog(MIME_type *type)
869 guchar *tmp;
870 GtkDialog *dialog;
871 GtkWidget *frame, *entry, *label, *button;
872 GtkWidget *hbox;
873 Radios *radios;
875 g_return_if_fail(type != NULL);
877 dialog = GTK_DIALOG(gtk_dialog_new());
878 gtk_dialog_set_has_separator(dialog, FALSE);
879 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
881 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
883 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
885 radios = radios_new(run_action_update, dialog);
886 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
888 radios_add(radios,
889 _("If a handler for the specific type isn't set up, "
890 "use this as the default."), SET_MEDIA,
891 _("Set default for all `%s/<anything>'"),
892 type->media_type);
894 radios_add(radios,
895 _("Use this application for all files with this MIME "
896 "type."), SET_TYPE,
897 _("Only for the type `%s' (%s/%s)"),
898 mime_type_comment(type),
899 type->media_type, type->subtype);
901 radios_set_value(radios, SET_TYPE);
903 frame = drop_box_new(_("Drop a suitable application here"));
905 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
907 radios_pack(radios, GTK_BOX(dialog->vbox));
908 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 0);
910 g_signal_connect(frame, "path_dropped",
911 G_CALLBACK(drag_app_dropped), dialog);
912 g_signal_connect(frame, "clear",
913 G_CALLBACK(clear_run_action), dialog);
915 hbox = gtk_hbox_new(FALSE, 4);
916 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
917 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
918 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
919 FALSE, TRUE, 0);
920 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
922 hbox = gtk_hbox_new(FALSE, 4);
923 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
925 label = gtk_label_new(_("Enter a shell command:")),
926 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
927 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
929 gtk_box_pack_start(GTK_BOX(hbox),
930 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
932 entry = gtk_entry_new();
933 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
934 gtk_widget_grab_focus(entry);
935 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
936 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
938 /* If possible, fill in the entry box with the current command */
939 tmp = get_current_command(type);
940 if (tmp)
942 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
943 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
944 g_free(tmp);
946 else
948 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
949 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
952 gtk_dialog_add_button(dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL);
954 button = button_new_mixed(GTK_STOCK_OK, _("_Use Command"));
955 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
956 gtk_dialog_add_action_widget(dialog, button, GTK_RESPONSE_OK);
958 hbox = gtk_hbox_new(TRUE, 4);
959 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
961 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
963 g_signal_connect(dialog, "response",
964 G_CALLBACK(set_action_response), NULL);
966 gtk_widget_show_all(GTK_WIDGET(dialog));
969 /* path is an entry in Choices. If it's a symlink or a very small executable
970 * then just get rid of it, otherwise confirm first. It it doesn't exist,
971 * do nothing.
973 * FALSE on error (abort operation).
975 static gboolean remove_handler_with_confirm(const guchar *path)
977 struct stat info;
979 if (lstat(path, &info) == 0)
981 /* A binding already exists... */
982 if (S_ISREG(info.st_mode) && info.st_size > 256)
984 if (!confirm(_("A run action already exists and is "
985 "quite a big program - are you sure "
986 "you want to delete it?"),
987 GTK_STOCK_DELETE, NULL))
989 return FALSE;
993 if (unlink(path))
995 report_error(_("Can't remove %s: %s"),
996 path, g_strerror(errno));
997 return FALSE;
1001 return TRUE;
1004 /* The user wants to set a new default action for files of this type (or just
1005 * clear the action). Removes the current binding if possible and returns the
1006 * path to save the new one to. NULL means cancel. g_free() the result.
1008 static char *get_action_save_path(GtkWidget *dialog)
1010 guchar *path = NULL;
1011 guchar *type_name = NULL;
1012 MIME_type *type;
1013 Radios *radios;
1015 g_return_val_if_fail(dialog != NULL, NULL);
1017 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
1018 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
1020 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
1022 if (radios_get_value(radios) == SET_MEDIA)
1023 type_name = g_strdup(type->media_type);
1024 else
1025 type_name = g_strconcat(type->media_type, "_",
1026 type->subtype, NULL);
1028 path = choices_find_path_save("", PROJECT, FALSE);
1029 if (!path)
1031 report_error(
1032 _("Choices saving is disabled by CHOICESPATH variable"));
1033 goto out;
1035 g_free(path);
1037 path = choices_find_path_save(type_name, "MIME-types", TRUE);
1039 if (!remove_handler_with_confirm(path))
1040 null_g_free(&path);
1041 out:
1042 g_free(type_name);
1043 return path;
1046 MIME_type *mime_type_from_base_type(int base_type)
1048 switch (base_type)
1050 case TYPE_FILE:
1051 return text_plain;
1052 case TYPE_DIRECTORY:
1053 return inode_directory;
1054 case TYPE_PIPE:
1055 return inode_pipe;
1056 case TYPE_SOCKET:
1057 return inode_socket;
1058 case TYPE_BLOCK_DEVICE:
1059 return inode_block_dev;
1060 case TYPE_CHAR_DEVICE:
1061 return inode_char_dev;
1062 case TYPE_DOOR:
1063 return inode_door;
1065 return inode_unknown;
1068 /* Takes the st_mode field from stat() and returns the base type.
1069 * Should not be a symlink.
1071 int mode_to_base_type(int st_mode)
1073 if (S_ISREG(st_mode))
1074 return TYPE_FILE;
1075 else if (S_ISDIR(st_mode))
1076 return TYPE_DIRECTORY;
1077 else if (S_ISBLK(st_mode))
1078 return TYPE_BLOCK_DEVICE;
1079 else if (S_ISCHR(st_mode))
1080 return TYPE_CHAR_DEVICE;
1081 else if (S_ISFIFO(st_mode))
1082 return TYPE_PIPE;
1083 else if (S_ISSOCK(st_mode))
1084 return TYPE_SOCKET;
1085 else if (S_ISDOOR(st_mode))
1086 return TYPE_DOOR;
1088 return TYPE_ERROR;
1091 /* Returns TRUE is this is something that is run by looking up its type
1092 * in MIME-types and, hence, can have its run action set.
1094 gboolean can_set_run_action(DirItem *item)
1096 g_return_val_if_fail(item != NULL, FALSE);
1098 return item->base_type == TYPE_FILE &&
1099 !(item->mime_type == application_executable);
1102 /* Parse file type colours and allocate/free them as necessary */
1103 static void alloc_type_colours(void)
1105 gboolean success[NUM_TYPE_COLOURS];
1106 int change_count = 0; /* No. needing realloc */
1107 int i;
1108 static gboolean allocated = FALSE;
1110 /* Parse colours */
1111 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1113 GdkColor *c = &type_colours[i];
1114 gushort r = c->red;
1115 gushort g = c->green;
1116 gushort b = c->blue;
1118 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1120 if (allocated && (c->red != r || c->green != g || c->blue != b))
1121 change_count++;
1124 /* Free colours if they were previously allocated and
1125 * have changed or become unneeded.
1127 if (allocated && (change_count || !o_display_colour_types.int_value))
1129 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1130 type_colours, NUM_TYPE_COLOURS);
1131 allocated = FALSE;
1134 /* Allocate colours, unless they are still allocated (=> they didn't
1135 * change) or we don't want them anymore.
1136 * XXX: what should be done if allocation fails?
1138 if (!allocated && o_display_colour_types.int_value)
1140 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1141 type_colours, NUM_TYPE_COLOURS,
1142 FALSE, TRUE, success);
1143 allocated = TRUE;
1147 static void expire_timer(gpointer key, gpointer value, gpointer data)
1149 MIME_type *type = value;
1151 type->image_time = 0;
1154 static void options_changed(void)
1156 alloc_type_colours();
1157 if (o_icon_theme.has_changed)
1159 set_icon_theme();
1160 g_hash_table_foreach(type_hash, expire_timer, NULL);
1161 full_refresh();
1165 /* Return a pointer to a (static) colour for this item. If colouring is
1166 * off, returns normal.
1168 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1170 int type = item->base_type;
1172 if (!o_display_colour_types.int_value)
1173 return normal;
1175 if (item->flags & ITEM_FLAG_EXEC_FILE &&
1176 item->mime_type == application_executable)
1177 type = TYPE_EXEC;
1178 else if (item->flags & ITEM_FLAG_APPDIR)
1179 type = TYPE_APPDIR;
1181 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1183 return &type_colours[type];
1186 /* Process the 'Patterns' value */
1187 static void add_pattern(MIME_type *type, const char *pattern, GHashTable *globs)
1189 if (pattern[0] == '*' && pattern[1] == '.' &&
1190 strpbrk(pattern + 2, "*?[") == NULL)
1192 g_hash_table_insert(extension_hash,
1193 g_strdup(pattern + 2),
1194 type);
1196 else if (strpbrk(pattern, "*?[") == NULL)
1197 g_hash_table_insert(literal_hash, g_strdup(pattern), type);
1198 else
1199 g_hash_table_insert(globs, g_strdup(pattern), type);
1202 /* Load and parse this file. literal_hash and extension_hash are updated
1203 * directly. Other patterns are added to 'globs'.
1205 static void import_file(const gchar *file, GHashTable *globs)
1207 MIME_type *type = NULL;
1208 GError *error = NULL;
1209 gchar *data, *line;
1211 if (access(file, F_OK) != 0)
1212 return; /* Doesn't exist. No problem. */
1214 if (!g_file_get_contents(file, &data, NULL, &error))
1216 delayed_error(_("Error loading MIME database:\n%s"),
1217 error->message);
1218 g_error_free(error);
1219 return;
1222 line = data;
1224 while (line && *line)
1226 char *nl;
1228 nl = strchr(line, '\n');
1229 if (!nl)
1230 break;
1231 *nl = '\0';
1233 if (*line != '#')
1235 const gchar *colon;
1236 gchar *name;
1238 colon = strchr(line, ':');
1239 if (!colon)
1241 delayed_error(_("File '%s' corrupted!"), file);
1242 break;
1245 name = g_strndup(line, colon - line);
1246 type = get_mime_type(name, TRUE);
1247 g_free(name);
1248 if (!type)
1249 g_warning("Invalid type in '%s'", file);
1251 add_pattern(type, colon + 1, globs);
1254 line = nl + 1;
1257 g_free(data);
1260 static void add_to_glob_patterns(gpointer key, gpointer value, gpointer unused)
1262 Pattern *pattern;
1264 pattern = g_new(Pattern, 1);
1265 pattern->glob = g_strdup((gchar *) key);
1266 pattern->type = (MIME_type *) value;
1267 pattern->len = strlen(pattern->glob);
1269 g_ptr_array_add(glob_patterns, pattern);
1272 static gint sort_by_strlen(gconstpointer a, gconstpointer b)
1274 const Pattern *pa = *(const Pattern **) a;
1275 const Pattern *pb = *(const Pattern **) b;
1277 if (pa->len > pb->len)
1278 return -1;
1279 else if (pa->len == pb->len)
1280 return 0;
1281 return 1;
1284 static char **get_xdg_data_dirs(int *n_dirs)
1286 const char *env;
1287 char **dirs;
1288 int i, n;
1290 env = getenv("XDG_DATA_DIRS");
1291 if (!env)
1292 env = "/usr/local/share/:/usr/share/";
1293 dirs = g_strsplit(env, ":", 0);
1294 g_return_val_if_fail(dirs != NULL, NULL);
1295 for (n = 0; dirs[n]; n++)
1297 for (i = n; i > 0; i--)
1298 dirs[i] = dirs[i - 1];
1299 env = getenv("XDG_DATA_HOME");
1300 if (env)
1301 dirs[0] = g_strdup(env);
1302 else
1303 dirs[0] = g_build_filename(g_get_home_dir(), ".local",
1304 "share", NULL);
1305 *n_dirs = n + 1;
1306 return dirs;
1309 /* Clear all currently stored information and re-read everything.
1310 * Note: calls filer_update_all.
1312 static void load_mime_types(void)
1314 GHashTable *globs;
1315 char **dirs;
1316 int n_dirs;
1317 int i;
1319 dirs = get_xdg_data_dirs(&n_dirs);
1320 g_return_if_fail(dirs != NULL);
1323 struct stat info;
1324 if (lstat(make_path(home_dir, ".mime"), &info) == 0 &&
1325 S_ISDIR(info.st_mode))
1327 delayed_error(_("The ~/.mime directory has moved. "
1328 "It should now be ~/.local/share/mime. You "
1329 "should move it there (and make a symlink "
1330 "from ~/.mime to it for older applications)."));
1334 if (!glob_patterns)
1335 glob_patterns = g_ptr_array_new();
1336 else
1338 int i;
1340 for (i = glob_patterns->len - 1; i >= 0; i--)
1342 Pattern *p = glob_patterns->pdata[i];
1343 g_free(p->glob);
1344 g_free(p);
1346 g_ptr_array_set_size(glob_patterns, 0);
1349 if (literal_hash)
1350 g_hash_table_destroy(literal_hash);
1351 if (extension_hash)
1352 g_hash_table_destroy(extension_hash);
1353 literal_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1354 g_free, NULL);
1355 extension_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
1356 g_free, NULL);
1357 globs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1359 for (i = n_dirs - 1; i >= 0; i--)
1361 char *path;
1362 path = g_build_filename(dirs[i], "mime", "globs", NULL);
1363 import_file(path, globs);
1364 g_free(path);
1365 g_free(dirs[i]);
1367 g_free(dirs);
1369 /* Turn the globs hash into a pointer array */
1370 g_hash_table_foreach(globs, add_to_glob_patterns, NULL);
1371 g_hash_table_destroy(globs);
1372 globs = NULL;
1374 if (glob_patterns->len)
1375 g_ptr_array_sort(glob_patterns, sort_by_strlen);
1377 if (g_hash_table_size(extension_hash) == 0)
1379 delayed_error(_("The standard MIME type database "
1380 "(version 0.9 or later) was not found. "
1381 "The filer will probably not show the correct "
1382 "types for different files. You should download and "
1383 "install the 'shared-mime-info-0.9' package from "
1384 "here:\n"
1385 "http://www.freedesktop.org/software/shared-mime-info\n\n"
1386 "If you have already installed this package, check that the "
1387 "permissions allow the files to be read (check "
1388 "/usr/local/share/mime/globs or /usr/share/mime/globs)."));
1391 filer_update_all();
1394 /* Try to fill in 'type->comment' from this document */
1395 static void get_comment(MIME_type *type, const guchar *path)
1397 xmlNode *node;
1398 XMLwrapper *doc;
1400 doc = xml_cache_load(path);
1401 if (!doc)
1402 return;
1404 node = xml_get_section(doc, TYPE_NS, "comment");
1406 if (node)
1408 char *val;
1409 g_return_if_fail(type->comment == NULL);
1410 val= xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
1411 type->comment = g_strdup(val);
1412 xmlFree(val);
1415 g_object_unref(doc);
1418 /* Fill in the comment field for this MIME type */
1419 static void find_comment(MIME_type *type)
1421 char **dirs;
1422 int i, n_dirs;
1424 if (type->comment)
1426 g_free(type->comment);
1427 type->comment = NULL;
1430 dirs = get_xdg_data_dirs(&n_dirs);
1431 g_return_if_fail(dirs != NULL);
1433 for (i = 0; i < n_dirs; i++)
1435 guchar *path;
1437 path = g_strdup_printf("%s/mime/%s/%s.xml", dirs[i],
1438 type->media_type, type->subtype);
1439 get_comment(type, path);
1440 g_free(path);
1441 if (type->comment)
1442 break;
1445 if (!type->comment)
1446 type->comment = g_strdup_printf("%s/%s", type->media_type,
1447 type->subtype);
1449 for (i = 0; i < n_dirs; i++)
1450 g_free(dirs[i]);
1451 g_free(dirs);
1454 const char *mime_type_comment(MIME_type *type)
1456 if (!type->comment)
1457 find_comment(type);
1459 return type->comment;
1462 static void set_icon_theme(void)
1464 RoxIconInfo *info;
1465 char *icon_home;
1466 const char *theme_name = o_icon_theme.value;
1468 if (!theme_name || !*theme_name)
1469 theme_name = "ROX";
1471 while (1)
1473 rox_icon_theme_set_custom_theme(icon_theme, theme_name);
1474 info = rox_icon_theme_lookup_icon(icon_theme,
1475 "mime-application:postscript",
1476 ICON_HEIGHT, 0);
1477 if (!info)
1479 info = rox_icon_theme_lookup_icon(icon_theme,
1480 "gnome-mime-application-postscript",
1481 ICON_HEIGHT, 0);
1483 if (info)
1485 rox_icon_info_free(info);
1486 return;
1489 if (strcmp(theme_name, "ROX") == 0)
1490 break;
1492 delayed_error(_("Icon theme '%s' does not contain MIME icons. "
1493 "Using ROX default theme instead."),
1494 theme_name);
1496 theme_name = "ROX";
1499 icon_home = g_build_filename(home_dir, ".icons", NULL);
1500 if (!file_exists(icon_home))
1501 mkdir(icon_home, 0755);
1502 g_free(icon_home);
1504 icon_home = g_build_filename(home_dir, ".icons", "ROX", NULL);
1505 if (symlink(make_path(app_dir, "ROX"), icon_home))
1506 delayed_error(_("Failed to create symlink '%s':\n%s\n\n"
1507 "(this may mean that the ROX theme already exists there, but "
1508 "the 'mime-application:postscript' icon couldn't be loaded for "
1509 "some reason)"), icon_home, g_strerror(errno));
1510 g_free(icon_home);
1512 rox_icon_theme_rescan_if_needed(icon_theme);
1515 static guchar *read_theme(Option *option)
1517 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1518 GtkLabel *item;
1520 item = GTK_LABEL(GTK_BIN(om)->child);
1522 g_return_val_if_fail(item != NULL, g_strdup("ROX"));
1524 return g_strdup(gtk_label_get_text(item));
1527 static void update_theme(Option *option)
1529 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1530 GtkWidget *menu;
1531 GList *kids, *next;
1532 int i = 0;
1534 menu = gtk_option_menu_get_menu(om);
1536 kids = gtk_container_get_children(GTK_CONTAINER(menu));
1537 for (next = kids; next; next = next->next, i++)
1539 GtkLabel *item = GTK_LABEL(GTK_BIN(next->data)->child);
1540 const gchar *label;
1542 /* The label actually moves from the menu!! */
1543 if (!item)
1544 item = GTK_LABEL(GTK_BIN(om)->child);
1546 label = gtk_label_get_text(item);
1548 g_return_if_fail(label != NULL);
1550 if (strcmp(label, option->value) == 0)
1551 break;
1553 g_list_free(kids);
1555 if (next)
1556 gtk_option_menu_set_history(om, i);
1557 else
1558 g_warning("Theme '%s' not found", option->value);
1561 static void add_themes_from_dir(GPtrArray *names, const char *dir)
1563 GPtrArray *list;
1564 int i;
1566 if (access(dir, F_OK) != 0)
1567 return;
1569 list = list_dir(dir);
1570 g_return_if_fail(list != NULL);
1572 for (i = 0; i < list->len; i++)
1574 char *index_path;
1576 index_path = g_build_filename(dir, list->pdata[i],
1577 "index.theme", NULL);
1579 if (access(index_path, F_OK) == 0)
1580 g_ptr_array_add(names, list->pdata[i]);
1581 else
1582 g_free(list->pdata[i]);
1584 g_free(index_path);
1587 g_ptr_array_free(list, TRUE);
1590 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label)
1592 GtkWidget *button, *menu, *hbox;
1593 GPtrArray *names;
1594 gchar **theme_dirs = NULL;
1595 gint n_dirs = 0;
1596 int i;
1598 g_return_val_if_fail(option != NULL, NULL);
1599 g_return_val_if_fail(label != NULL, NULL);
1601 hbox = gtk_hbox_new(FALSE, 4);
1603 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(label)),
1604 FALSE, TRUE, 0);
1606 button = gtk_option_menu_new();
1607 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
1609 menu = gtk_menu_new();
1610 gtk_option_menu_set_menu(GTK_OPTION_MENU(button), menu);
1612 rox_icon_theme_get_search_path(icon_theme, &theme_dirs, &n_dirs);
1613 names = g_ptr_array_new();
1614 for (i = 0; i < n_dirs; i++)
1615 add_themes_from_dir(names, theme_dirs[i]);
1616 g_strfreev(theme_dirs);
1618 g_ptr_array_sort(names, strcmp2);
1620 for (i = 0; i < names->len; i++)
1622 GtkWidget *item;
1623 char *name = names->pdata[i];
1625 item = gtk_menu_item_new_with_label(name);
1626 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1627 gtk_widget_show_all(item);
1629 g_free(name);
1632 g_ptr_array_free(names, TRUE);
1634 option->update_widget = update_theme;
1635 option->read_widget = read_theme;
1636 option->widget = button;
1638 gtk_signal_connect_object(GTK_OBJECT(button), "changed",
1639 GTK_SIGNAL_FUNC(option_check_widget),
1640 (GtkObject *) option);
1642 return g_list_append(NULL, hbox);