/* -*- mode: c; style: linux -*- */ /* mime-type-info.c * * Copyright (C) 2000 Eazel, Inc. * Copyright (C) 2002 Ximian, Inc. * * Written by Bradford Hovinen , * Jonathan Blandford , * Gene Z. Ragan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "libuuid/uuid.h" #include "mime-type-info.h" #include "mime-types-model.h" static const gchar *get_category_name (const gchar *mime_type); static const gchar *get_category_description (const gchar *mime_type); static GSList *get_lang_list (void); static gchar *form_extensions_string (const MimeTypeInfo *info, gchar *sep, gchar *prepend); static void get_icon_pixbuf (MimeTypeInfo *info, const gchar *icon_path, gboolean want_large); static MimeCategoryInfo *get_category (const gchar *category_name, const gchar *category_desc, GtkTreeModel *model); gchar *get_real_icon_path (const MimeTypeInfo *info, const gchar *icon_name); void load_all_mime_types (GtkTreeModel *model) { GList *list, *tmp; list = gnome_vfs_get_registered_mime_types (); for (tmp = list; tmp != NULL; tmp = tmp->next) mime_type_info_new (tmp->data, model); g_list_free (list); } MimeTypeInfo * mime_type_info_new (const gchar *mime_type, GtkTreeModel *model) { MimeTypeInfo *info; info = g_new0 (MimeTypeInfo, 1); MODEL_ENTRY (info)->type = MODEL_ENTRY_MIME_TYPE; if (mime_type != NULL) { info->mime_type = g_strdup (mime_type); mime_type_info_set_category_name (info, get_category_name (mime_type), get_category_description (mime_type), model); } else info->entry.parent = get_model_entries (model); return info; } /* Fill in the remaining fields in a MimeTypeInfo structure; suitable for * subsequent use in an edit dialog */ void mime_type_info_load_all (MimeTypeInfo *info) { mime_type_info_get_description (info); mime_type_info_get_file_extensions (info); if (info->icon_name == NULL) info->icon_name = g_strdup (gnome_vfs_mime_get_icon (info->mime_type)); if (info->default_action == NULL && info->mime_type != NULL) { /* DO NOT USE gnome_vfs_mime_get_default_application * it will silently remove non-existant applications * which will make them seem to disappear on systems that * are configured differently */ char const *app_id = gnome_vfs_mime_get_value ( info->mime_type, "default_application_id"); if (app_id != NULL && app_id[0] != '\0') info->default_action = gnome_vfs_application_registry_get_mime_application (app_id); } if (info->default_action == NULL) info->default_action = g_new0 (GnomeVFSMimeApplication, 1); if (info->default_component == NULL) info->default_component = gnome_vfs_mime_get_default_component (info->mime_type); mime_type_info_get_use_category (info); } const gchar * mime_type_info_get_description (MimeTypeInfo *info) { if (info->description == NULL) info->description = g_strdup (gnome_vfs_mime_get_description (info->mime_type)); return info->description; } GdkPixbuf * mime_type_info_get_icon (MimeTypeInfo *info) { if (info->small_icon_pixbuf == NULL) get_icon_pixbuf (info, mime_type_info_get_icon_path (info), FALSE); if (info->small_icon_pixbuf != NULL) g_object_ref (G_OBJECT (info->small_icon_pixbuf)); return info->small_icon_pixbuf; } const gchar * mime_type_info_get_icon_path (MimeTypeInfo *info) { if (info->icon_name == NULL) info->icon_name = g_strdup (gnome_vfs_mime_get_icon (info->mime_type)); info->icon_path = get_real_icon_path (info, info->icon_name); return info->icon_path; } const GList * mime_type_info_get_file_extensions (MimeTypeInfo *info) { if (info->file_extensions == NULL) info->file_extensions = gnome_vfs_mime_get_extensions_list (info->mime_type); return info->file_extensions; } gboolean mime_type_info_get_use_category (MimeTypeInfo *info) { const gchar *tmp1; if (!info->use_cat_loaded) { tmp1 = gnome_vfs_mime_get_value (info->mime_type, "use_category_default"); if (tmp1 != NULL && !strcmp (tmp1, "yes")) info->use_category = TRUE; else info->use_category = FALSE; info->use_cat_loaded = TRUE; } return info->use_category; } gchar * mime_type_info_get_file_extensions_pretty_string (MimeTypeInfo *info) { mime_type_info_get_file_extensions (info); return form_extensions_string (info, ", ", "."); } gchar * mime_type_info_get_category_name (const MimeTypeInfo *info) { return mime_category_info_get_full_description (MIME_CATEGORY_INFO (info->entry.parent)); } void mime_type_info_set_category_name (const MimeTypeInfo *info, const gchar *category_name, const gchar *category_desc, GtkTreeModel *model) { if (MODEL_ENTRY (info)->parent != NULL) model_entry_remove_child (MODEL_ENTRY (info)->parent, MODEL_ENTRY (info), model); if (category_name != NULL) { MODEL_ENTRY (info)->parent = MODEL_ENTRY (get_category (category_name, category_desc, model)); if (MODEL_ENTRY (info)->parent != NULL) model_entry_insert_child (MODEL_ENTRY (info)->parent, MODEL_ENTRY (info), model); } else { MODEL_ENTRY (info)->parent = NULL; } } void mime_type_info_set_file_extensions (MimeTypeInfo *info, GList *list) { gnome_vfs_mime_extensions_list_free (info->file_extensions); info->file_extensions = list; } void mime_type_info_save (MimeTypeInfo *info) { gchar *tmp; gnome_vfs_mime_freeze (); gnome_vfs_mime_set_description (info->mime_type, info->description); gnome_vfs_mime_set_icon (info->mime_type, info->icon_name); /* Be really anal about validating this action */ if (info->default_action != NULL) { if ( info->default_action->command == NULL || *info->default_action->command == '\0' || info->default_action->id == NULL || *info->default_action->id == '\0') { g_warning ("Invalid application"); gnome_vfs_mime_application_free (info->default_action); info->default_action = NULL; } } if (info->default_action != NULL) { gnome_vfs_mime_set_default_application (info->mime_type, info->default_action->id); gnome_vfs_mime_add_application_to_short_list (info->mime_type, info->default_action->id); } else gnome_vfs_mime_set_default_application (info->mime_type, NULL); tmp = form_extensions_string (info, " ", NULL); gnome_vfs_mime_set_extensions_list (info->mime_type, tmp); g_free (tmp); if (info->default_component != NULL) gnome_vfs_mime_set_default_component (info->mime_type, info->default_component->iid); else gnome_vfs_mime_set_default_component (info->mime_type, NULL); tmp = mime_type_info_get_category_name (info); gnome_vfs_mime_set_value (info->mime_type, "category", tmp); g_free (tmp); gnome_vfs_mime_set_value (info->mime_type, "use_category_default", info->use_category ? "yes" : "no"); gnome_vfs_application_registry_sync (); gnome_vfs_mime_thaw (); } void mime_type_info_free (MimeTypeInfo *info) { g_free (info->mime_type); g_free (info->description); g_free (info->icon_name); g_free (info->icon_path); gnome_vfs_mime_extensions_list_free (info->file_extensions); CORBA_free (info->default_component); gnome_vfs_mime_application_free (info->default_action); if (info->icon_pixbuf != NULL) g_object_unref (G_OBJECT (info->icon_pixbuf)); if (info->small_icon_pixbuf != NULL) g_object_unref (G_OBJECT (info->small_icon_pixbuf)); g_free (info); } MimeCategoryInfo * mime_category_info_new (MimeCategoryInfo *parent, const gchar *name, const gchar *description, GtkTreeModel *model) { MimeCategoryInfo *info; info = g_new0 (MimeCategoryInfo, 1); MODEL_ENTRY (info)->type = MODEL_ENTRY_CATEGORY; info->name = g_strdup (name); info->description = g_strdup (description); if (parent != NULL) MODEL_ENTRY (info)->parent = MODEL_ENTRY (parent); else MODEL_ENTRY (info)->parent = get_model_entries (model); return info; } static gchar * get_gconf_base_name (MimeCategoryInfo *category) { gchar *tmp, *tmp1; tmp1 = mime_category_info_get_full_name (category); for (tmp = tmp1; *tmp != '\0'; tmp++) if (g_ascii_isspace (*tmp) || *tmp == '(' || *tmp == ')') *tmp = '-'; tmp = g_strconcat ("/desktop/gnome/file-types-categories/", tmp1, NULL); g_free (tmp1); return tmp; } void mime_category_info_load_all (MimeCategoryInfo *category) { gchar *tmp, *tmp1; gchar *appid; tmp1 = get_gconf_base_name (category); if (category->default_action == NULL) { tmp = g_strconcat (tmp1, "/default-action-id", NULL); appid = gconf_client_get_string (gconf_client_get_default (), tmp, NULL); g_free (tmp); if (appid != NULL && *appid != '\0') category->default_action = gnome_vfs_application_registry_get_mime_application (appid); /* This must be non NULL, so be extra careful incase gnome-vfs * spits back a NULL */ if (category->default_action == NULL) category->default_action = g_new0 (GnomeVFSMimeApplication, 1); } if (!category->use_parent_cat_loaded) { if (category->entry.parent->type == MODEL_ENTRY_CATEGORY) { tmp = g_strconcat (tmp1, "/use-parent-category", NULL); category->use_parent_category = gconf_client_get_bool (gconf_client_get_default (), tmp, NULL); g_free (tmp); } else { category->use_parent_category = FALSE; } category->use_parent_cat_loaded = TRUE; } g_free (tmp1); } static void set_subcategory_ids (ModelEntry *entry, MimeCategoryInfo *category, gchar *app_id) { ModelEntry *tmp; switch (entry->type) { case MODEL_ENTRY_MIME_TYPE: if (MIME_TYPE_INFO (entry)->use_category) gnome_vfs_mime_set_default_application (MIME_TYPE_INFO (entry)->mime_type, app_id); break; case MODEL_ENTRY_CATEGORY: if (entry != MODEL_ENTRY (category) && MIME_CATEGORY_INFO (entry)->use_parent_category) for (tmp = entry->first_child; tmp != NULL; tmp = tmp->next) set_subcategory_ids (tmp, category, app_id); break; default: break; } } void mime_category_info_save (MimeCategoryInfo *category) { gchar *key, *basename; gboolean set_ids; g_warning ("Do not call this, nothing actually observes the gconf settings"); return; /* Be really anal about validating this action */ if (category->default_action != NULL) { if ( category->default_action->command == NULL || *category->default_action->command == '\0' || category->default_action->id == NULL || *category->default_action->id == '\0') { g_warning ("Invalid application"); gnome_vfs_mime_application_free (category->default_action); category->default_action = NULL; } } basename = get_gconf_base_name (category); key = g_strconcat (basename, "/default-action-id", NULL); if ((set_ids = (category->default_action != NULL))) gconf_client_set_string (gconf_client_get_default (), key, category->default_action->id, NULL); else gconf_client_unset (gconf_client_get_default (), key, NULL); g_free (key); key = g_strconcat (basename, "/use-parent-category", NULL); gconf_client_set_bool (gconf_client_get_default (), key, category->use_parent_category, NULL); g_free (key); g_free (basename); if (set_ids) set_subcategory_ids (MODEL_ENTRY (category), category, category->default_action->id); } static GList * find_possible_supported_apps (ModelEntry *entry, gboolean top) { GList *ret; ModelEntry *tmp; if (entry == NULL) return NULL; switch (entry->type) { case MODEL_ENTRY_CATEGORY: if (!top && !MIME_CATEGORY_INFO (entry)->use_parent_category) return NULL; for (tmp = entry->first_child; tmp != NULL; tmp = tmp->next) { ret = find_possible_supported_apps (tmp, FALSE); if (ret != NULL) return ret; } return NULL; case MODEL_ENTRY_MIME_TYPE: if (mime_type_info_get_use_category (MIME_TYPE_INFO (entry))) return gnome_vfs_application_registry_get_applications (MIME_TYPE_INFO (entry)->mime_type); else return NULL; default: return NULL; } } static GList * intersect_lists (GList *list, GList *list1) { GList *tmp, *tmp1, *tmpnext; tmp = list; while (tmp != NULL) { tmpnext = tmp->next; for (tmp1 = list1; tmp1 != NULL; tmp1 = tmp1->next) if (!strcmp (tmp->data, tmp1->data)) break; if (tmp1 == NULL) list = g_list_remove_link (list, tmp); tmp = tmpnext; } return list; } static GList * reduce_supported_app_list (ModelEntry *entry, GList *list, gboolean top) { GList *type_list; ModelEntry *tmp; switch (entry->type) { case MODEL_ENTRY_CATEGORY: if (!top && !MIME_CATEGORY_INFO (entry)->use_parent_category) break; for (tmp = entry->first_child; tmp != NULL; tmp = tmp->next) list = reduce_supported_app_list (tmp, list, FALSE); break; case MODEL_ENTRY_MIME_TYPE: if (mime_type_info_get_use_category (MIME_TYPE_INFO (entry))) { type_list = gnome_vfs_application_registry_get_applications (MIME_TYPE_INFO (entry)->mime_type); list = intersect_lists (list, type_list); g_list_free (type_list); } break; default: break; } return list; } GList * mime_category_info_find_apps (MimeCategoryInfo *info) { return reduce_supported_app_list (MODEL_ENTRY (info), find_possible_supported_apps (MODEL_ENTRY (info), TRUE), TRUE); } gchar * mime_category_info_get_full_name (MimeCategoryInfo *info) { GString *string; ModelEntry *tmp; gchar *ret, *s; string = g_string_new (""); for (tmp = MODEL_ENTRY (info); tmp != NULL && tmp->type != MODEL_ENTRY_NONE; tmp = tmp->parent) { g_string_prepend (string, MIME_CATEGORY_INFO (tmp)->name); g_string_prepend (string, "/"); } /* work around gcc 2.96 bug */ s = (*string->str == '\0') ? string->str : string->str + 1; ret = g_strdup (s); g_string_free (string, TRUE); return ret; } gchar * mime_category_info_get_full_description (MimeCategoryInfo *info) { GString *string; ModelEntry *tmp; gchar *ret, *s; string = g_string_new (""); for (tmp = MODEL_ENTRY (info); tmp != NULL && tmp->type != MODEL_ENTRY_NONE; tmp = tmp->parent) { g_string_prepend (string, MIME_CATEGORY_INFO (tmp)->description); g_string_prepend (string, "/"); } /* work around gcc 2.96 bug */ s = (*string->str == '\0') ? string->str : string->str + 1; ret = g_strdup (s); g_string_free (string, TRUE); return ret; } char * mime_type_get_pretty_name_for_server (Bonobo_ServerInfo *server) { const char *view_as_name; char *display_name; GSList *langs; display_name = NULL; langs = get_lang_list (); view_as_name = bonobo_server_info_prop_lookup (server, "nautilus:view_as_name", langs); if (view_as_name == NULL) view_as_name = bonobo_server_info_prop_lookup (server, "name", langs); if (view_as_name == NULL) view_as_name = server->iid; g_slist_foreach (langs, (GFunc) g_free, NULL); g_slist_free (langs); /* if the name is an OAFIID, clean it up for display */ if (!strncmp (view_as_name, "OAFIID:", strlen ("OAFIID:"))) { char *display_name, *colon_ptr; display_name = g_strdup (view_as_name + strlen ("OAFIID:")); colon_ptr = strchr (display_name, ':'); if (colon_ptr) *colon_ptr = '\0'; return display_name; } return g_strdup_printf ("View as %s", view_as_name); } static MimeTypeInfo * get_mime_type_info_int (ModelEntry *entry, const gchar *mime_type) { ModelEntry *tmp; MimeTypeInfo *ret; switch (entry->type) { case MODEL_ENTRY_MIME_TYPE: if (!strcmp (MIME_TYPE_INFO (entry)->mime_type, mime_type)) return MIME_TYPE_INFO (entry); return NULL; case MODEL_ENTRY_CATEGORY: case MODEL_ENTRY_NONE: for (tmp = entry->first_child; tmp != NULL; tmp = tmp->next) if ((ret = get_mime_type_info_int (tmp, mime_type)) != NULL) return ret; return NULL; default: return NULL; } } MimeTypeInfo * get_mime_type_info (const gchar *mime_type) { return get_mime_type_info_int (get_model_entries (NULL), mime_type); } static const gchar * get_category_name (const gchar *mime_type) { const gchar *path; path = gnome_vfs_mime_get_value (mime_type, "category"); if (path != NULL) return g_strdup (path); else if (!strncmp (mime_type, "image/", strlen ("image/"))) return "Images"; else if (!strncmp (mime_type, "video/", strlen ("video/"))) return "Video"; else if (!strncmp (mime_type, "audio/", strlen ("audio/"))) return "Audio"; else return "Misc"; } static const gchar * get_category_description (const gchar *mime_type) { const gchar *path; path = gnome_vfs_mime_get_value (mime_type, "category"); if (path != NULL) return g_strdup (path); else if (!strncmp (mime_type, "image/", strlen ("image/"))) return _("Images"); else if (!strncmp (mime_type, "video/", strlen ("video/"))) return _("Video"); else if (!strncmp (mime_type, "audio/", strlen ("audio/"))) return _("Audio"); else return _("Misc"); } static GSList * get_lang_list (void) { GSList *retval; const char *lang; char *equal_char; retval = NULL; lang = g_getenv ("LANGUAGE"); if (lang == NULL) lang = g_getenv ("LANG"); if (lang != NULL) { equal_char = strchr (lang, '='); if (equal_char != NULL) lang = equal_char + 1; retval = g_slist_prepend (retval, g_strdup (lang)); } return retval; } static gchar * form_extensions_string (const MimeTypeInfo *info, gchar *sep, gchar *prepend) { gchar *tmp; gchar **array; GList *l; gint i = 0; if (prepend == NULL) prepend = ""; array = g_new0 (gchar *, g_list_length (info->file_extensions) + 1); for (l = info->file_extensions; l != NULL; l = l->next) array[i++] = g_strconcat (prepend, l->data, NULL); tmp = g_strjoinv (sep, array); g_strfreev (array); return tmp; } gchar *get_real_icon_path (const MimeTypeInfo *info, const gchar *icon_name) { gchar *tmp, *tmp1, *ret, *real_icon_name; if (icon_name == NULL || *icon_name == '\0') { tmp = g_strdup (info->mime_type); tmp1 = strchr (tmp, '/'); if (tmp1 != NULL) *tmp1 = '-'; real_icon_name = g_strconcat ("document-icons/gnome-", tmp, ".png", NULL); g_free (tmp); } else { real_icon_name = g_strdup (icon_name); } ret = gnome_vfs_icon_path_from_filename (real_icon_name); if (ret == NULL && strstr (real_icon_name, ".png") == NULL) { tmp = g_strconcat (real_icon_name, ".png", NULL); ret = gnome_vfs_icon_path_from_filename (tmp); g_free (tmp); } if (ret == NULL) { tmp = g_strconcat ("nautilus/", real_icon_name, NULL); ret = gnome_vfs_icon_path_from_filename (tmp); g_free (tmp); } if (ret == NULL && strstr (real_icon_name, ".png") == NULL) { tmp = g_strconcat ("nautilus/", real_icon_name, ".png", NULL); ret = gnome_vfs_icon_path_from_filename (tmp); g_free (tmp); } if (ret == NULL) ret = gnome_vfs_icon_path_from_filename ("nautilus/i-regular-24.png"); g_free (real_icon_name); return ret; } /* Loads a pixbuf for the icon, falling back on the default icon if * necessary */ void get_icon_pixbuf (MimeTypeInfo *info, const gchar *icon_path, gboolean want_large) { static GHashTable *icon_table = NULL; if (icon_path == NULL) icon_path = get_real_icon_path (info, NULL); if (icon_path == NULL) return; if ((want_large && info->icon_pixbuf != NULL) || info->small_icon_pixbuf != NULL) return; if (icon_table == NULL) icon_table = g_hash_table_new (g_str_hash, g_str_equal); if (!want_large) info->small_icon_pixbuf = g_hash_table_lookup (icon_table, icon_path); if (info->small_icon_pixbuf != NULL) { g_object_ref (G_OBJECT (info->small_icon_pixbuf)); } else { info->icon_pixbuf = gdk_pixbuf_new_from_file (icon_path, NULL); if (info->icon_pixbuf == NULL) { get_icon_pixbuf (info, NULL, want_large); } else if (!want_large) { info->small_icon_pixbuf = gdk_pixbuf_scale_simple (info->icon_pixbuf, 16, 16, GDK_INTERP_HYPER); g_hash_table_insert (icon_table, g_strdup (icon_path), info->small_icon_pixbuf); } } } static MimeCategoryInfo * get_category (const gchar *category_name, const gchar *category_desc, GtkTreeModel *model) { ModelEntry *current, *child; gchar **cf = NULL, **df = NULL; gchar **categories = NULL, **desc_categories = NULL; int i; if (category_name == NULL && category_desc == NULL) return NULL; if (category_name != NULL) categories = cf = g_strsplit (category_name, "/", -1); if (category_desc != NULL) desc_categories = df = g_strsplit (category_desc, "/", -1); if (category_name == NULL) categories = desc_categories; else if (category_desc == NULL) desc_categories = categories; current = get_model_entries (model); for (i = 0; categories[i] != NULL; i++) { for (child = current->first_child; child != NULL; child = child->next) { if (child->type != MODEL_ENTRY_CATEGORY) continue; if (!g_ascii_strcasecmp ((category_name == NULL) ? MIME_CATEGORY_INFO (child)->description : MIME_CATEGORY_INFO (child)->name, categories[i])) break; } if (child == NULL) { child = MODEL_ENTRY (mime_category_info_new (MIME_CATEGORY_INFO (current), categories[i], desc_categories[i], model)); model_entry_insert_child (MODEL_ENTRY (child)->parent, MODEL_ENTRY (child), model); } current = child; } g_strfreev (cf); g_strfreev (df); return MIME_CATEGORY_INFO (current); }