Converted README to markdown
[rox-filer.git] / ROX-Filer / src / type.c
blobcf2a324d11f42d0d905b05fb10e459dd49b23fba
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 static GtkIconTheme *icon_theme = NULL;
121 static GtkIconTheme *rox_theme = NULL;
122 static GtkIconTheme *gnome_theme = NULL;
124 void type_init(void)
126 int i;
128 icon_theme = gtk_icon_theme_new();
130 type_hash = g_hash_table_new(g_str_hash, g_str_equal);
132 text_plain = get_mime_type("text/plain", TRUE);
133 inode_directory = get_mime_type("inode/directory", TRUE);
134 inode_mountpoint = get_mime_type("inode/mount-point", TRUE);
135 inode_pipe = get_mime_type("inode/fifo", TRUE);
136 inode_socket = get_mime_type("inode/socket", TRUE);
137 inode_block_dev = get_mime_type("inode/blockdevice", TRUE);
138 inode_char_dev = get_mime_type("inode/chardevice", TRUE);
139 application_executable = get_mime_type("application/x-executable", TRUE);
140 application_octet_stream = get_mime_type("application/octet-stream", TRUE);
141 application_x_shellscript = get_mime_type("application/x-shellscript", TRUE);
142 application_x_desktop = get_mime_type("application/x-desktop", TRUE);
143 application_x_desktop->executable = TRUE;
144 inode_unknown = get_mime_type("inode/unknown", TRUE);
145 inode_door = get_mime_type("inode/door", TRUE);
147 option_add_string(&o_icon_theme, "icon_theme", "ROX");
148 option_add_int(&o_display_colour_types, "display_colour_types", TRUE);
149 option_register_widget("icon-theme-chooser", build_icon_theme);
151 for (i = 0; i < NUM_TYPE_COLOURS; i++)
152 option_add_string(&o_type_colours[i],
153 opt_type_colours[i][0],
154 opt_type_colours[i][1]);
155 alloc_type_colours();
157 set_icon_theme();
159 option_add_notify(options_changed);
162 /* Read-load all the glob patterns.
163 * Note: calls filer_update_all.
165 void reread_mime_files(void)
167 gtk_icon_theme_rescan_if_needed(icon_theme);
169 xdg_mime_shutdown();
171 filer_update_all();
174 /* Returns the MIME_type structure for the given type name. It is looked
175 * up in type_hash and returned if found. If not found (and can_create is
176 * TRUE) then a new MIME_type is made, added to type_hash and returned.
177 * NULL is returned if type_name is not in type_hash and can_create is
178 * FALSE, or if type_name does not contain a '/' character.
180 static MIME_type *get_mime_type(const gchar *type_name, gboolean can_create)
182 MIME_type *mtype;
183 gchar *slash;
185 mtype = g_hash_table_lookup(type_hash, type_name);
186 if (mtype || !can_create)
187 return mtype;
189 slash = strchr(type_name, '/');
190 if (slash == NULL)
192 g_warning("MIME type '%s' does not contain a '/' character!",
193 type_name);
194 return NULL;
197 mtype = g_new(MIME_type, 1);
198 mtype->media_type = g_strndup(type_name, slash - type_name);
199 mtype->subtype = g_strdup(slash + 1);
200 mtype->image = NULL;
201 mtype->comment = NULL;
203 mtype->executable = xdg_mime_mime_type_subclass(type_name,
204 "application/x-executable");
206 g_hash_table_insert(type_hash, g_strdup(type_name), mtype);
208 return mtype;
211 const char *basetype_name(DirItem *item)
213 if (item->flags & ITEM_FLAG_SYMLINK)
214 return _("Sym link");
215 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
216 return _("Mount point");
217 else if (item->flags & ITEM_FLAG_APPDIR)
218 return _("App dir");
220 switch (item->base_type)
222 case TYPE_FILE:
223 return _("File");
224 case TYPE_DIRECTORY:
225 return _("Dir");
226 case TYPE_CHAR_DEVICE:
227 return _("Char dev");
228 case TYPE_BLOCK_DEVICE:
229 return _("Block dev");
230 case TYPE_PIPE:
231 return _("Pipe");
232 case TYPE_SOCKET:
233 return _("Socket");
234 case TYPE_DOOR:
235 return _("Door");
238 return _("Unknown");
241 struct mime_list {
242 GList *list;
243 gboolean only_regular;
246 static void append_names(gpointer key, gpointer value, gpointer udata)
248 struct mime_list *mlist = (struct mime_list*) udata;
250 if(!mlist->only_regular || strncmp((char *)key, "inode/", 6)!=0)
251 mlist->list = g_list_prepend(mlist->list, key);
254 /* Return list of all mime type names. Caller must free the list
255 * but NOT the strings it contains (which are never freed).
256 If only_regular is true then inode types are excluded.
258 GList *mime_type_name_list(gboolean only_regular)
260 struct mime_list list;
262 list.list=NULL;
263 list.only_regular=only_regular;
265 g_hash_table_foreach(type_hash, append_names, &list);
266 list.list = g_list_sort(list.list, (GCompareFunc) strcmp);
268 return list.list;
271 /* MIME-type guessing */
273 /* Get the type of this file - stats the file and uses that if
274 * possible. For regular or missing files, uses the pathname.
276 MIME_type *type_get_type(const guchar *path)
278 DirItem *item;
279 MIME_type *type = NULL;
281 item = diritem_new("");
282 diritem_restat(path, item, NULL);
283 if (item->base_type != TYPE_ERROR)
284 type = item->mime_type;
285 diritem_free(item);
287 if (type)
288 return type;
290 type = type_from_path(path);
292 if (!type)
293 return text_plain;
295 return type;
298 /* Returns a pointer to the MIME-type.
300 * Tries all enabled methods:
301 * - Look for extended attribute
302 * - If no attribute, check file name
303 * - If no name rule, check contents
305 * NULL if we can't think of anything.
307 MIME_type *type_from_path(const char *path)
309 MIME_type *mime_type = NULL;
310 const char *type_name;
312 /* Check for extended attribute first */
313 mime_type = xtype_get(path);
314 if (mime_type)
315 return mime_type;
317 /* Try name and contents next */
318 type_name = xdg_mime_get_mime_type_for_file(path, NULL);
319 if (type_name)
320 return get_mime_type(type_name, TRUE);
322 return NULL;
325 /* Returns the file/dir in Choices for handling this type.
326 * NULL if there isn't one. g_free() the result.
328 char *handler_for(MIME_type *type)
330 char *type_name;
331 char *open;
332 char *target;
334 type_name = g_strconcat(type->media_type, "_", type->subtype, NULL);
335 open = choices_find_xdg_path_load(type_name, "MIME-types", SITE);
336 g_free(type_name);
338 if (!open)
339 open = choices_find_xdg_path_load(type->media_type,
340 "MIME-types", SITE);
342 if (!open)
343 return NULL;
345 /* Some programs behave differently depending on the command
346 * name (e.g., 'vim' vs 'gvim'), so symlinks need to be followed here.
348 target = readlink_dup(open);
349 if (!target)
351 return open;
354 if (target[0] == '/')
356 /* Absolute path */
357 g_free(open);
358 return target;
360 else
362 /* Relative path (shouldn't normally be needed) */
363 gchar *dir;
364 char *abs_path;
366 dir = g_path_get_dirname(open);
367 g_free(open);
369 abs_path = g_strconcat(dir, "/", target, NULL);
370 g_free(target);
371 g_free(dir);
373 return abs_path;
377 MIME_type *mime_type_lookup(const char *type)
379 return get_mime_type(type, TRUE);
382 static void init_aux_theme(GtkIconTheme **ptheme, const char *name)
384 if (*ptheme)
385 return;
386 *ptheme = gtk_icon_theme_new();
387 gtk_icon_theme_set_custom_theme(*ptheme, name);
390 inline static void init_rox_theme(void)
392 init_aux_theme(&rox_theme, "ROX");
395 inline static void init_gnome_theme(void)
397 init_aux_theme(&gnome_theme, "gnome");
400 /* We don't want ROX to override configured theme so try all possibilities
401 * in icon_theme first */
402 static GtkIconInfo *mime_type_lookup_icon_info(GtkIconTheme *theme,
403 MIME_type *type)
405 char *type_name = g_strconcat(type->media_type, "-", type->subtype, NULL);
406 GtkIconInfo *full = gtk_icon_theme_lookup_icon(theme, type_name, HUGE_HEIGHT, 0);
408 g_free(type_name);
409 if (!full)
411 /* Ugly hack... try for a GNOME icon */
412 if (type == inode_directory)
413 type_name = g_strdup("gnome-fs-directory");
414 else
415 type_name = g_strconcat("gnome-mime-", type->media_type,
416 "-", type->subtype, NULL);
417 full = gtk_icon_theme_lookup_icon(theme, type_name, HUGE_HEIGHT, 0);
418 g_free(type_name);
420 if (!full)
422 /* Try for a media type */
423 type_name = g_strconcat(type->media_type, "-x-generic", NULL);
424 full = gtk_icon_theme_lookup_icon(theme, type_name, HUGE_HEIGHT, 0);
425 g_free(type_name);
427 if (!full)
429 /* Ugly hack... try for a GNOME default media icon */
430 type_name = g_strconcat("gnome-mime-", type->media_type, NULL);
432 full = gtk_icon_theme_lookup_icon(theme, type_name, HUGE_HEIGHT, 0);
433 g_free(type_name);
435 return full;
438 /* Actions for types */
440 /* Return the image for this type, loading it if needed.
441 * Places to check are: (eg type="text_plain", base="text")
442 * 1. <Choices>/MIME-icons/base_subtype
443 * 2. Icon theme 'mime-base:subtype'
444 * 3. Icon theme 'mime-base'
445 * 4. Unknown type icon.
447 * Special case: If an icon cannot be found for inode/mount-point, the icon for
448 * inode/directory will be returned (if possible).
450 * Note: You must g_object_unref() the image afterwards.
452 MaskedPixmap *type_to_icon(MIME_type *type)
454 GtkIconInfo *full;
455 char *type_name, *path;
456 time_t now;
458 if (type == NULL)
460 g_object_ref(im_unknown);
461 return im_unknown;
464 now = time(NULL);
465 /* Already got an image? */
466 if (type->image)
468 /* Yes - don't recheck too often */
469 if (abs(now - type->image_time) < 2)
471 g_object_ref(type->image);
472 return type->image;
474 g_object_unref(type->image);
475 type->image = NULL;
478 again:
479 type_name = g_strconcat(type->media_type, "_", type->subtype,
480 ".png", NULL);
481 path = choices_find_xdg_path_load(type_name, "MIME-icons", SITE);
482 g_free(type_name);
483 if (path)
485 type->image = g_fscache_lookup(pixmap_cache, path);
486 g_free(path);
489 if (type->image)
490 goto out;
492 full = mime_type_lookup_icon_info(icon_theme, type);
493 if (!full && icon_theme != rox_theme)
495 init_rox_theme();
496 full = mime_type_lookup_icon_info(rox_theme, type);
498 if (!full && icon_theme != gnome_theme)
500 init_gnome_theme();
501 full = mime_type_lookup_icon_info(gnome_theme, type);
503 if (!full && type == inode_mountpoint)
505 /* Try to use the inode/directory icon for inode/mount-point */
506 type = inode_directory;
507 goto again;
509 if (full)
511 const char *icon_path;
512 /* Get the actual icon through our cache, not through GTK, because
513 * GTK doesn't cache icons.
515 icon_path = gtk_icon_info_get_filename(full);
516 if (icon_path != NULL)
517 type->image = g_fscache_lookup(pixmap_cache, icon_path);
518 /* else shouldn't happen, because we didn't use
519 * GTK_ICON_LOOKUP_USE_BUILTIN.
521 gtk_icon_info_free(full);
524 out:
525 if (!type->image)
527 /* One ref from the type structure, one returned */
528 type->image = im_unknown;
529 g_object_ref(im_unknown);
532 type->image_time = now;
534 g_object_ref(type->image);
535 return type->image;
538 GdkAtom type_to_atom(MIME_type *type)
540 char *str;
541 GdkAtom retval;
543 g_return_val_if_fail(type != NULL, GDK_NONE);
545 str = g_strconcat(type->media_type, "/", type->subtype, NULL);
546 retval = gdk_atom_intern(str, FALSE);
547 g_free(str);
549 return retval;
552 static void show_shell_help(gpointer data)
554 info_message(_("Enter a shell command which will load \"$@\" into "
555 "a suitable program. Eg:\n\n"
556 "gimp \"$@\""));
559 /* Called if the user clicks on the OK button. Returns FALSE if an error
560 * was displayed instead of performing the action.
562 static gboolean set_shell_action(GtkWidget *dialog)
564 GtkEntry *entry;
565 const guchar *command;
566 gchar *tmp, *path;
567 int error = 0, len;
568 int fd;
570 entry = g_object_get_data(G_OBJECT(dialog), "shell_command");
572 g_return_val_if_fail(entry != NULL, FALSE);
574 command = gtk_entry_get_text(entry);
576 if (!strchr(command, '$'))
578 show_shell_help(NULL);
579 return FALSE;
582 path = get_action_save_path(dialog);
583 if (!path)
584 return FALSE;
586 tmp = g_strdup_printf("#! /bin/sh\nexec %s\n", command);
587 len = strlen(tmp);
589 fd = open(path, O_CREAT | O_WRONLY, 0755);
590 if (fd == -1)
591 error = errno;
592 else
594 FILE *file;
596 file = fdopen(fd, "w");
597 if (file)
599 if (fwrite(tmp, 1, len, file) < len)
600 error = errno;
601 if (fclose(file) && error == 0)
602 error = errno;
604 else
605 error = errno;
608 if (error)
609 report_error(g_strerror(error));
611 g_free(tmp);
612 g_free(path);
614 gtk_widget_destroy(dialog);
616 return TRUE;
619 static void set_action_response(GtkWidget *dialog, gint response, gpointer data)
621 if (response == GTK_RESPONSE_OK)
622 if (!set_shell_action(dialog))
623 return;
624 gtk_widget_destroy(dialog);
627 /* Return the path of the file in choices that handles this type and
628 * radio setting.
629 * NULL if nothing is defined for it.
631 static guchar *handler_for_radios(GObject *dialog)
633 Radios *radios;
634 MIME_type *type;
636 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
637 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
639 g_return_val_if_fail(radios != NULL, NULL);
640 g_return_val_if_fail(type != NULL, NULL);
642 switch (radios_get_value(radios))
644 case SET_MEDIA:
645 return choices_find_xdg_path_load(type->media_type,
646 "MIME-types", SITE);
647 case SET_TYPE:
649 gchar *tmp, *handler;
650 tmp = g_strconcat(type->media_type, "_",
651 type->subtype, NULL);
652 handler = choices_find_xdg_path_load(tmp,
653 "MIME-types",
654 SITE);
655 g_free(tmp);
656 return handler;
658 default:
659 g_warning("Bad type");
660 return NULL;
664 /* (radios can be NULL if called from clear_run_action) */
665 static void run_action_update(Radios *radios, 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(NULL, 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 || EXECUTABLE_FILE(item))
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 * Only call for non-executable files.
782 * g_free() the result.
784 gchar *describe_current_command(MIME_type *type)
786 char *handler;
787 char *desc = NULL;
788 struct stat info;
790 g_return_val_if_fail(type != NULL, NULL);
792 handler = handler_for(type);
794 if (!handler)
795 return g_strdup(_("No run action defined"));
797 if (mc_stat(handler, &info) !=0 )
799 desc = g_strdup_printf(_("Error in handler %s: %s"), handler,
800 g_strerror(errno));
801 goto out;
804 if (S_ISDIR(info.st_mode))
806 const guchar *tmp;
807 uid_t dir_uid = info.st_uid;
809 tmp = make_path(handler, "AppRun");
811 if (mc_lstat(tmp, &info) != 0 || info.st_uid != dir_uid
812 || !(info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
813 desc = g_strdup_printf(
814 _("Invalid application %s (bad AppRun)"),
815 handler);
816 /* Else, just report handler... */
818 goto out;
821 /* It's not an application directory, and it's not a symlink... */
823 if (access(handler, X_OK) != 0)
825 desc = g_strdup_printf(_("Non-executable %s"), handler);
826 goto out;
829 desc = get_current_command(type);
830 out:
831 if (!desc)
832 desc = handler;
833 else
834 g_free(handler);
836 return desc;
839 /* Display a dialog box allowing the user to set the default run action
840 * for this type.
842 void type_set_handler_dialog(MIME_type *type)
844 guchar *tmp;
845 GtkDialog *dialog;
846 GtkWidget *frame, *entry, *label, *button;
847 GtkWidget *hbox;
848 Radios *radios;
850 g_return_if_fail(type != NULL);
852 dialog = GTK_DIALOG(gtk_dialog_new());
853 gtk_dialog_set_has_separator(dialog, FALSE);
854 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
856 g_object_set_data(G_OBJECT(dialog), "mime_type", type);
858 gtk_window_set_title(GTK_WINDOW(dialog), _("Set run action"));
860 radios = radios_new(run_action_update, dialog);
861 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
863 radios_add(radios,
864 _("If a handler for the specific type isn't set up, "
865 "use this as the default."), SET_MEDIA,
866 _("Set default for all `%s/<anything>'"),
867 type->media_type);
869 radios_add(radios,
870 _("Use this application for all files with this MIME "
871 "type."), SET_TYPE,
872 _("Only for the type `%s' (%s/%s)"),
873 mime_type_comment(type),
874 type->media_type, type->subtype);
876 radios_set_value(radios, SET_TYPE);
878 frame = drop_box_new(_("Drop a suitable application here"));
880 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
882 radios_pack(radios, GTK_BOX(dialog->vbox));
883 gtk_box_pack_start(GTK_BOX(dialog->vbox), frame, TRUE, TRUE, 0);
885 g_signal_connect(frame, "path_dropped",
886 G_CALLBACK(drag_app_dropped), dialog);
887 g_signal_connect(frame, "clear",
888 G_CALLBACK(clear_run_action), dialog);
890 hbox = gtk_hbox_new(FALSE, 4);
891 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 4);
892 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
893 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("OR")),
894 FALSE, TRUE, 0);
895 gtk_box_pack_start(GTK_BOX(hbox), gtk_hseparator_new(), TRUE, TRUE, 0);
897 hbox = gtk_hbox_new(FALSE, 4);
898 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
900 label = gtk_label_new(_("Enter a shell command:")),
901 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
902 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
904 gtk_box_pack_start(GTK_BOX(hbox),
905 new_help_button(show_shell_help, NULL), FALSE, TRUE, 0);
907 entry = gtk_entry_new();
908 gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, FALSE, TRUE, 0);
909 gtk_widget_grab_focus(entry);
910 g_object_set_data(G_OBJECT(dialog), "shell_command", entry);
911 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
913 /* If possible, fill in the entry box with the current command */
914 tmp = get_current_command(type);
915 if (tmp)
917 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
918 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
919 g_free(tmp);
921 else
923 gtk_entry_set_text(GTK_ENTRY(entry), " \"$@\"");
924 gtk_editable_set_position(GTK_EDITABLE(entry), 0);
927 gtk_dialog_add_button(dialog, GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL);
929 button = button_new_mixed(GTK_STOCK_OK, _("_Use Command"));
930 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
931 gtk_dialog_add_action_widget(dialog, button, GTK_RESPONSE_OK);
933 hbox = gtk_hbox_new(TRUE, 4);
934 gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, FALSE, TRUE, 0);
936 gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
938 g_signal_connect(dialog, "response",
939 G_CALLBACK(set_action_response), NULL);
941 gtk_widget_show_all(GTK_WIDGET(dialog));
944 /* path is an entry in Choices. If it's a symlink or a very small executable
945 * then just get rid of it, otherwise confirm first. It it doesn't exist,
946 * do nothing.
948 * FALSE on error (abort operation).
950 static gboolean remove_handler_with_confirm(const guchar *path)
952 struct stat info;
954 if (lstat(path, &info) == 0)
956 /* A binding already exists... */
957 if (S_ISREG(info.st_mode) && info.st_size > 256)
959 if (!confirm(_("A run action already exists and is "
960 "quite a big program - are you sure "
961 "you want to delete it?"),
962 GTK_STOCK_DELETE, NULL))
964 return FALSE;
968 if (unlink(path))
970 report_error(_("Can't remove %s: %s"),
971 path, g_strerror(errno));
972 return FALSE;
976 return TRUE;
979 /* The user wants to set a new default action for files of this type (or just
980 * clear the action). Removes the current binding if possible and returns the
981 * path to save the new one to. NULL means cancel. g_free() the result.
983 static char *get_action_save_path(GtkWidget *dialog)
985 guchar *path = NULL;
986 guchar *type_name = NULL;
987 MIME_type *type;
988 Radios *radios;
990 g_return_val_if_fail(dialog != NULL, NULL);
992 type = g_object_get_data(G_OBJECT(dialog), "mime_type");
993 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
995 g_return_val_if_fail(radios != NULL && type != NULL, NULL);
997 if (radios_get_value(radios) == SET_MEDIA)
998 type_name = g_strdup(type->media_type);
999 else
1000 type_name = g_strconcat(type->media_type, "_",
1001 type->subtype, NULL);
1003 path = choices_find_xdg_path_save("", PROJECT, SITE, FALSE);
1004 if (!path)
1006 report_error(
1007 _("Choices saving is disabled by CHOICESPATH variable"));
1008 goto out;
1010 g_free(path);
1012 path = choices_find_xdg_path_save(type_name, "MIME-types", SITE, TRUE);
1014 if (!remove_handler_with_confirm(path))
1015 null_g_free(&path);
1016 out:
1017 g_free(type_name);
1018 return path;
1021 MIME_type *mime_type_from_base_type(int base_type)
1023 switch (base_type)
1025 case TYPE_FILE:
1026 return text_plain;
1027 case TYPE_DIRECTORY:
1028 return inode_directory;
1029 case TYPE_PIPE:
1030 return inode_pipe;
1031 case TYPE_SOCKET:
1032 return inode_socket;
1033 case TYPE_BLOCK_DEVICE:
1034 return inode_block_dev;
1035 case TYPE_CHAR_DEVICE:
1036 return inode_char_dev;
1037 case TYPE_DOOR:
1038 return inode_door;
1040 return inode_unknown;
1043 /* Takes the st_mode field from stat() and returns the base type.
1044 * Should not be a symlink.
1046 int mode_to_base_type(int st_mode)
1048 if (S_ISREG(st_mode))
1049 return TYPE_FILE;
1050 else if (S_ISDIR(st_mode))
1051 return TYPE_DIRECTORY;
1052 else if (S_ISBLK(st_mode))
1053 return TYPE_BLOCK_DEVICE;
1054 else if (S_ISCHR(st_mode))
1055 return TYPE_CHAR_DEVICE;
1056 else if (S_ISFIFO(st_mode))
1057 return TYPE_PIPE;
1058 else if (S_ISSOCK(st_mode))
1059 return TYPE_SOCKET;
1060 else if (S_ISDOOR(st_mode))
1061 return TYPE_DOOR;
1063 return TYPE_ERROR;
1066 /* Returns TRUE is this is something that is run by looking up its type
1067 * in MIME-types and, hence, can have its run action set.
1069 gboolean can_set_run_action(DirItem *item)
1071 g_return_val_if_fail(item != NULL, FALSE);
1073 return item->base_type == TYPE_FILE && !EXECUTABLE_FILE(item);
1076 /* Parse file type colours and allocate/free them as necessary */
1077 static void alloc_type_colours(void)
1079 gboolean success[NUM_TYPE_COLOURS];
1080 int change_count = 0; /* No. needing realloc */
1081 int i;
1082 static gboolean allocated = FALSE;
1084 /* Parse colours */
1085 for (i = 0; i < NUM_TYPE_COLOURS; i++)
1087 GdkColor *c = &type_colours[i];
1088 gushort r = c->red;
1089 gushort g = c->green;
1090 gushort b = c->blue;
1092 gdk_color_parse(o_type_colours[i].value, &type_colours[i]);
1094 if (allocated && (c->red != r || c->green != g || c->blue != b))
1095 change_count++;
1098 /* Free colours if they were previously allocated and
1099 * have changed or become unneeded.
1101 if (allocated && (change_count || !o_display_colour_types.int_value))
1103 gdk_colormap_free_colors(gdk_rgb_get_colormap(),
1104 type_colours, NUM_TYPE_COLOURS);
1105 allocated = FALSE;
1108 /* Allocate colours, unless they are still allocated (=> they didn't
1109 * change) or we don't want them anymore.
1110 * XXX: what should be done if allocation fails?
1112 if (!allocated && o_display_colour_types.int_value)
1114 gdk_colormap_alloc_colors(gdk_rgb_get_colormap(),
1115 type_colours, NUM_TYPE_COLOURS,
1116 FALSE, TRUE, success);
1117 allocated = TRUE;
1121 static void expire_timer(gpointer key, gpointer value, gpointer data)
1123 MIME_type *type = value;
1125 type->image_time = 0;
1128 static void options_changed(void)
1130 alloc_type_colours();
1131 if (o_icon_theme.has_changed)
1133 set_icon_theme();
1134 g_hash_table_foreach(type_hash, expire_timer, NULL);
1135 full_refresh();
1139 /* Return a pointer to a (static) colour for this item. If colouring is
1140 * off, returns normal.
1142 GdkColor *type_get_colour(DirItem *item, GdkColor *normal)
1144 int type = item->base_type;
1146 if (!o_display_colour_types.int_value)
1147 return normal;
1149 if (EXECUTABLE_FILE(item))
1150 type = TYPE_EXEC;
1151 else if (item->flags & ITEM_FLAG_APPDIR)
1152 type = TYPE_APPDIR;
1154 g_return_val_if_fail(type >= 0 && type < NUM_TYPE_COLOURS, normal);
1156 return &type_colours[type];
1159 static char **get_xdg_data_dirs(int *n_dirs)
1161 const char *env;
1162 char **dirs;
1163 int i, n;
1165 env = getenv("XDG_DATA_DIRS");
1166 if (!env)
1167 env = "/usr/local/share/:/usr/share/";
1168 dirs = g_strsplit(env, ":", 0);
1169 g_return_val_if_fail(dirs != NULL, NULL);
1170 for (n = 0; dirs[n]; n++)
1172 for (i = n; i > 0; i--)
1173 dirs[i] = dirs[i - 1];
1174 env = getenv("XDG_DATA_HOME");
1175 if (env)
1176 dirs[0] = g_strdup(env);
1177 else
1178 dirs[0] = g_build_filename(g_get_home_dir(), ".local",
1179 "share", NULL);
1180 *n_dirs = n + 1;
1181 return dirs;
1184 /* Try to fill in 'type->comment' from this document */
1185 static void get_comment(MIME_type *type, const guchar *path)
1187 xmlNode *node;
1188 XMLwrapper *doc;
1190 doc = xml_cache_load(path);
1191 if (!doc)
1192 return;
1194 node = xml_get_section(doc, TYPE_NS, "comment");
1196 if (node)
1198 char *val;
1199 g_return_if_fail(type->comment == NULL);
1200 val= xmlNodeListGetString(node->doc, node->xmlChildrenNode, 1);
1201 type->comment = g_strdup(val);
1202 xmlFree(val);
1205 g_object_unref(doc);
1208 /* Fill in the comment field for this MIME type */
1209 static void find_comment(MIME_type *type)
1211 char **dirs;
1212 int i, n_dirs = 0;
1214 if (type->comment)
1216 g_free(type->comment);
1217 type->comment = NULL;
1220 dirs = get_xdg_data_dirs(&n_dirs);
1221 g_return_if_fail(dirs != NULL);
1223 for (i = 0; i < n_dirs; i++)
1225 guchar *path;
1227 path = g_strdup_printf("%s/mime/%s/%s.xml", dirs[i],
1228 type->media_type, type->subtype);
1229 get_comment(type, path);
1230 g_free(path);
1231 if (type->comment)
1232 break;
1235 if (!type->comment)
1236 type->comment = g_strdup_printf("%s/%s", type->media_type,
1237 type->subtype);
1239 for (i = 0; i < n_dirs; i++)
1240 g_free(dirs[i]);
1241 g_free(dirs);
1244 const char *mime_type_comment(MIME_type *type)
1246 if (!type->comment)
1247 find_comment(type);
1249 return type->comment;
1252 static void unref_icon_theme(void)
1254 if (icon_theme && icon_theme != rox_theme && icon_theme != gnome_theme)
1255 g_object_unref(icon_theme);
1258 static void set_icon_theme(void)
1260 struct stat info;
1261 char *icon_home;
1262 const char *theme_dir;
1263 const char *theme_name = o_icon_theme.value;
1265 if (!theme_name || !*theme_name)
1266 theme_name = "ROX";
1268 if (!strcmp(theme_name, "ROX"))
1270 unref_icon_theme();
1271 init_rox_theme();
1272 icon_theme = rox_theme;
1274 else if (!strcmp(theme_name, "gnome"))
1276 unref_icon_theme();
1277 init_gnome_theme();
1278 icon_theme = gnome_theme;
1280 else
1282 if (icon_theme == rox_theme || icon_theme == gnome_theme)
1283 icon_theme = gtk_icon_theme_new();
1284 gtk_icon_theme_set_custom_theme(icon_theme, theme_name);
1287 /* Ensure the ROX theme exists. */
1289 icon_home = g_build_filename(home_dir, ".icons", "ROX", NULL);
1290 if (stat(icon_home, &info) == 0)
1291 return; /* Already exists */
1293 /* First, create the .icons directory */
1294 theme_dir = make_path(home_dir, ".icons");
1295 if (!file_exists(theme_dir))
1296 mkdir(theme_dir, 0755);
1298 if (lstat(icon_home, &info) == 0)
1300 /* Probably a broken symlink, then. Remove it. */
1301 if (unlink(icon_home))
1302 g_warning("Error removing broken symlink %s: %s", icon_home, g_strerror(errno));
1303 else
1304 g_warning("Removed broken symlink %s", icon_home);
1307 if (symlink(make_path(app_dir, "ROX"), icon_home))
1309 delayed_error(_("Failed to create symlink '%s':\n%s"), icon_home, g_strerror(errno));
1310 open_to_show(icon_home);
1312 g_free(icon_home);
1314 gtk_icon_theme_rescan_if_needed(icon_theme);
1317 static guchar *read_theme(Option *option)
1319 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1320 GtkLabel *item;
1322 item = GTK_LABEL(GTK_BIN(om)->child);
1324 g_return_val_if_fail(item != NULL, g_strdup("ROX"));
1326 return g_strdup(gtk_label_get_text(item));
1329 static void update_theme(Option *option)
1331 GtkOptionMenu *om = GTK_OPTION_MENU(option->widget);
1332 GtkWidget *menu;
1333 GList *kids, *next;
1334 int i = 0;
1336 menu = gtk_option_menu_get_menu(om);
1338 kids = gtk_container_get_children(GTK_CONTAINER(menu));
1339 for (next = kids; next; next = next->next, i++)
1341 GtkLabel *item = GTK_LABEL(GTK_BIN(next->data)->child);
1342 const gchar *label;
1344 /* The label actually moves from the menu!! */
1345 if (!item)
1346 item = GTK_LABEL(GTK_BIN(om)->child);
1348 label = gtk_label_get_text(item);
1350 g_return_if_fail(label != NULL);
1352 if (strcmp(label, option->value) == 0)
1353 break;
1355 g_list_free(kids);
1357 if (next)
1358 gtk_option_menu_set_history(om, i);
1359 else
1360 g_warning("Theme '%s' not found", option->value);
1363 static void add_themes_from_dir(GPtrArray *names, const char *dir)
1365 GPtrArray *list;
1366 int i;
1368 if (access(dir, F_OK) != 0)
1369 return;
1371 list = list_dir(dir);
1372 g_return_if_fail(list != NULL);
1374 for (i = 0; i < list->len; i++)
1376 char *index_path;
1378 index_path = g_build_filename(dir, list->pdata[i],
1379 "index.theme", NULL);
1381 if (access(index_path, F_OK) == 0)
1382 g_ptr_array_add(names, list->pdata[i]);
1383 else
1384 g_free(list->pdata[i]);
1386 g_free(index_path);
1389 g_ptr_array_free(list, TRUE);
1392 static GList *build_icon_theme(Option *option, xmlNode *node, guchar *label)
1394 GtkWidget *button, *menu, *hbox;
1395 GPtrArray *names;
1396 gchar **theme_dirs = NULL;
1397 gint n_dirs = 0;
1398 int i;
1400 g_return_val_if_fail(option != NULL, NULL);
1401 g_return_val_if_fail(label != NULL, NULL);
1403 hbox = gtk_hbox_new(FALSE, 4);
1405 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_(label)),
1406 FALSE, TRUE, 0);
1408 button = gtk_option_menu_new();
1409 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
1411 menu = gtk_menu_new();
1412 gtk_option_menu_set_menu(GTK_OPTION_MENU(button), menu);
1414 gtk_icon_theme_get_search_path(icon_theme, &theme_dirs, &n_dirs);
1415 names = g_ptr_array_new();
1416 for (i = 0; i < n_dirs; i++)
1417 add_themes_from_dir(names, theme_dirs[i]);
1418 g_strfreev(theme_dirs);
1420 g_ptr_array_sort(names, strcmp2);
1422 for (i = 0; i < names->len; i++)
1424 GtkWidget *item;
1425 char *name = names->pdata[i];
1427 item = gtk_menu_item_new_with_label(name);
1428 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1429 gtk_widget_show_all(item);
1431 g_free(name);
1434 g_ptr_array_free(names, TRUE);
1436 option->update_widget = update_theme;
1437 option->read_widget = read_theme;
1438 option->widget = button;
1440 gtk_signal_connect_object(GTK_OBJECT(button), "changed",
1441 GTK_SIGNAL_FUNC(option_check_widget),
1442 (GtkObject *) option);
1444 return g_list_append(NULL, hbox);
1447 GtkIconInfo *theme_lookup_icon(const gchar *icon_name, gint size,
1448 GtkIconLookupFlags flags)
1450 GtkIconInfo *result = gtk_icon_theme_lookup_icon(icon_theme,
1451 icon_name, size, flags);
1453 if (!result && icon_theme != rox_theme)
1455 init_rox_theme();
1456 result = gtk_icon_theme_lookup_icon(rox_theme,
1457 icon_name, size, flags);
1459 if (!result && icon_theme != gnome_theme)
1461 init_gnome_theme();
1462 result = gtk_icon_theme_lookup_icon(gnome_theme,
1463 icon_name, size, flags);
1465 return result;
1468 GdkPixbuf *theme_load_icon(const gchar *icon_name, gint size,
1469 GtkIconLookupFlags flags, GError **perror)
1471 GdkPixbuf *result = gtk_icon_theme_load_icon(icon_theme,
1472 icon_name, size, flags, NULL);
1474 if (!result && icon_theme != gnome_theme)
1476 init_gnome_theme();
1477 result = gtk_icon_theme_load_icon(gnome_theme,
1478 icon_name, size, flags, NULL);
1480 if (!result && icon_theme != rox_theme)
1482 init_rox_theme();
1483 result = gtk_icon_theme_load_icon(rox_theme,
1484 icon_name, size, flags, perror);
1486 return result;