r4646: Bugfix: when a panel icon's image changed, the display didn't update
[rox-filer/translations.git] / ROX-Filer / src / type.c
blobbfaf11ddf9e3efb75fe88baa5eecc3d97d06cbfe
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* type.c - code for dealing with filetypes */
22 #include "config.h"
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <time.h>
29 #include <sys/param.h>
30 #include <fnmatch.h>
31 #include <sys/types.h>
32 #include <fcntl.h>
34 #ifdef WITH_GNOMEVFS
35 # include <libgnomevfs/gnome-vfs.h>
36 # include <libgnomevfs/gnome-vfs-mime.h>
37 # include <libgnomevfs/gnome-vfs-mime-handlers.h>
38 # include <libgnomevfs/gnome-vfs-application-registry.h>
39 #endif
41 #include "global.h"
43 #include "string.h"
44 #include "fscache.h"
45 #include "main.h"
46 #include "pixmaps.h"
47 #include "run.h"
48 #include "gui_support.h"
49 #include "choices.h"
50 #include "type.h"
51 #include "support.h"
52 #include "diritem.h"
53 #include "dnd.h"
54 #include "options.h"
55 #include "filer.h"
56 #include "action.h" /* (for action_chmod) */
57 #include "xml.h"
58 #include "dropbox.h"
59 #include "xdgmime.h"
60 #include "xtypes.h"
61 #include "run.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 alloc_type_colours(void);
89 static void options_changed(void);
90 static char *get_action_save_path(GtkWidget *dialog);
91 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create);
92 static gboolean remove_handler_with_confirm(const guchar *path);
93 static void set_icon_theme(void);
94 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label);
96 /* Hash of all allocated MIME types, indexed by "media/subtype".
97 * MIME_type structs are never freed; this table prevents memory leaks
98 * when rereading the config files.
100 static GHashTable *type_hash = NULL;
102 /* Most things on Unix are text files, so this is the default type */
103 MIME_type *text_plain;
104 MIME_type *inode_directory;
105 MIME_type *inode_mountpoint;
106 MIME_type *inode_pipe;
107 MIME_type *inode_socket;
108 MIME_type *inode_block_dev;
109 MIME_type *inode_char_dev;
110 MIME_type *application_executable;
111 MIME_type *application_octet_stream;
112 MIME_type *application_x_shellscript;
113 MIME_type *application_x_desktop;
114 MIME_type *inode_unknown;
115 MIME_type *inode_door;
117 static Option o_display_colour_types;
118 static Option o_icon_theme;
120 GtkIconTheme *icon_theme = NULL;
122 void type_init(void)
124 int i;
126 icon_theme = gtk_icon_theme_new();
128 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
130 text_plain = get_mime_type("text/plain", TRUE);
131 inode_directory = get_mime_type("inode/directory", TRUE);
132 inode_mountpoint = get_mime_type("inode/mount-point", TRUE);
133 inode_pipe = get_mime_type("inode/fifo", TRUE);
134 inode_socket = get_mime_type("inode/socket", TRUE);
135 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
136 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
137 application_executable = get_mime_type("application/x-executable", TRUE);
138 application_octet_stream = get_mime_type("application/octet-stream", TRUE);
139 application_x_shellscript = get_mime_type("application/x-shellscript", TRUE);
140 application_x_desktop = get_mime_type("application/x-desktop", TRUE);
141 application_x_desktop->executable = TRUE;
142 inode_unknown = get_mime_type("inode/unknown", TRUE);
143 inode_door = get_mime_type("inode/door", TRUE);
145 option_add_string(&o_icon_theme, "icon_theme", "ROX");
146 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
147 option_register_widget("icon-theme-chooser", build_icon_theme);
149 for (i = 0; i < NUM_TYPE_COLOURS; i++)
150 option_add_string(&o_type_colours[i],
151 opt_type_colours[i][0],
152 opt_type_colours[i][1]);
153 alloc_type_colours();
155 set_icon_theme();
157 option_add_notify(options_changed);
160 /* Read-load all the glob patterns.
161 * Note: calls filer_update_all.
163 void reread_mime_files(void)
165 gtk_icon_theme_rescan_if_needed(icon_theme);
167 xdg_mime_shutdown();
169 filer_update_all();
172 /* Returns the MIME_type structure for the given type name. It is looked
173 * up in type_hash and returned if found. If not found (and can_create is
174 * TRUE) then a new MIME_type is made, added to type_hash and returned.
175 * NULL is returned if type_name is not in type_hash and can_create is
176 * FALSE, or if type_name does not contain a '/' character.
178 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
180 MIME_type *mtype;
181 gchar *slash;
183 mtype = g_hash_table_lookup(type_hash, type_name);
184 if (mtype || !can_create)
185 return mtype;
187 slash = strchr(type_name, '/');
188 g_return_val_if_fail(slash != NULL, NULL); /* XXX: Report nicely */
190 mtype = g_new(MIME_type, 1);
191 mtype->media_type = g_strndup(type_name, slash - type_name);
192 mtype->subtype = g_strdup(slash + 1);
193 mtype->image = NULL;
194 mtype->comment = NULL;
196 mtype->executable = xdg_mime_mime_type_subclass(type_name,
197 "application/x-executable");
199 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
201 return mtype;
204 const char *basetype_name(DirItem *item)
206 if (item->flags & ITEM_FLAG_SYMLINK)
207 return _("Sym link");
208 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
209 return _("Mount point");
210 else if (item->flags & ITEM_FLAG_APPDIR)
211 return _("App dir");
213 switch (item->base_type)
215 case TYPE_FILE:
216 return _("File");
217 case TYPE_DIRECTORY:
218 return _("Dir");
219 case TYPE_CHAR_DEVICE:
220 return _("Char dev");
221 case TYPE_BLOCK_DEVICE:
222 return _("Block dev");
223 case TYPE_PIPE:
224 return _("Pipe");
225 case TYPE_SOCKET:
226 return _("Socket");
227 case TYPE_DOOR:
228 return _("Door");
231 return _("Unknown");
234 static void append_names(gpointer key, gpointer value, gpointer udata)
236 GList **list = (GList **) udata;
238 *list = g_list_prepend(*list, key);
241 /* Return list of all mime type names. Caller must free the list
242 * but NOT the strings it contains (which are never freed).
244 GList *mime_type_name_list(void)
246 GList *list = NULL;
248 g_hash_table_foreach(type_hash, append_names, &list);
249 list = g_list_sort(list, (GCompareFunc) strcmp);
251 return list;
254 /* MIME-type guessing */
256 /* Get the type of this file - stats the file and uses that if
257 * possible. For regular or missing files, uses the pathname.
259 MIME_type *type_get_type(const guchar *path)
261 DirItem *item;
262 MIME_type *type = NULL;
264 item = diritem_new("");
265 diritem_restat(path, item, NULL);
266 if (item->base_type != TYPE_ERROR)
267 type = item->mime_type;
268 diritem_free(item);
270 if (type)
271 return type;
273 type = type_from_path(path);
275 if (!type)
276 return text_plain;
278 return type;
281 /* Returns a pointer to the MIME-type.
283 * Tries all enabled methods:
284 * - Look for extended attribute
285 * - If no attribute, check file name
286 * - If no name rule, check contents
288 * NULL if we can't think of anything.
290 MIME_type *type_from_path(const char *path)
292 MIME_type *mime_type = NULL;
293 const char *type_name;
295 /* Check for extended attribute first */
296 mime_type = xtype_get(path);
297 if (mime_type)
298 return mime_type;
300 /* Try name and contents next */
301 type_name = xdg_mime_get_mime_type_for_file(path);
302 if (type_name)
303 return get_mime_type(type_name, TRUE);
305 return NULL;
308 /* Returns the file/dir in Choices for handling this type.
309 * NULL if there isn't one. g_free() the result.
311 char *handler_for(MIME_type *type)
313 char *type_name;
314 char *open;
316 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
317 open = choices_find_xdg_path_load(type_name, "MIME-types", SITE);
318 g_free(type_name);
320 if (!open)
321 open = choices_find_xdg_path_load(type->media_type,
322 "MIME-types", SITE);
324 return open;
327 MIME_type *mime_type_lookup(const char *type)
329 return get_mime_type(type, TRUE);
332 /* Actions for types */
334 /* Return the image for this type, loading it if needed.
335 * Places to check are: (eg type="text_plain", base="text")
336 * 1. <Choices>/MIME-icons/base_subtype
337 * 2. Icon theme 'mime-base:subtype'
338 * 3. Icon theme 'mime-base'
339 * 4. Unknown type icon.
341 * Note: You must g_object_unref() the image afterwards.
343 MaskedPixmap *type_to_icon(MIME_type *type)
345 GtkIconInfo *full;
346 char *type_name, *path;
347 time_t now;
349 if (type == NULL)
351 g_object_ref(im_unknown);
352 return im_unknown;
355 now = time(NULL);
356 /* Already got an image? */
357 if (type->image)
359 /* Yes - don't recheck too often */
360 if (abs(now - type->image_time) < 2)
362 g_object_ref(type->image);
363 return type->image;
365 g_object_unref(type->image);
366 type->image = NULL;
369 type_name = g_strconcat(type->media_type, "_", type->subtype,
370 ".png", NULL);
371 path = choices_find_xdg_path_load(type_name, "MIME-icons", SITE);
372 g_free(type_name);
373 if (path)
375 type->image = g_fscache_lookup(pixmap_cache, path);
376 g_free(path);
379 if (type->image)
380 goto out;
382 type_name = g_strconcat("mime-", type->media_type, ":",
383 type->subtype, NULL);
384 full = gtk_icon_theme_lookup_icon(icon_theme, type_name, HUGE_HEIGHT, 0);
385 g_free(type_name);
386 if (!full)
388 /* Ugly hack... try for a GNOME icon */
389 if (type == inode_directory)
390 type_name = g_strdup("gnome-fs-directory");
391 else
392 type_name = g_strconcat("gnome-mime-", type->media_type,
393 "-", type->subtype, NULL);
394 full = gtk_icon_theme_lookup_icon(icon_theme,
395 type_name,
396 HUGE_HEIGHT, 0);
397 g_free(type_name);
399 if (!full)
401 /* Try for a media type */
402 type_name = g_strconcat("mime-", type->media_type, NULL);
403 full = gtk_icon_theme_lookup_icon(icon_theme,
404 type_name,
405 HUGE_HEIGHT, 0);
406 g_free(type_name);
408 if (!full)
410 /* Ugly hack... try for a GNOME default media icon */
411 type_name = g_strconcat("gnome-mime-", type->media_type, NULL);
413 full = gtk_icon_theme_lookup_icon(icon_theme,
414 type_name,
415 HUGE_HEIGHT, 0);
416 g_free(type_name);
418 if (full)
420 const char *icon_path;
421 /* Get the actual icon through our cache, not through GTK, because
422 * GTK doesn't cache icons.
424 icon_path = gtk_icon_info_get_filename(full);
425 if (icon_path != NULL)
426 type->image = g_fscache_lookup(pixmap_cache, icon_path);
427 /* else shouldn't happen, because we didn't use
428 * GTK_ICON_LOOKUP_USE_BUILTIN.
430 gtk_icon_info_free(full);
433 out:
434 if (!type->image)
436 /* One ref from the type structure, one returned */
437 type->image = im_unknown;
438 g_object_ref(im_unknown);
441 type->image_time = now;
443 g_object_ref(type->image);
444 return type->image;
447 GdkAtom type_to_atom(MIME_type *type)
449 char *str;
450 GdkAtom retval;
452 g_return_val_if_fail(type != NULL, GDK_NONE);
454 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
455 retval = gdk_atom_intern(str, FALSE);
456 g_free(str);
458 return retval;
461 static void show_shell_help(gpointer data)
463 info_message(_("Enter a shell command which will load \"$@\" into "
464 "a suitable program. Eg:\n\n"
465 "gimp \"$@\""));
468 /* Called if the user clicks on the OK button. Returns FALSE if an error
469 * was displayed instead of performing the action.
471 static gboolean set_shell_action(GtkWidget *dialog)
473 GtkEntry *entry;
474 const guchar *command;
475 gchar *tmp, *path;
476 int error = 0, len;
477 int fd;
479 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
481 g_return_val_if_fail(entry != NULL, FALSE);
483 command = gtk_entry_get_text(entry);
485 if (!strchr(command, '$'))
487 show_shell_help(NULL);
488 return FALSE;
491 path = get_action_save_path(dialog);
492 if (!path)
493 return FALSE;
495 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
496 len = strlen(tmp);
498 fd = open(path, O_CREAT | O_WRONLY, 0755);
499 if (fd == -1)
500 error = errno;
501 else
503 FILE *file;
505 file = fdopen(fd, "w");
506 if (file)
508 if (fwrite(tmp, 1, len, file) < len)
509 error = errno;
510 if (fclose(file) && error == 0)
511 error = errno;
513 else
514 error = errno;
517 if (error)
518 report_error(g_strerror(error));
520 g_free(tmp);
521 g_free(path);
523 gtk_widget_destroy(dialog);
525 return TRUE;
528 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
530 if (response == GTK_RESPONSE_OK)
531 if (!set_shell_action(dialog))
532 return;
533 gtk_widget_destroy(dialog);
536 /* Return the path of the file in choices that handles this type and
537 * radio setting.
538 * NULL if nothing is defined for it.
540 static guchar *handler_for_radios(GObject *dialog)
542 Radios *radios;
543 MIME_type *type;
545 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
546 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
548 g_return_val_if_fail(radios != NULL, NULL);
549 g_return_val_if_fail(type != NULL, NULL);
551 switch (radios_get_value(radios))
553 case SET_MEDIA:
554 return choices_find_xdg_path_load(type->media_type,
555 "MIME-types", SITE);
556 case SET_TYPE:
558 gchar *tmp, *handler;
559 tmp = g_strconcat(type->media_type, "_",
560 type->subtype, NULL);
561 handler = choices_find_xdg_path_load(tmp,
562 "MIME-types",
563 SITE);
564 g_free(tmp);
565 return handler;
567 default:
568 g_warning("Bad type");
569 return NULL;
573 /* (radios can be NULL if called from clear_run_action) */
574 static void run_action_update(Radios *radios, gpointer data)
576 guchar *handler;
577 DropBox *drop_box;
578 GObject *dialog = G_OBJECT(data);
580 drop_box = g_object_get_data(dialog, "rox-dropbox");
582 g_return_if_fail(drop_box != NULL);
584 handler = handler_for_radios(dialog);
586 if (handler)
588 char *old = handler;
590 handler = readlink_dup(old);
591 if (handler)
592 g_free(old);
593 else
594 handler = old;
597 drop_box_set_path(DROP_BOX(drop_box), handler);
598 g_free(handler);
601 static void clear_run_action(GtkWidget *drop_box, GtkWidget *dialog)
603 guchar *handler;
605 handler = handler_for_radios(G_OBJECT(dialog));
607 if (handler)
608 remove_handler_with_confirm(handler);
610 run_action_update(NULL, dialog);
613 /* Called when a URI list is dropped onto the box in the Set Run Action
614 * dialog. Make sure it's an application, and make that the default
615 * handler.
617 static void drag_app_dropped(GtkWidget *drop_box,
618 const guchar *app,
619 GtkWidget *dialog)
621 DirItem *item;
623 item = diritem_new("");
624 diritem_restat(app, item, NULL);
625 if (item->flags & ITEM_FLAG_APPDIR || EXECUTABLE_FILE(item))
627 guchar *path;
629 path = get_action_save_path(dialog);
631 if (path)
633 if (symlink(app, path))
634 delayed_error("symlink: %s",
635 g_strerror(errno));
636 else
637 destroy_on_idle(dialog);
639 g_free(path);
642 else
643 delayed_error(
644 _("This is not a program! Give me an application "
645 "instead!"));
647 diritem_free(item);
650 /* Find the current command which is used to run files of this type.
651 * Returns NULL on failure. g_free() the result.
653 static guchar *get_current_command(MIME_type *type)
655 struct stat info;
656 char *handler, *nl, *data = NULL;
657 long len;
658 guchar *command = NULL;
660 handler = handler_for(type);
662 if (!handler)
663 return NULL; /* No current handler */
665 if (stat(handler, &info))
666 goto out; /* Can't stat */
668 if ((!S_ISREG(info.st_mode)) || info.st_size > 256)
669 goto out; /* Only use small regular files */
671 if (!load_file(handler, &data, &len))
672 goto out; /* Didn't load OK */
674 if (strncmp(data, "#! /bin/sh\nexec ", 16) != 0)
675 goto out; /* Not one of ours */
677 nl = strchr(data + 16, '\n');
678 if (!nl)
679 goto out; /* No newline! */
681 command = g_strndup(data + 16, nl - data - 16);
682 out:
683 g_free(handler);
684 g_free(data);
685 return command;
688 /* Find the current command which is used to run files of this type,
689 * and return a textual description of it.
690 * Only call for non-executable files.
691 * g_free() the result.
693 gchar *describe_current_command(MIME_type *type)
695 char *handler;
696 char *desc = NULL;
697 struct stat info;
698 char *target;
700 g_return_val_if_fail(type != NULL, NULL);
702 handler = handler_for(type);
704 if (!handler)
705 return g_strdup(_("No run action defined"));
707 target = readlink_dup(handler);
708 if (target)
710 /* Cope with relative paths (shouldn't normally be needed) */
712 if (target[0] == '/')
714 g_free(handler);
715 handler = target;
717 else
719 gchar *dir;
721 dir = g_path_get_dirname(handler);
722 g_free(handler);
723 handler = g_strconcat(dir, "/", target, NULL);
724 g_free(target);
725 g_free(dir);
729 if (mc_stat(handler, &info) !=0 )
731 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
732 g_strerror(errno));
733 goto out;
736 if (S_ISDIR(info.st_mode))
738 const guchar *tmp;
739 uid_t dir_uid = info.st_uid;
741 tmp = make_path(handler, "AppRun");
743 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
744 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
745 desc = g_strdup_printf(
746 _("Invalid application %s (bad AppRun)"),
747 handler);
748 /* Else, just report handler... */
750 goto out;
753 /* It's not an application directory, and it's not a symlink... */
755 if (access(handler, X_OK) != 0)
757 desc = g_strdup_printf(_("Non-executable %s"), handler);
758 goto out;
761 desc = get_current_command(type);
762 out:
763 if (!desc)
764 desc = handler;
765 else
766 g_free(handler);
768 return desc;
771 /* Display a dialog box allowing the user to set the default run action
772 * for this type.
774 void type_set_handler_dialog(MIME_type *type)
776 guchar *tmp;
777 GtkDialog *dialog;
778 GtkWidget *frame, *entry, *label, *button;
779 GtkWidget *hbox;
780 Radios *radios;
782 g_return_if_fail(type != NULL);
784 dialog = GTK_DIALOG(gtk_dialog_new());
785 gtk_dialog_set_has_separator(dialog, FALSE);
786 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
788 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
790 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
792 radios = radios_new(run_action_update, dialog);
793 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
795 radios_add(radios,
796 _("If a handler for the specific type isn't set up, "
797 "use this as the default."), SET_MEDIA,
798 _("Set default for all `%s/<anything>'"),
799 type->media_type);
801 radios_add(radios,
802 _("Use this application for all files with this MIME "
803 "type."), SET_TYPE,
804 _("Only for the type `%s' (%s/%s)"),
805 mime_type_comment(type),
806 type->media_type, type->subtype);
808 radios_set_value(radios, SET_TYPE);
810 frame = drop_box_new(_("Drop a suitable application here"));
812 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
814 radios_pack(radios, GTK_BOX(dialog->vbox));
815 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 0);
817 g_signal_connect(frame, "path_dropped",
818 G_CALLBACK(drag_app_dropped), dialog);
819 g_signal_connect(frame, "clear",
820 G_CALLBACK(clear_run_action), dialog);
822 hbox = gtk_hbox_new(FALSE, 4);
823 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
824 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
825 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
826 FALSE, TRUE, 0);
827 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
829 hbox = gtk_hbox_new(FALSE, 4);
830 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
832 label = gtk_label_new(_("Enter a shell command:")),
833 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
834 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
836 gtk_box_pack_start(GTK_BOX(hbox),
837 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
839 entry = gtk_entry_new();
840 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
841 gtk_widget_grab_focus(entry);
842 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
843 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
845 /* If possible, fill in the entry box with the current command */
846 tmp = get_current_command(type);
847 if (tmp)
849 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
850 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
851 g_free(tmp);
853 else
855 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
856 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
859 gtk_dialog_add_button(dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL);
861 button = button_new_mixed(GTK_STOCK_OK, _("_Use Command"));
862 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
863 gtk_dialog_add_action_widget(dialog, button, GTK_RESPONSE_OK);
865 hbox = gtk_hbox_new(TRUE, 4);
866 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
868 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
870 g_signal_connect(dialog, "response",
871 G_CALLBACK(set_action_response), NULL);
873 gtk_widget_show_all(GTK_WIDGET(dialog));
876 /* path is an entry in Choices. If it's a symlink or a very small executable
877 * then just get rid of it, otherwise confirm first. It it doesn't exist,
878 * do nothing.
880 * FALSE on error (abort operation).
882 static gboolean remove_handler_with_confirm(const guchar *path)
884 struct stat info;
886 if (lstat(path, &info) == 0)
888 /* A binding already exists... */
889 if (S_ISREG(info.st_mode) && info.st_size > 256)
891 if (!confirm(_("A run action already exists and is "
892 "quite a big program - are you sure "
893 "you want to delete it?"),
894 GTK_STOCK_DELETE, NULL))
896 return FALSE;
900 if (unlink(path))
902 report_error(_("Can't remove %s: %s"),
903 path, g_strerror(errno));
904 return FALSE;
908 return TRUE;
911 /* The user wants to set a new default action for files of this type (or just
912 * clear the action). Removes the current binding if possible and returns the
913 * path to save the new one to. NULL means cancel. g_free() the result.
915 static char *get_action_save_path(GtkWidget *dialog)
917 guchar *path = NULL;
918 guchar *type_name = NULL;
919 MIME_type *type;
920 Radios *radios;
922 g_return_val_if_fail(dialog != NULL, NULL);
924 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
925 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
927 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
929 if (radios_get_value(radios) == SET_MEDIA)
930 type_name = g_strdup(type->media_type);
931 else
932 type_name = g_strconcat(type->media_type, "_",
933 type->subtype, NULL);
935 path = choices_find_xdg_path_save("", PROJECT, SITE, FALSE);
936 if (!path)
938 report_error(
939 _("Choices saving is disabled by CHOICESPATH variable"));
940 goto out;
942 g_free(path);
944 path = choices_find_xdg_path_save(type_name, "MIME-types", SITE, TRUE);
946 if (!remove_handler_with_confirm(path))
947 null_g_free(&path);
948 out:
949 g_free(type_name);
950 return path;
953 MIME_type *mime_type_from_base_type(int base_type)
955 switch (base_type)
957 case TYPE_FILE:
958 return text_plain;
959 case TYPE_DIRECTORY:
960 return inode_directory;
961 case TYPE_PIPE:
962 return inode_pipe;
963 case TYPE_SOCKET:
964 return inode_socket;
965 case TYPE_BLOCK_DEVICE:
966 return inode_block_dev;
967 case TYPE_CHAR_DEVICE:
968 return inode_char_dev;
969 case TYPE_DOOR:
970 return inode_door;
972 return inode_unknown;
975 /* Takes the st_mode field from stat() and returns the base type.
976 * Should not be a symlink.
978 int mode_to_base_type(int st_mode)
980 if (S_ISREG(st_mode))
981 return TYPE_FILE;
982 else if (S_ISDIR(st_mode))
983 return TYPE_DIRECTORY;
984 else if (S_ISBLK(st_mode))
985 return TYPE_BLOCK_DEVICE;
986 else if (S_ISCHR(st_mode))
987 return TYPE_CHAR_DEVICE;
988 else if (S_ISFIFO(st_mode))
989 return TYPE_PIPE;
990 else if (S_ISSOCK(st_mode))
991 return TYPE_SOCKET;
992 else if (S_ISDOOR(st_mode))
993 return TYPE_DOOR;
995 return TYPE_ERROR;
998 /* Returns TRUE is this is something that is run by looking up its type
999 * in MIME-types and, hence, can have its run action set.
1001 gboolean can_set_run_action(DirItem *item)
1003 g_return_val_if_fail(item != NULL, FALSE);
1005 return item->base_type == TYPE_FILE && !EXECUTABLE_FILE(item);
1008 /* Parse file type colours and allocate/free them as necessary */
1009 static void alloc_type_colours(void)
1011 gboolean success[NUM_TYPE_COLOURS];
1012 int change_count = 0; /* No. needing realloc */
1013 int i;
1014 static gboolean allocated = FALSE;
1016 /* Parse colours */
1017 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1019 GdkColor *c = &type_colours[i];
1020 gushort r = c->red;
1021 gushort g = c->green;
1022 gushort b = c->blue;
1024 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1026 if (allocated && (c->red != r || c->green != g || c->blue != b))
1027 change_count++;
1030 /* Free colours if they were previously allocated and
1031 * have changed or become unneeded.
1033 if (allocated && (change_count || !o_display_colour_types.int_value))
1035 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1036 type_colours, NUM_TYPE_COLOURS);
1037 allocated = FALSE;
1040 /* Allocate colours, unless they are still allocated (=> they didn't
1041 * change) or we don't want them anymore.
1042 * XXX: what should be done if allocation fails?
1044 if (!allocated && o_display_colour_types.int_value)
1046 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1047 type_colours, NUM_TYPE_COLOURS,
1048 FALSE, TRUE, success);
1049 allocated = TRUE;
1053 static void expire_timer(gpointer key, gpointer value, gpointer data)
1055 MIME_type *type = value;
1057 type->image_time = 0;
1060 static void options_changed(void)
1062 alloc_type_colours();
1063 if (o_icon_theme.has_changed)
1065 set_icon_theme();
1066 g_hash_table_foreach(type_hash, expire_timer, NULL);
1067 full_refresh();
1071 /* Return a pointer to a (static) colour for this item. If colouring is
1072 * off, returns normal.
1074 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1076 int type = item->base_type;
1078 if (!o_display_colour_types.int_value)
1079 return normal;
1081 if (EXECUTABLE_FILE(item))
1082 type = TYPE_EXEC;
1083 else if (item->flags & ITEM_FLAG_APPDIR)
1084 type = TYPE_APPDIR;
1086 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1088 return &type_colours[type];
1091 static char **get_xdg_data_dirs(int *n_dirs)
1093 const char *env;
1094 char **dirs;
1095 int i, n;
1097 env = getenv("XDG_DATA_DIRS");
1098 if (!env)
1099 env = "/usr/local/share/:/usr/share/";
1100 dirs = g_strsplit(env, ":", 0);
1101 g_return_val_if_fail(dirs != NULL, NULL);
1102 for (n = 0; dirs[n]; n++)
1104 for (i = n; i > 0; i--)
1105 dirs[i] = dirs[i - 1];
1106 env = getenv("XDG_DATA_HOME");
1107 if (env)
1108 dirs[0] = g_strdup(env);
1109 else
1110 dirs[0] = g_build_filename(g_get_home_dir(), ".local",
1111 "share", NULL);
1112 *n_dirs = n + 1;
1113 return dirs;
1116 /* Try to fill in 'type->comment' from this document */
1117 static void get_comment(MIME_type *type, const guchar *path)
1119 xmlNode *node;
1120 XMLwrapper *doc;
1122 doc = xml_cache_load(path);
1123 if (!doc)
1124 return;
1126 node = xml_get_section(doc, TYPE_NS, "comment");
1128 if (node)
1130 char *val;
1131 g_return_if_fail(type->comment == NULL);
1132 val= xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
1133 type->comment = g_strdup(val);
1134 xmlFree(val);
1137 g_object_unref(doc);
1140 /* Fill in the comment field for this MIME type */
1141 static void find_comment(MIME_type *type)
1143 char **dirs;
1144 int i, n_dirs = 0;
1146 if (type->comment)
1148 g_free(type->comment);
1149 type->comment = NULL;
1152 dirs = get_xdg_data_dirs(&n_dirs);
1153 g_return_if_fail(dirs != NULL);
1155 for (i = 0; i < n_dirs; i++)
1157 guchar *path;
1159 path = g_strdup_printf("%s/mime/%s/%s.xml", dirs[i],
1160 type->media_type, type->subtype);
1161 get_comment(type, path);
1162 g_free(path);
1163 if (type->comment)
1164 break;
1167 if (!type->comment)
1168 type->comment = g_strdup_printf("%s/%s", type->media_type,
1169 type->subtype);
1171 for (i = 0; i < n_dirs; i++)
1172 g_free(dirs[i]);
1173 g_free(dirs);
1176 const char *mime_type_comment(MIME_type *type)
1178 if (!type->comment)
1179 find_comment(type);
1181 return type->comment;
1184 static void set_icon_theme(void)
1186 GtkIconInfo *info;
1187 char *icon_home;
1188 const char *theme_name = o_icon_theme.value;
1190 if (!theme_name || !*theme_name)
1191 theme_name = "ROX";
1193 while (1)
1195 gtk_icon_theme_set_custom_theme(icon_theme, theme_name);
1196 info = gtk_icon_theme_lookup_icon(icon_theme,
1197 "mime-application:postscript",
1198 ICON_HEIGHT, 0);
1199 if (!info)
1201 info = gtk_icon_theme_lookup_icon(icon_theme,
1202 "gnome-mime-application-postscript",
1203 ICON_HEIGHT, 0);
1205 if (info)
1207 gtk_icon_info_free(info);
1208 return;
1211 if (strcmp(theme_name, "ROX") == 0)
1212 break;
1214 delayed_error(_("Icon theme '%s' does not contain MIME icons. "
1215 "Using ROX default theme instead."),
1216 theme_name);
1218 theme_name = "ROX";
1221 icon_home = g_build_filename(home_dir, ".icons", NULL);
1222 if (!file_exists(icon_home))
1223 mkdir(icon_home, 0755);
1224 g_free(icon_home);
1226 icon_home = g_build_filename(home_dir, ".icons", "ROX", NULL);
1227 if (symlink(make_path(app_dir, "ROX"), icon_home))
1229 delayed_error(_("Failed to create symlink '%s':\n%s\n\n"
1230 "(this may mean that the ROX theme already exists there, but "
1231 "the 'mime-application:postscript' icon couldn't be loaded for "
1232 "some reason, or %s is a link to an invalid directory; try "
1233 "deleting it)"), icon_home, g_strerror(errno), icon_home);
1234 open_to_show(icon_home);
1236 g_free(icon_home);
1238 gtk_icon_theme_rescan_if_needed(icon_theme);
1241 static guchar *read_theme(Option *option)
1243 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1244 GtkLabel *item;
1246 item = GTK_LABEL(GTK_BIN(om)->child);
1248 g_return_val_if_fail(item != NULL, g_strdup("ROX"));
1250 return g_strdup(gtk_label_get_text(item));
1253 static void update_theme(Option *option)
1255 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1256 GtkWidget *menu;
1257 GList *kids, *next;
1258 int i = 0;
1260 menu = gtk_option_menu_get_menu(om);
1262 kids = gtk_container_get_children(GTK_CONTAINER(menu));
1263 for (next = kids; next; next = next->next, i++)
1265 GtkLabel *item = GTK_LABEL(GTK_BIN(next->data)->child);
1266 const gchar *label;
1268 /* The label actually moves from the menu!! */
1269 if (!item)
1270 item = GTK_LABEL(GTK_BIN(om)->child);
1272 label = gtk_label_get_text(item);
1274 g_return_if_fail(label != NULL);
1276 if (strcmp(label, option->value) == 0)
1277 break;
1279 g_list_free(kids);
1281 if (next)
1282 gtk_option_menu_set_history(om, i);
1283 else
1284 g_warning("Theme '%s' not found", option->value);
1287 static void add_themes_from_dir(GPtrArray *names, const char *dir)
1289 GPtrArray *list;
1290 int i;
1292 if (access(dir, F_OK) != 0)
1293 return;
1295 list = list_dir(dir);
1296 g_return_if_fail(list != NULL);
1298 for (i = 0; i < list->len; i++)
1300 char *index_path;
1302 index_path = g_build_filename(dir, list->pdata[i],
1303 "index.theme", NULL);
1305 if (access(index_path, F_OK) == 0)
1306 g_ptr_array_add(names, list->pdata[i]);
1307 else
1308 g_free(list->pdata[i]);
1310 g_free(index_path);
1313 g_ptr_array_free(list, TRUE);
1316 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label)
1318 GtkWidget *button, *menu, *hbox;
1319 GPtrArray *names;
1320 gchar **theme_dirs = NULL;
1321 gint n_dirs = 0;
1322 int i;
1324 g_return_val_if_fail(option != NULL, NULL);
1325 g_return_val_if_fail(label != NULL, NULL);
1327 hbox = gtk_hbox_new(FALSE, 4);
1329 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(label)),
1330 FALSE, TRUE, 0);
1332 button = gtk_option_menu_new();
1333 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
1335 menu = gtk_menu_new();
1336 gtk_option_menu_set_menu(GTK_OPTION_MENU(button), menu);
1338 gtk_icon_theme_get_search_path(icon_theme, &theme_dirs, &n_dirs);
1339 names = g_ptr_array_new();
1340 for (i = 0; i < n_dirs; i++)
1341 add_themes_from_dir(names, theme_dirs[i]);
1342 g_strfreev(theme_dirs);
1344 g_ptr_array_sort(names, strcmp2);
1346 for (i = 0; i < names->len; i++)
1348 GtkWidget *item;
1349 char *name = names->pdata[i];
1351 item = gtk_menu_item_new_with_label(name);
1352 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1353 gtk_widget_show_all(item);
1355 g_free(name);
1358 g_ptr_array_free(names, TRUE);
1360 option->update_widget = update_theme;
1361 option->read_widget = read_theme;
1362 option->widget = button;
1364 gtk_signal_connect_object(GTK_OBJECT(button), "changed",
1365 GTK_SIGNAL_FUNC(option_check_widget),
1366 (GtkObject *) option);
1368 return g_list_append(NULL, hbox);