r4241: Better support for old GNOME themes with GNOME-specific MIME names (Dennis
[rox-filer/translations.git] / ROX-Filer / src / type.c
blobf2b46e67bfa18ef13993ab21ad9d55762f3db55a
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 "xdgmime.h"
62 #include "xtypes.h"
64 #define TYPE_NS "http://www.freedesktop.org/standards/shared-mime-info"
65 enum {SET_MEDIA, SET_TYPE};
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_door_colour", "#ff00ff"},
78 {"display_exec_colour", "#006000"},
79 {"display_adir_colour", "#006000"}
81 #define NUM_TYPE_COLOURS\
82 (sizeof(opt_type_colours) / sizeof(opt_type_colours[0]))
84 /* Parsed colours for file types */
85 static Option o_type_colours[NUM_TYPE_COLOURS];
86 static GdkColor type_colours[NUM_TYPE_COLOURS];
88 /* Static prototypes */
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 /* Hash of all allocated MIME types, indexed by "media/subtype".
98 * MIME_type structs are never freed; this table prevents memory leaks
99 * when rereading the config files.
101 static GHashTable *type_hash = NULL;
103 /* Most things on Unix are text files, so this is the default type */
104 MIME_type *text_plain;
105 MIME_type *inode_directory;
106 MIME_type *inode_mountpoint;
107 MIME_type *inode_pipe;
108 MIME_type *inode_socket;
109 MIME_type *inode_block_dev;
110 MIME_type *inode_char_dev;
111 MIME_type *application_executable;
112 MIME_type *application_octet_stream;
113 MIME_type *application_x_shellscript;
114 MIME_type *application_x_desktop;
115 MIME_type *inode_unknown;
116 MIME_type *inode_door;
118 static Option o_display_colour_types;
119 static Option o_icon_theme;
121 GtkIconTheme *icon_theme = NULL;
123 void type_init(void)
125 int i;
127 icon_theme = gtk_icon_theme_new();
129 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
131 text_plain = get_mime_type("text/plain", TRUE);
132 inode_directory = get_mime_type("inode/directory", TRUE);
133 inode_mountpoint = get_mime_type("inode/mount-point", TRUE);
134 inode_pipe = get_mime_type("inode/fifo", TRUE);
135 inode_socket = get_mime_type("inode/socket", TRUE);
136 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
137 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
138 application_executable = get_mime_type("application/x-executable", TRUE);
139 application_octet_stream = get_mime_type("application/octet-stream", TRUE);
140 application_x_shellscript = get_mime_type("application/x-shellscript", TRUE);
141 application_x_desktop = get_mime_type("application/x-desktop", TRUE);
142 application_x_desktop->executable = TRUE;
143 inode_unknown = get_mime_type("inode/unknown", TRUE);
144 inode_door = get_mime_type("inode/door", TRUE);
146 option_add_string(&o_icon_theme, "icon_theme", "ROX");
147 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
148 option_register_widget("icon-theme-chooser", build_icon_theme);
150 for (i = 0; i < NUM_TYPE_COLOURS; i++)
151 option_add_string(&o_type_colours[i],
152 opt_type_colours[i][0],
153 opt_type_colours[i][1]);
154 alloc_type_colours();
156 set_icon_theme();
158 option_add_notify(options_changed);
161 /* Read-load all the glob patterns.
162 * Note: calls filer_update_all.
164 void reread_mime_files(void)
166 gtk_icon_theme_rescan_if_needed(icon_theme);
168 xdg_mime_shutdown();
170 filer_update_all();
173 /* Returns the MIME_type structure for the given type name. It is looked
174 * up in type_hash and returned if found. If not found (and can_create is
175 * TRUE) then a new MIME_type is made, added to type_hash and returned.
176 * NULL is returned if type_name is not in type_hash and can_create is
177 * FALSE, or if type_name does not contain a '/' character.
179 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
181 MIME_type *mtype;
182 gchar *slash;
184 mtype = g_hash_table_lookup(type_hash, type_name);
185 if (mtype || !can_create)
186 return mtype;
188 slash = strchr(type_name, '/');
189 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
191 mtype = g_new(MIME_type, 1);
192 mtype->media_type = g_strndup(type_name, slash - type_name);
193 mtype->subtype = g_strdup(slash + 1);
194 mtype->image = NULL;
195 mtype->comment = NULL;
197 mtype->executable = xdg_mime_mime_type_subclass(type_name,
198 "application/x-executable");
200 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
202 return mtype;
205 const char *basetype_name(DirItem *item)
207 if (item->flags & ITEM_FLAG_SYMLINK)
208 return _("Sym link");
209 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
210 return _("Mount point");
211 else if (item->flags & ITEM_FLAG_APPDIR)
212 return _("App dir");
214 switch (item->base_type)
216 case TYPE_FILE:
217 return _("File");
218 case TYPE_DIRECTORY:
219 return _("Dir");
220 case TYPE_CHAR_DEVICE:
221 return _("Char dev");
222 case TYPE_BLOCK_DEVICE:
223 return _("Block dev");
224 case TYPE_PIPE:
225 return _("Pipe");
226 case TYPE_SOCKET:
227 return _("Socket");
228 case TYPE_DOOR:
229 return _("Door");
232 return _("Unknown");
235 static void append_names(gpointer key, gpointer value, gpointer udata)
237 GList **list = (GList **) udata;
239 *list = g_list_prepend(*list, key);
242 /* Return list of all mime type names. Caller must free the list
243 * but NOT the strings it contains (which are never freed).
245 GList *mime_type_name_list(void)
247 GList *list = NULL;
249 g_hash_table_foreach(type_hash, append_names, &list);
250 list = g_list_sort(list, (GCompareFunc) strcmp);
252 return list;
255 /* MIME-type guessing */
257 /* Get the type of this file - stats the file and uses that if
258 * possible. For regular or missing files, uses the pathname.
260 MIME_type *type_get_type(const guchar *path)
262 DirItem *item;
263 MIME_type *type = NULL;
265 item = diritem_new("");
266 diritem_restat(path, item, NULL);
267 if (item->base_type != TYPE_ERROR)
268 type = item->mime_type;
269 diritem_free(item);
271 if (type)
272 return type;
274 type = type_from_path(path);
276 if (!type)
277 return text_plain;
279 return type;
282 /* Returns a pointer to the MIME-type.
284 * Tries all enabled methods:
285 * - Look for extended attribute
286 * - If no attribute, check file name
287 * - If no name rule, check contents
289 * NULL if we can't think of anything.
291 MIME_type *type_from_path(const char *path)
293 MIME_type *mime_type = NULL;
294 const char *type_name;
296 /* Check for extended attribute first */
297 mime_type = xtype_get(path);
298 if (mime_type)
299 return mime_type;
301 /* Try name and contents next */
302 type_name = xdg_mime_get_mime_type_for_file(path);
303 if (type_name)
304 return get_mime_type(type_name, TRUE);
306 return NULL;
309 /* Returns the file/dir in Choices for handling this type.
310 * NULL if there isn't one. g_free() the result.
312 static char *handler_for(MIME_type *type)
314 char *type_name;
315 char *open;
317 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
318 open = choices_find_xdg_path_load(type_name, "MIME-types", SITE);
319 g_free(type_name);
321 if (!open)
322 open = choices_find_xdg_path_load(type->media_type,
323 "MIME-types", SITE);
325 return open;
328 MIME_type *mime_type_lookup(const char *type)
330 return get_mime_type(type, TRUE);
333 /* Actions for types */
335 gboolean type_open(const char *path, MIME_type *type)
337 gchar *argv[] = {NULL, NULL, NULL};
338 char *open;
339 gboolean retval;
340 struct stat info;
342 argv[1] = (char *) path;
344 open = handler_for(type);
345 if (!open)
346 return FALSE;
348 if (stat(open, &info))
350 report_error("stat(%s): %s", open, g_strerror(errno));
351 g_free(open);
352 return FALSE;
355 if (info.st_mode & S_IWOTH)
357 gchar *choices_dir;
358 GList *paths;
360 report_error(_("Executable '%s' is world-writeable! Refusing "
361 "to run. Please change the permissions now (this "
362 "problem may have been caused by a bug in earlier "
363 "versions of the filer).\n\n"
364 "Having (non-symlink) run actions world-writeable "
365 "means that other people who use your computer can "
366 "replace your run actions with malicious versions.\n\n"
367 "If you trust everyone who could write to these files "
368 "then you needn't worry. Otherwise, you should check, "
369 "or even just delete, all the existing run actions."),
370 open);
371 choices_dir = g_path_get_dirname(open);
372 paths = g_list_append(NULL, choices_dir);
373 action_chmod(paths, TRUE, _("go-w (Fix security problem)"));
374 g_free(choices_dir);
375 g_list_free(paths);
376 g_free(open);
377 return TRUE;
380 if (S_ISDIR(info.st_mode))
381 argv[0] = g_strconcat(open, "/AppRun", NULL);
382 else
383 argv[0] = open;
385 retval = rox_spawn(home_dir, (const gchar **) argv) != 0;
387 if (argv[0] != open)
388 g_free(argv[0]);
390 g_free(open);
392 return retval;
395 /* Return the image for this type, loading it if needed.
396 * Places to check are: (eg type="text_plain", base="text")
397 * 1. <Choices>/MIME-icons/base_subtype
398 * 2. Icon theme 'mime-base:subtype'
399 * 3. Icon theme 'mime-base'
400 * 4. Unknown type icon.
402 * Note: You must g_object_unref() the image afterwards.
404 MaskedPixmap *type_to_icon(MIME_type *type)
406 GdkPixbuf *full;
407 char *type_name, *path;
408 time_t now;
410 if (type == NULL)
412 g_object_ref(im_unknown);
413 return im_unknown;
416 now = time(NULL);
417 /* Already got an image? */
418 if (type->image)
420 /* Yes - don't recheck too often */
421 if (abs(now - type->image_time) < 2)
423 g_object_ref(type->image);
424 return type->image;
426 g_object_unref(type->image);
427 type->image = NULL;
430 type_name = g_strconcat(type->media_type, "_", type->subtype,
431 ".png", NULL);
432 path = choices_find_xdg_path_load(type_name, "MIME-icons", SITE);
433 g_free(type_name);
434 if (path)
436 type->image = g_fscache_lookup(pixmap_cache, path);
437 g_free(path);
440 if (type->image)
441 goto out;
443 type_name = g_strconcat("mime-", type->media_type, ":",
444 type->subtype, NULL);
445 full = gtk_icon_theme_load_icon(icon_theme, type_name, HUGE_HEIGHT,
446 0, NULL);
447 g_free(type_name);
448 if (!full)
450 /* Ugly hack... try for a GNOME icon */
451 if (type == inode_directory)
452 type_name = g_strdup("gnome-fs-directory");
453 else
454 type_name = g_strconcat("gnome-mime-", type->media_type,
455 "-", type->subtype, NULL);
456 full = gtk_icon_theme_load_icon(icon_theme,
457 type_name,
458 HUGE_HEIGHT, 0, NULL);
459 g_free(type_name);
461 if (!full)
463 /* Try for a media type */
464 type_name = g_strconcat("mime-", type->media_type, NULL);
465 full = gtk_icon_theme_load_icon(icon_theme,
466 type_name,
467 HUGE_HEIGHT, 0, NULL);
468 g_free(type_name);
470 if (!full)
472 /* Ugly hack... try for a GNOME default media icon */
473 type_name = g_strconcat("gnome-mime-", type->media_type, NULL);
475 full = gtk_icon_theme_load_icon(icon_theme,
476 type_name,
477 HUGE_HEIGHT, 0, NULL);
478 g_free(type_name);
480 if (full)
482 type->image = masked_pixmap_new(full);
483 g_object_unref(full);
486 out:
487 if (!type->image)
489 /* One ref from the type structure, one returned */
490 type->image = im_unknown;
491 g_object_ref(im_unknown);
494 type->image_time = now;
496 g_object_ref(type->image);
497 return type->image;
500 GdkAtom type_to_atom(MIME_type *type)
502 char *str;
503 GdkAtom retval;
505 g_return_val_if_fail(type != NULL, GDK_NONE);
507 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
508 retval = gdk_atom_intern(str, FALSE);
509 g_free(str);
511 return retval;
514 static void show_shell_help(gpointer data)
516 info_message(_("Enter a shell command which will load \"$@\" into "
517 "a suitable program. Eg:\n\n"
518 "gimp \"$@\""));
521 /* Called if the user clicks on the OK button. Returns FALSE if an error
522 * was displayed instead of performing the action.
524 static gboolean set_shell_action(GtkWidget *dialog)
526 GtkEntry *entry;
527 const guchar *command;
528 gchar *tmp, *path;
529 int error = 0, len;
530 int fd;
532 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
534 g_return_val_if_fail(entry != NULL, FALSE);
536 command = gtk_entry_get_text(entry);
538 if (!strchr(command, '$'))
540 show_shell_help(NULL);
541 return FALSE;
544 path = get_action_save_path(dialog);
545 if (!path)
546 return FALSE;
548 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
549 len = strlen(tmp);
551 fd = open(path, O_CREAT | O_WRONLY, 0755);
552 if (fd == -1)
553 error = errno;
554 else
556 FILE *file;
558 file = fdopen(fd, "w");
559 if (file)
561 if (fwrite(tmp, 1, len, file) < len)
562 error = errno;
563 if (fclose(file) && error == 0)
564 error = errno;
566 else
567 error = errno;
570 if (error)
571 report_error(g_strerror(error));
573 g_free(tmp);
574 g_free(path);
576 gtk_widget_destroy(dialog);
578 return TRUE;
581 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
583 if (response == GTK_RESPONSE_OK)
584 if (!set_shell_action(dialog))
585 return;
586 gtk_widget_destroy(dialog);
589 /* Return the path of the file in choices that handles this type and
590 * radio setting.
591 * NULL if nothing is defined for it.
593 static guchar *handler_for_radios(GObject *dialog)
595 Radios *radios;
596 MIME_type *type;
598 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
599 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
601 g_return_val_if_fail(radios != NULL, NULL);
602 g_return_val_if_fail(type != NULL, NULL);
604 switch (radios_get_value(radios))
606 case SET_MEDIA:
607 return choices_find_xdg_path_load(type->media_type,
608 "MIME-types", SITE);
609 case SET_TYPE:
611 gchar *tmp, *handler;
612 tmp = g_strconcat(type->media_type, "_",
613 type->subtype, NULL);
614 handler = choices_find_xdg_path_load(tmp,
615 "MIME-types",
616 SITE);
617 g_free(tmp);
618 return handler;
620 default:
621 g_warning("Bad type");
622 return NULL;
626 /* (radios can be NULL if called from clear_run_action) */
627 static void run_action_update(Radios *radios, gpointer data)
629 guchar *handler;
630 DropBox *drop_box;
631 GObject *dialog = G_OBJECT(data);
633 drop_box = g_object_get_data(dialog, "rox-dropbox");
635 g_return_if_fail(drop_box != NULL);
637 handler = handler_for_radios(dialog);
639 if (handler)
641 char *old = handler;
643 handler = readlink_dup(old);
644 if (handler)
645 g_free(old);
646 else
647 handler = old;
650 drop_box_set_path(DROP_BOX(drop_box), handler);
651 g_free(handler);
654 static void clear_run_action(GtkWidget *drop_box, GtkWidget *dialog)
656 guchar *handler;
658 handler = handler_for_radios(G_OBJECT(dialog));
660 if (handler)
661 remove_handler_with_confirm(handler);
663 run_action_update(NULL, dialog);
666 /* Called when a URI list is dropped onto the box in the Set Run Action
667 * dialog. Make sure it's an application, and make that the default
668 * handler.
670 static void drag_app_dropped(GtkWidget *drop_box,
671 const guchar *app,
672 GtkWidget *dialog)
674 DirItem *item;
676 item = diritem_new("");
677 diritem_restat(app, item, NULL);
678 if (item->flags & ITEM_FLAG_APPDIR || EXECUTABLE_FILE(item))
680 guchar *path;
682 path = get_action_save_path(dialog);
684 if (path)
686 if (symlink(app, path))
687 delayed_error("symlink: %s",
688 g_strerror(errno));
689 else
690 destroy_on_idle(dialog);
692 g_free(path);
695 else
696 delayed_error(
697 _("This is not a program! Give me an application "
698 "instead!"));
700 diritem_free(item);
703 /* Find the current command which is used to run files of this type.
704 * Returns NULL on failure. g_free() the result.
706 static guchar *get_current_command(MIME_type *type)
708 struct stat info;
709 char *handler, *nl, *data = NULL;
710 long len;
711 guchar *command = NULL;
713 handler = handler_for(type);
715 if (!handler)
716 return NULL; /* No current handler */
718 if (stat(handler, &info))
719 goto out; /* Can't stat */
721 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
722 goto out; /* Only use small regular files */
724 if (!load_file(handler, &data, &len))
725 goto out; /* Didn't load OK */
727 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
728 goto out; /* Not one of ours */
730 nl = strchr(data + 16, '\n');
731 if (!nl)
732 goto out; /* No newline! */
734 command = g_strndup(data + 16, nl - data - 16);
735 out:
736 g_free(handler);
737 g_free(data);
738 return command;
741 /* Find the current command which is used to run files of this type,
742 * and return a textual description of it.
743 * Only call for non-executable files.
744 * g_free() the result.
746 gchar *describe_current_command(MIME_type *type)
748 char *handler;
749 char *desc = NULL;
750 struct stat info;
751 char *target;
753 g_return_val_if_fail(type != NULL, NULL);
755 handler = handler_for(type);
757 if (!handler)
758 return g_strdup(_("No run action defined"));
760 target = readlink_dup(handler);
761 if (target)
763 /* Cope with relative paths (shouldn't normally be needed) */
765 if (target[0] == '/')
767 g_free(handler);
768 handler = target;
770 else
772 gchar *dir;
774 dir = g_path_get_dirname(handler);
775 g_free(handler);
776 handler = g_strconcat(dir, "/", target, NULL);
777 g_free(target);
778 g_free(dir);
782 if (mc_stat(handler, &info) !=0 )
784 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
785 g_strerror(errno));
786 goto out;
789 if (S_ISDIR(info.st_mode))
791 const guchar *tmp;
792 uid_t dir_uid = info.st_uid;
794 tmp = make_path(handler, "AppRun");
796 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
797 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
798 desc = g_strdup_printf(
799 _("Invalid application %s (bad AppRun)"),
800 handler);
801 /* Else, just report handler... */
803 goto out;
806 /* It's not an application directory, and it's not a symlink... */
808 if (access(handler, X_OK) != 0)
810 desc = g_strdup_printf(_("Non-executable %s"), handler);
811 goto out;
814 desc = get_current_command(type);
815 out:
816 if (!desc)
817 desc = handler;
818 else
819 g_free(handler);
821 return desc;
824 /* Display a dialog box allowing the user to set the default run action
825 * for this type.
827 void type_set_handler_dialog(MIME_type *type)
829 guchar *tmp;
830 GtkDialog *dialog;
831 GtkWidget *frame, *entry, *label, *button;
832 GtkWidget *hbox;
833 Radios *radios;
835 g_return_if_fail(type != NULL);
837 dialog = GTK_DIALOG(gtk_dialog_new());
838 gtk_dialog_set_has_separator(dialog, FALSE);
839 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
841 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
843 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
845 radios = radios_new(run_action_update, dialog);
846 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
848 radios_add(radios,
849 _("If a handler for the specific type isn't set up, "
850 "use this as the default."), SET_MEDIA,
851 _("Set default for all `%s/<anything>'"),
852 type->media_type);
854 radios_add(radios,
855 _("Use this application for all files with this MIME "
856 "type."), SET_TYPE,
857 _("Only for the type `%s' (%s/%s)"),
858 mime_type_comment(type),
859 type->media_type, type->subtype);
861 radios_set_value(radios, SET_TYPE);
863 frame = drop_box_new(_("Drop a suitable application here"));
865 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
867 radios_pack(radios, GTK_BOX(dialog->vbox));
868 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 0);
870 g_signal_connect(frame, "path_dropped",
871 G_CALLBACK(drag_app_dropped), dialog);
872 g_signal_connect(frame, "clear",
873 G_CALLBACK(clear_run_action), dialog);
875 hbox = gtk_hbox_new(FALSE, 4);
876 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
877 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
878 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
879 FALSE, TRUE, 0);
880 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
882 hbox = gtk_hbox_new(FALSE, 4);
883 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
885 label = gtk_label_new(_("Enter a shell command:")),
886 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
887 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
889 gtk_box_pack_start(GTK_BOX(hbox),
890 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
892 entry = gtk_entry_new();
893 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
894 gtk_widget_grab_focus(entry);
895 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
896 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
898 /* If possible, fill in the entry box with the current command */
899 tmp = get_current_command(type);
900 if (tmp)
902 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
903 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
904 g_free(tmp);
906 else
908 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
909 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
912 gtk_dialog_add_button(dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL);
914 button = button_new_mixed(GTK_STOCK_OK, _("_Use Command"));
915 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
916 gtk_dialog_add_action_widget(dialog, button, GTK_RESPONSE_OK);
918 hbox = gtk_hbox_new(TRUE, 4);
919 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
921 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
923 g_signal_connect(dialog, "response",
924 G_CALLBACK(set_action_response), NULL);
926 gtk_widget_show_all(GTK_WIDGET(dialog));
929 /* path is an entry in Choices. If it's a symlink or a very small executable
930 * then just get rid of it, otherwise confirm first. It it doesn't exist,
931 * do nothing.
933 * FALSE on error (abort operation).
935 static gboolean remove_handler_with_confirm(const guchar *path)
937 struct stat info;
939 if (lstat(path, &info) == 0)
941 /* A binding already exists... */
942 if (S_ISREG(info.st_mode) && info.st_size > 256)
944 if (!confirm(_("A run action already exists and is "
945 "quite a big program - are you sure "
946 "you want to delete it?"),
947 GTK_STOCK_DELETE, NULL))
949 return FALSE;
953 if (unlink(path))
955 report_error(_("Can't remove %s: %s"),
956 path, g_strerror(errno));
957 return FALSE;
961 return TRUE;
964 /* The user wants to set a new default action for files of this type (or just
965 * clear the action). Removes the current binding if possible and returns the
966 * path to save the new one to. NULL means cancel. g_free() the result.
968 static char *get_action_save_path(GtkWidget *dialog)
970 guchar *path = NULL;
971 guchar *type_name = NULL;
972 MIME_type *type;
973 Radios *radios;
975 g_return_val_if_fail(dialog != NULL, NULL);
977 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
978 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
980 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
982 if (radios_get_value(radios) == SET_MEDIA)
983 type_name = g_strdup(type->media_type);
984 else
985 type_name = g_strconcat(type->media_type, "_",
986 type->subtype, NULL);
988 path = choices_find_xdg_path_save("", PROJECT, SITE, FALSE);
989 if (!path)
991 report_error(
992 _("Choices saving is disabled by CHOICESPATH variable"));
993 goto out;
995 g_free(path);
997 path = choices_find_xdg_path_save(type_name, "MIME-types", SITE, TRUE);
999 if (!remove_handler_with_confirm(path))
1000 null_g_free(&path);
1001 out:
1002 g_free(type_name);
1003 return path;
1006 MIME_type *mime_type_from_base_type(int base_type)
1008 switch (base_type)
1010 case TYPE_FILE:
1011 return text_plain;
1012 case TYPE_DIRECTORY:
1013 return inode_directory;
1014 case TYPE_PIPE:
1015 return inode_pipe;
1016 case TYPE_SOCKET:
1017 return inode_socket;
1018 case TYPE_BLOCK_DEVICE:
1019 return inode_block_dev;
1020 case TYPE_CHAR_DEVICE:
1021 return inode_char_dev;
1022 case TYPE_DOOR:
1023 return inode_door;
1025 return inode_unknown;
1028 /* Takes the st_mode field from stat() and returns the base type.
1029 * Should not be a symlink.
1031 int mode_to_base_type(int st_mode)
1033 if (S_ISREG(st_mode))
1034 return TYPE_FILE;
1035 else if (S_ISDIR(st_mode))
1036 return TYPE_DIRECTORY;
1037 else if (S_ISBLK(st_mode))
1038 return TYPE_BLOCK_DEVICE;
1039 else if (S_ISCHR(st_mode))
1040 return TYPE_CHAR_DEVICE;
1041 else if (S_ISFIFO(st_mode))
1042 return TYPE_PIPE;
1043 else if (S_ISSOCK(st_mode))
1044 return TYPE_SOCKET;
1045 else if (S_ISDOOR(st_mode))
1046 return TYPE_DOOR;
1048 return TYPE_ERROR;
1051 /* Returns TRUE is this is something that is run by looking up its type
1052 * in MIME-types and, hence, can have its run action set.
1054 gboolean can_set_run_action(DirItem *item)
1056 g_return_val_if_fail(item != NULL, FALSE);
1058 return item->base_type == TYPE_FILE && !EXECUTABLE_FILE(item);
1061 /* Parse file type colours and allocate/free them as necessary */
1062 static void alloc_type_colours(void)
1064 gboolean success[NUM_TYPE_COLOURS];
1065 int change_count = 0; /* No. needing realloc */
1066 int i;
1067 static gboolean allocated = FALSE;
1069 /* Parse colours */
1070 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1072 GdkColor *c = &type_colours[i];
1073 gushort r = c->red;
1074 gushort g = c->green;
1075 gushort b = c->blue;
1077 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1079 if (allocated && (c->red != r || c->green != g || c->blue != b))
1080 change_count++;
1083 /* Free colours if they were previously allocated and
1084 * have changed or become unneeded.
1086 if (allocated && (change_count || !o_display_colour_types.int_value))
1088 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1089 type_colours, NUM_TYPE_COLOURS);
1090 allocated = FALSE;
1093 /* Allocate colours, unless they are still allocated (=> they didn't
1094 * change) or we don't want them anymore.
1095 * XXX: what should be done if allocation fails?
1097 if (!allocated && o_display_colour_types.int_value)
1099 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1100 type_colours, NUM_TYPE_COLOURS,
1101 FALSE, TRUE, success);
1102 allocated = TRUE;
1106 static void expire_timer(gpointer key, gpointer value, gpointer data)
1108 MIME_type *type = value;
1110 type->image_time = 0;
1113 static void options_changed(void)
1115 alloc_type_colours();
1116 if (o_icon_theme.has_changed)
1118 set_icon_theme();
1119 g_hash_table_foreach(type_hash, expire_timer, NULL);
1120 full_refresh();
1124 /* Return a pointer to a (static) colour for this item. If colouring is
1125 * off, returns normal.
1127 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1129 int type = item->base_type;
1131 if (!o_display_colour_types.int_value)
1132 return normal;
1134 if (EXECUTABLE_FILE(item))
1135 type = TYPE_EXEC;
1136 else if (item->flags & ITEM_FLAG_APPDIR)
1137 type = TYPE_APPDIR;
1139 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1141 return &type_colours[type];
1144 static char **get_xdg_data_dirs(int *n_dirs)
1146 const char *env;
1147 char **dirs;
1148 int i, n;
1150 env = getenv("XDG_DATA_DIRS");
1151 if (!env)
1152 env = "/usr/local/share/:/usr/share/";
1153 dirs = g_strsplit(env, ":", 0);
1154 g_return_val_if_fail(dirs != NULL, NULL);
1155 for (n = 0; dirs[n]; n++)
1157 for (i = n; i > 0; i--)
1158 dirs[i] = dirs[i - 1];
1159 env = getenv("XDG_DATA_HOME");
1160 if (env)
1161 dirs[0] = g_strdup(env);
1162 else
1163 dirs[0] = g_build_filename(g_get_home_dir(), ".local",
1164 "share", NULL);
1165 *n_dirs = n + 1;
1166 return dirs;
1169 /* Try to fill in 'type->comment' from this document */
1170 static void get_comment(MIME_type *type, const guchar *path)
1172 xmlNode *node;
1173 XMLwrapper *doc;
1175 doc = xml_cache_load(path);
1176 if (!doc)
1177 return;
1179 node = xml_get_section(doc, TYPE_NS, "comment");
1181 if (node)
1183 char *val;
1184 g_return_if_fail(type->comment == NULL);
1185 val= xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
1186 type->comment = g_strdup(val);
1187 xmlFree(val);
1190 g_object_unref(doc);
1193 /* Fill in the comment field for this MIME type */
1194 static void find_comment(MIME_type *type)
1196 char **dirs;
1197 int i, n_dirs = 0;
1199 if (type->comment)
1201 g_free(type->comment);
1202 type->comment = NULL;
1205 dirs = get_xdg_data_dirs(&n_dirs);
1206 g_return_if_fail(dirs != NULL);
1208 for (i = 0; i < n_dirs; i++)
1210 guchar *path;
1212 path = g_strdup_printf("%s/mime/%s/%s.xml", dirs[i],
1213 type->media_type, type->subtype);
1214 get_comment(type, path);
1215 g_free(path);
1216 if (type->comment)
1217 break;
1220 if (!type->comment)
1221 type->comment = g_strdup_printf("%s/%s", type->media_type,
1222 type->subtype);
1224 for (i = 0; i < n_dirs; i++)
1225 g_free(dirs[i]);
1226 g_free(dirs);
1229 const char *mime_type_comment(MIME_type *type)
1231 if (!type->comment)
1232 find_comment(type);
1234 return type->comment;
1237 static void set_icon_theme(void)
1239 GtkIconInfo *info;
1240 char *icon_home;
1241 const char *theme_name = o_icon_theme.value;
1243 if (!theme_name || !*theme_name)
1244 theme_name = "ROX";
1246 while (1)
1248 gtk_icon_theme_set_custom_theme(icon_theme, theme_name);
1249 info = gtk_icon_theme_lookup_icon(icon_theme,
1250 "mime-application:postscript",
1251 ICON_HEIGHT, 0);
1252 if (!info)
1254 info = gtk_icon_theme_lookup_icon(icon_theme,
1255 "gnome-mime-application-postscript",
1256 ICON_HEIGHT, 0);
1258 if (info)
1260 gtk_icon_info_free(info);
1261 return;
1264 if (strcmp(theme_name, "ROX") == 0)
1265 break;
1267 delayed_error(_("Icon theme '%s' does not contain MIME icons. "
1268 "Using ROX default theme instead."),
1269 theme_name);
1271 theme_name = "ROX";
1274 icon_home = g_build_filename(home_dir, ".icons", NULL);
1275 if (!file_exists(icon_home))
1276 mkdir(icon_home, 0755);
1277 g_free(icon_home);
1279 icon_home = g_build_filename(home_dir, ".icons", "ROX", NULL);
1280 if (symlink(make_path(app_dir, "ROX"), icon_home))
1282 delayed_error(_("Failed to create symlink '%s':\n%s\n\n"
1283 "(this may mean that the ROX theme already exists there, but "
1284 "the 'mime-application:postscript' icon couldn't be loaded for "
1285 "some reason, or %s is a link to an invalid directory; try "
1286 "deleting it)"), icon_home, g_strerror(errno), icon_home);
1287 open_to_show(icon_home);
1289 g_free(icon_home);
1291 gtk_icon_theme_rescan_if_needed(icon_theme);
1294 static guchar *read_theme(Option *option)
1296 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1297 GtkLabel *item;
1299 item = GTK_LABEL(GTK_BIN(om)->child);
1301 g_return_val_if_fail(item != NULL, g_strdup("ROX"));
1303 return g_strdup(gtk_label_get_text(item));
1306 static void update_theme(Option *option)
1308 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1309 GtkWidget *menu;
1310 GList *kids, *next;
1311 int i = 0;
1313 menu = gtk_option_menu_get_menu(om);
1315 kids = gtk_container_get_children(GTK_CONTAINER(menu));
1316 for (next = kids; next; next = next->next, i++)
1318 GtkLabel *item = GTK_LABEL(GTK_BIN(next->data)->child);
1319 const gchar *label;
1321 /* The label actually moves from the menu!! */
1322 if (!item)
1323 item = GTK_LABEL(GTK_BIN(om)->child);
1325 label = gtk_label_get_text(item);
1327 g_return_if_fail(label != NULL);
1329 if (strcmp(label, option->value) == 0)
1330 break;
1332 g_list_free(kids);
1334 if (next)
1335 gtk_option_menu_set_history(om, i);
1336 else
1337 g_warning("Theme '%s' not found", option->value);
1340 static void add_themes_from_dir(GPtrArray *names, const char *dir)
1342 GPtrArray *list;
1343 int i;
1345 if (access(dir, F_OK) != 0)
1346 return;
1348 list = list_dir(dir);
1349 g_return_if_fail(list != NULL);
1351 for (i = 0; i < list->len; i++)
1353 char *index_path;
1355 index_path = g_build_filename(dir, list->pdata[i],
1356 "index.theme", NULL);
1358 if (access(index_path, F_OK) == 0)
1359 g_ptr_array_add(names, list->pdata[i]);
1360 else
1361 g_free(list->pdata[i]);
1363 g_free(index_path);
1366 g_ptr_array_free(list, TRUE);
1369 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label)
1371 GtkWidget *button, *menu, *hbox;
1372 GPtrArray *names;
1373 gchar **theme_dirs = NULL;
1374 gint n_dirs = 0;
1375 int i;
1377 g_return_val_if_fail(option != NULL, NULL);
1378 g_return_val_if_fail(label != NULL, NULL);
1380 hbox = gtk_hbox_new(FALSE, 4);
1382 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(label)),
1383 FALSE, TRUE, 0);
1385 button = gtk_option_menu_new();
1386 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
1388 menu = gtk_menu_new();
1389 gtk_option_menu_set_menu(GTK_OPTION_MENU(button), menu);
1391 gtk_icon_theme_get_search_path(icon_theme, &theme_dirs, &n_dirs);
1392 names = g_ptr_array_new();
1393 for (i = 0; i < n_dirs; i++)
1394 add_themes_from_dir(names, theme_dirs[i]);
1395 g_strfreev(theme_dirs);
1397 g_ptr_array_sort(names, strcmp2);
1399 for (i = 0; i < names->len; i++)
1401 GtkWidget *item;
1402 char *name = names->pdata[i];
1404 item = gtk_menu_item_new_with_label(name);
1405 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1406 gtk_widget_show_all(item);
1408 g_free(name);
1411 g_ptr_array_free(names, TRUE);
1413 option->update_widget = update_theme;
1414 option->read_widget = read_theme;
1415 option->widget = button;
1417 gtk_signal_connect_object(GTK_OBJECT(button), "changed",
1418 GTK_SIGNAL_FUNC(option_check_widget),
1419 (GtkObject *) option);
1421 return g_list_append(NULL, hbox);