/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* nautilus-mime-actions.h - uri-specific versions of mime action functions Copyright (C) 2000 Eazel, Inc. The Gnome Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Authors: Maciej Stachowiak */ #include #include "nautilus-mime-actions.h" #include #include #include #include #include "nautilus-lib-self-check-functions.h" #include "nautilus-file.h" #include "nautilus-file-attributes.h" #include "nautilus-glib-extensions.h" #include "nautilus-metadata.h" #include "nautilus-wait-until-ready.h" #include static gint gnome_vfs_mime_application_has_id (GnomeVFSMimeApplication *application, const char *id); static gint gnome_vfs_mime_id_matches_application (const char *id, GnomeVFSMimeApplication *application); static gboolean gnome_vfs_mime_application_has_id_not_in_list (GnomeVFSMimeApplication *application, GList *ids); static gboolean string_not_in_list (const char *str, GList *list); static char *extract_prefix_add_suffix (const char *string, const char *separator, const char *suffix); static char *mime_type_get_supertype (const char *mime_type); static GList *get_explicit_content_view_iids_from_metafile (NautilusFile *file); static char *make_oaf_query_for_explicit_content_view_iids (GList *view_iids); static char *make_oaf_query_with_known_mime_type (const char *mime_type, const char *uri_scheme, GList *explicit_iids, const char *extra_requirements); static char *make_oaf_query_with_uri_scheme_only (const char *uri_scheme, GList *explicit_iids, const char *extra_requirements); static GHashTable *mime_type_list_to_hash_table (GList *files); static void free_key (gpointer key, gpointer value, gpointer user_data); static void mime_type_hash_table_destroy (GHashTable *table); static gboolean server_matches_content_requirements (OAF_ServerInfo *server, GHashTable *type_table, GList *explicit_iids); static GList *nautilus_do_component_query (const char *mime_type, const char *uri_scheme, GList *files, GList *explicit_iids, char **extra_sort_criteria, char *extra_requirements, CORBA_Environment *ev); static GList *str_list_difference (GList *a, GList *b); static int strv_length (char **a); static char **strv_concat (char **a, char **b); static gboolean is_known_mime_type (const char *mime_type) { if (mime_type == NULL) { return FALSE; } if (strcasecmp (mime_type, "application/octet-stream") == 0) { return FALSE; } return TRUE; } static gboolean nautilus_mime_actions_check_if_required_attributes_ready (NautilusFile *file) { GList *attributes; gboolean ready; attributes = nautilus_mime_actions_get_required_file_attributes (); ready = nautilus_file_check_if_ready (file, attributes); g_list_free (attributes); return ready; } GList * nautilus_mime_actions_get_required_file_attributes () { GList *attributes; attributes = NULL; attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_METADATA); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_SLOW_MIME_TYPE); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES); return attributes; } void nautilus_mime_actions_wait_for_required_file_attributes (NautilusFile *file) { GList *attributes; attributes = nautilus_mime_actions_get_required_file_attributes (); nautilus_file_wait_until_ready (file, attributes); g_list_free (attributes); } GnomeVFSMimeActionType nautilus_mime_get_default_action_type_for_file (NautilusFile *file) { char *mime_type; char *action_type_string; GnomeVFSMimeActionType action_type; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_MIME_ACTION_TYPE_NONE); action_type_string = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_ACTION_TYPE, NULL); if (action_type_string == NULL) { mime_type = nautilus_file_get_slow_mime_type (file); action_type = gnome_vfs_mime_get_default_action_type (mime_type); g_free (mime_type); return action_type; } else { if (strcasecmp (action_type_string, "application") == 0) { return GNOME_VFS_MIME_ACTION_TYPE_APPLICATION; } else if (strcasecmp (action_type_string, "component") == 0) { return GNOME_VFS_MIME_ACTION_TYPE_COMPONENT; } else { return GNOME_VFS_MIME_ACTION_TYPE_NONE; } } } GnomeVFSMimeAction * nautilus_mime_get_default_action_for_file (NautilusFile *file) { GnomeVFSMimeAction *action; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); action = g_new0 (GnomeVFSMimeAction, 1); action->action_type = nautilus_mime_get_default_action_type_for_file (file); switch (action->action_type) { case GNOME_VFS_MIME_ACTION_TYPE_APPLICATION: action->action.application = nautilus_mime_get_default_application_for_file (file); if (action->action.application == NULL) { g_free (action); action = NULL; } break; case GNOME_VFS_MIME_ACTION_TYPE_COMPONENT: action->action.component = nautilus_mime_get_default_component_for_file (file); if (action->action.component == NULL) { g_free (action); action = NULL; } case GNOME_VFS_MIME_ACTION_TYPE_NONE: g_free (action); action = NULL; break; default: g_assert_not_reached (); } return action; } static GnomeVFSMimeApplication * nautilus_mime_get_default_application_for_file_internal (NautilusFile *file, gboolean *user_chosen) { char *mime_type; GnomeVFSMimeApplication *result; char *default_application_string; gboolean used_user_chosen_info; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); used_user_chosen_info = TRUE; default_application_string = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_APPLICATION, NULL); if (default_application_string == NULL) { mime_type = nautilus_file_get_slow_mime_type (file); result = gnome_vfs_mime_get_default_application (mime_type); g_free (mime_type); used_user_chosen_info = FALSE; } else { result = gnome_vfs_application_registry_get_mime_application (default_application_string); } if (user_chosen != NULL) { *user_chosen = used_user_chosen_info; } return result; } GnomeVFSMimeApplication * nautilus_mime_get_default_application_for_file (NautilusFile *file) { return nautilus_mime_get_default_application_for_file_internal (file, NULL); } gboolean nautilus_mime_is_default_application_for_file_user_chosen (NautilusFile *file) { GnomeVFSMimeApplication *application; gboolean user_chosen; application = nautilus_mime_get_default_application_for_file_internal (file, &user_chosen); /* Doesn't count as user chosen if the user-specified data is bogus and doesn't * result in an actual application. */ if (application == NULL) { return FALSE; } gnome_vfs_mime_application_free (application); return user_chosen; } static OAF_ServerInfo * nautilus_mime_get_default_component_for_file_internal (NautilusFile *file, gboolean *user_chosen) { GList *info_list; OAF_ServerInfo *mime_default; char *default_component_string; char *mime_type; char *uri_scheme; GList *item_mime_types; GList *explicit_iids; CORBA_Environment ev; OAF_ServerInfo *server; char *sort_conditions[5]; char *supertype; gboolean used_user_chosen_info; GList *short_list; GList *p; char *prev; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); used_user_chosen_info = TRUE; CORBA_exception_init (&ev); mime_type = nautilus_file_get_slow_mime_type (file); uri_scheme = nautilus_file_get_uri_scheme (file); explicit_iids = get_explicit_content_view_iids_from_metafile (file); nautilus_file_get_directory_item_mime_types (file, &item_mime_types); default_component_string = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_COMPONENT, NULL); if (default_component_string == NULL && is_known_mime_type (mime_type)) { mime_default = gnome_vfs_mime_get_default_component (mime_type); if (mime_default != NULL) { default_component_string = g_strdup (mime_default->iid); if (default_component_string != NULL) { /* Default component chosen based only on type. */ used_user_chosen_info = FALSE; } CORBA_free (mime_default); } } /* FIXME bugzilla.eazel.com 1264: * should return NULL for the following cases: * - Known URI scheme pointing to nonexistent location * - Syntactically invalid URI */ supertype = mime_type_get_supertype (mime_type); /* prefer the exact right IID */ if (default_component_string != NULL) { sort_conditions[0] = g_strconcat ("iid == '", default_component_string, "'", NULL); } else { sort_conditions[0] = g_strdup ("true"); } /* Prefer something from the short list */ short_list = nautilus_mime_get_short_list_components_for_file (file); if (short_list != NULL) { sort_conditions[1] = g_strdup ("has (['"); for (p = short_list; p != NULL; p = p->next) { prev = sort_conditions[1]; if (p->next != NULL) { sort_conditions[1] = g_strconcat (prev, ((OAF_ServerInfo *) (p->data))->iid, "','", NULL); } else { sort_conditions[1] = g_strconcat (prev, ((OAF_ServerInfo *) (p->data))->iid, "'], iid)", NULL); } g_free (prev); } } else { sort_conditions[1] = g_strdup ("true"); } gnome_vfs_mime_component_list_free (short_list); /* Prefer something that matches the exact type to something that matches the supertype */ if (is_known_mime_type (mime_type)) { sort_conditions[2] = g_strconcat ("bonobo:supported_mime_types.has ('",mime_type,"')", NULL); } else { sort_conditions[2] = g_strdup ("true"); } /* Prefer something that matches the supertype to something that matches `*' */ if (is_known_mime_type (mime_type) && supertype != NULL) { sort_conditions[3] = g_strconcat ("bonobo:supported_mime_types.has ('",supertype,"')", NULL); } else { sort_conditions[3] = g_strdup ("true"); } sort_conditions[4] = NULL; info_list = nautilus_do_component_query (mime_type, uri_scheme, item_mime_types, explicit_iids, sort_conditions, NULL, &ev); if (ev._major == CORBA_NO_EXCEPTION && info_list != NULL) { server = OAF_ServerInfo_duplicate (info_list->data); gnome_vfs_mime_component_list_free (info_list); if (default_component_string != NULL && strcmp (server->iid, default_component_string) == 0) { used_user_chosen_info = TRUE; /* Default component chosen based on user-stored . */ } } else { g_assert (info_list == NULL); /* or else we are leaking it */ server = NULL; return NULL; } g_free (sort_conditions[0]); g_free (sort_conditions[1]); g_free (sort_conditions[2]); g_free (sort_conditions[3]); g_free (supertype); g_free (uri_scheme); g_free (mime_type); g_free (default_component_string); CORBA_exception_free (&ev); if (user_chosen != NULL) { *user_chosen = used_user_chosen_info; } return server; } OAF_ServerInfo * nautilus_mime_get_default_component_for_file (NautilusFile *file) { return nautilus_mime_get_default_component_for_file_internal (file, NULL); } gboolean nautilus_mime_is_default_component_for_file_user_chosen (NautilusFile *file) { OAF_ServerInfo *component; gboolean user_chosen; component = nautilus_mime_get_default_component_for_file_internal (file, &user_chosen); /* Doesn't count as user chosen if the user-specified data is bogus and doesn't * result in an actual component. */ if (component == NULL) { return FALSE; } CORBA_free (component); return user_chosen; } GList * nautilus_mime_get_short_list_applications_for_file (NautilusFile *file) { char *mime_type; GList *result; GList *removed; GList *metadata_application_add_ids; GList *metadata_application_remove_ids; GList *p; GnomeVFSMimeApplication *application; CORBA_Environment ev; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); CORBA_exception_init (&ev); metadata_application_add_ids = nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_APPLICATION_ADD, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID); metadata_application_remove_ids = nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_APPLICATION_REMOVE, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID); mime_type = nautilus_file_get_slow_mime_type (file); result = gnome_vfs_mime_get_short_list_applications (mime_type); g_free (mime_type); result = nautilus_g_list_partition (result, (NautilusPredicateFunction) gnome_vfs_mime_application_has_id_not_in_list, metadata_application_remove_ids, &removed); gnome_vfs_mime_application_list_free (removed); for (p = metadata_application_add_ids; p != NULL; p = p->next) { if (g_list_find_custom (result, p->data, (GCompareFunc) gnome_vfs_mime_application_has_id) == NULL && g_list_find_custom (metadata_application_remove_ids, p->data, (GCompareFunc) strcmp) == NULL) { application = gnome_vfs_application_registry_get_mime_application (p->data); if (application != NULL) { result = g_list_prepend (result, application); } } } CORBA_exception_free (&ev); return result; } GList * nautilus_mime_get_short_list_components_for_file (NautilusFile *file) { char *mime_type; char *uri_scheme; GList *item_mime_types; GList *servers; GList *iids; GList *result; GList *removed; GList *metadata_component_add_ids; GList *metadata_component_remove_ids; GList *p; OAF_ServerInfo *component; GList *explicit_iids; CORBA_Environment ev; char *extra_requirements; char *prev; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); CORBA_exception_init (&ev); uri_scheme = nautilus_file_get_uri_scheme (file); explicit_iids = get_explicit_content_view_iids_from_metafile (file); nautilus_file_get_directory_item_mime_types (file, &item_mime_types); metadata_component_add_ids = nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_COMPONENT_ADD, NAUTILUS_METADATA_SUBKEY_COMPONENT_IID); metadata_component_remove_ids = nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_COMPONENT_REMOVE, NAUTILUS_METADATA_SUBKEY_COMPONENT_IID); mime_type = nautilus_file_get_slow_mime_type (file); servers = gnome_vfs_mime_get_short_list_components (mime_type); iids = NULL; for (p = servers; p != NULL; p = p->next) { component = (OAF_ServerInfo *) p->data; iids = g_list_prepend (iids, component->iid); } iids = nautilus_g_list_partition (iids, (NautilusPredicateFunction) string_not_in_list, metadata_component_remove_ids, &removed); g_list_free (removed); for (p = metadata_component_add_ids; p != NULL; p = p->next) { if (g_list_find_custom (iids, p->data, (GCompareFunc) strcmp) == NULL && g_list_find_custom (metadata_component_remove_ids, p->data, (GCompareFunc) strcmp) == NULL) { iids = g_list_prepend (iids, p->data); } } result = NULL; if (iids != NULL) { extra_requirements = g_strdup ("has (['"); for (p = iids; p != NULL; p = p->next) { prev = extra_requirements; if (p->next != NULL) { extra_requirements = g_strconcat (prev, p->data, "','", NULL); } else { extra_requirements = g_strconcat (prev, p->data, "'], iid)", NULL); } g_free (prev); } result = nautilus_do_component_query (mime_type, uri_scheme, item_mime_types, explicit_iids, NULL, extra_requirements, &ev); g_free (extra_requirements); } gnome_vfs_mime_component_list_free (servers); g_list_free (iids); g_free (uri_scheme); g_free (mime_type); return result; } /* FIXME: we should disable this for 1.0 I think */ char * nautilus_mime_get_short_list_methods_for_file (NautilusFile *file) { char *mime_type; const char *method; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); mime_type = nautilus_file_get_slow_mime_type (file); method = gnome_vfs_mime_get_value (mime_type, "vfs-method"); g_free (mime_type); return g_strdup (method); } GList * nautilus_mime_get_all_applications_for_file (NautilusFile *file) { char *mime_type; GList *result; GList *metadata_application_ids; GList *p; GnomeVFSMimeApplication *application; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); metadata_application_ids = nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_EXPLICIT_APPLICATION, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID); mime_type = nautilus_file_get_slow_mime_type (file); result = gnome_vfs_mime_get_all_applications (mime_type); for (p = metadata_application_ids; p != NULL; p = p->next) { if (!g_list_find_custom (result, p->data, (GCompareFunc) gnome_vfs_mime_application_has_id)) { application = gnome_vfs_application_registry_get_mime_application (p->data); if (application != NULL) { result = g_list_prepend (result, application); } } } g_free (mime_type); return result; } gboolean nautilus_mime_has_any_applications_for_file (NautilusFile *file) { GList *list; gboolean result; list = nautilus_mime_get_all_applications_for_file (file); result = list != NULL; gnome_vfs_mime_application_list_free (list); return result; } GList * nautilus_mime_get_all_components_for_file (NautilusFile *file) { char *mime_type; char *uri_scheme; GList *item_mime_types; GList *info_list; GList *explicit_iids; CORBA_Environment ev; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), NULL); CORBA_exception_init (&ev); uri_scheme = nautilus_file_get_uri_scheme (file); mime_type = nautilus_file_get_slow_mime_type (file); explicit_iids = get_explicit_content_view_iids_from_metafile (file); nautilus_file_get_directory_item_mime_types (file, &item_mime_types); info_list = nautilus_do_component_query (mime_type, uri_scheme, item_mime_types, explicit_iids, NULL, NULL, &ev); g_free (uri_scheme); g_free (mime_type); CORBA_exception_free (&ev); return info_list; } gboolean nautilus_mime_has_any_components_for_file (NautilusFile *file) { GList *list; gboolean result; list = nautilus_mime_get_all_components_for_file (file); result = list != NULL; gnome_vfs_mime_component_list_free (list); return result; } GnomeVFSResult nautilus_mime_set_default_action_type_for_file (NautilusFile *file, GnomeVFSMimeActionType action_type) { const char *action_string; switch (action_type) { case GNOME_VFS_MIME_ACTION_TYPE_APPLICATION: action_string = "application"; break; case GNOME_VFS_MIME_ACTION_TYPE_COMPONENT: action_string = "component"; break; case GNOME_VFS_MIME_ACTION_TYPE_NONE: default: action_string = "none"; } nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_ACTION_TYPE, NULL, action_string); return GNOME_VFS_OK; } GnomeVFSResult nautilus_mime_set_default_application_for_file (NautilusFile *file, const char *application_id) { g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_APPLICATION, NULL, application_id); /* If there's no default action type, set it to match this. */ if (application_id != NULL && nautilus_mime_get_default_action_type_for_file (file) == GNOME_VFS_MIME_ACTION_TYPE_NONE) { return nautilus_mime_set_default_action_type_for_file (file, GNOME_VFS_MIME_ACTION_TYPE_APPLICATION); } return GNOME_VFS_OK; } GnomeVFSResult nautilus_mime_set_default_component_for_file (NautilusFile *file, const char *component_iid) { g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_COMPONENT, NULL, component_iid); /* If there's no default action type, set it to match this. */ if (component_iid != NULL && nautilus_mime_get_default_action_type_for_file (file) == GNOME_VFS_MIME_ACTION_TYPE_NONE) { return nautilus_mime_set_default_action_type_for_file (file, GNOME_VFS_MIME_ACTION_TYPE_COMPONENT); } return GNOME_VFS_OK; } GnomeVFSResult nautilus_mime_set_short_list_applications_for_file (NautilusFile *file, GList *applications) { GList *add_list; GList *remove_list; GList *normal_short_list; GList *normal_short_list_ids; GList *p; char *mime_type; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); /* get per-mime short list */ mime_type = nautilus_file_get_slow_mime_type (file); normal_short_list = gnome_vfs_mime_get_short_list_applications (mime_type); g_free (mime_type); normal_short_list_ids = NULL; for (p = normal_short_list; p != NULL; p = p->next) { normal_short_list_ids = g_list_prepend (normal_short_list_ids, ((GnomeVFSMimeApplication *) p->data)->id); } /* compute delta */ add_list = str_list_difference (applications, normal_short_list_ids); remove_list = str_list_difference (normal_short_list_ids, applications); nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_APPLICATION_ADD, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID, add_list); nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_APPLICATION_REMOVE, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID, remove_list); /* FIXME bugzilla.eazel.com 1269: * need to free normal_short_list, normal_short_list_ids, add_list, remove_list */ return GNOME_VFS_OK; } GnomeVFSResult nautilus_mime_set_short_list_components_for_file (NautilusFile *file, GList *components) { GList *add_list; GList *remove_list; GList *normal_short_list; GList *normal_short_list_ids; GList *p; char *mime_type; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); /* get per-mime short list */ mime_type = nautilus_file_get_slow_mime_type (file); normal_short_list = gnome_vfs_mime_get_short_list_components (mime_type); g_free (mime_type); normal_short_list_ids = NULL; for (p = normal_short_list; p != NULL; p = p->next) { normal_short_list_ids = g_list_prepend (normal_short_list_ids, ((OAF_ServerInfo *) p->data)->iid); } /* compute delta */ add_list = str_list_difference (components, normal_short_list_ids); remove_list = str_list_difference (normal_short_list_ids, components); nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_COMPONENT_ADD, NAUTILUS_METADATA_SUBKEY_COMPONENT_IID, add_list); nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_SHORT_LIST_COMPONENT_REMOVE, NAUTILUS_METADATA_SUBKEY_COMPONENT_IID, remove_list); /* FIXME bugzilla.eazel.com 1269: * need to free normal_short_list, normal_short_list_ids, add_list, remove_list */ return GNOME_VFS_OK; } GnomeVFSResult nautilus_mime_add_application_to_short_list_for_file (NautilusFile *file, const char *application_id) { GList *old_list, *new_list; GnomeVFSResult result; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); result = GNOME_VFS_OK; old_list = nautilus_mime_get_short_list_applications_for_file (file); if (!gnome_vfs_mime_id_in_application_list (application_id, old_list)) { new_list = g_list_append (gnome_vfs_mime_id_list_from_application_list (old_list), g_strdup (application_id)); result = nautilus_mime_set_short_list_applications_for_file (file, new_list); nautilus_g_list_free_deep (new_list); } gnome_vfs_mime_application_list_free (old_list); return result; } GnomeVFSResult nautilus_mime_remove_application_from_short_list_for_file (NautilusFile *file, const char *application_id) { GList *old_list, *new_list; gboolean was_in_list; GnomeVFSResult result; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); old_list = nautilus_mime_get_short_list_applications_for_file (file); old_list = gnome_vfs_mime_remove_application_from_list (old_list, application_id, &was_in_list); if (!was_in_list) { result = GNOME_VFS_OK; } else { new_list = gnome_vfs_mime_id_list_from_application_list (old_list); result = nautilus_mime_set_short_list_applications_for_file (file, new_list); nautilus_g_list_free_deep (new_list); } gnome_vfs_mime_application_list_free (old_list); return result; } GnomeVFSResult nautilus_mime_add_component_to_short_list_for_file (NautilusFile *file, const char *iid) { GList *old_list, *new_list; GnomeVFSResult result; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); old_list = nautilus_mime_get_short_list_components_for_file (file); if (gnome_vfs_mime_id_in_component_list (iid, old_list)) { result = GNOME_VFS_OK; } else { new_list = g_list_append (gnome_vfs_mime_id_list_from_component_list (old_list), g_strdup (iid)); result = nautilus_mime_set_short_list_components_for_file (file, new_list); nautilus_g_list_free_deep (new_list); } gnome_vfs_mime_component_list_free (old_list); return result; } GnomeVFSResult nautilus_mime_remove_component_from_short_list_for_file (NautilusFile *file, const char *iid) { GList *old_list, *new_list; gboolean was_in_list; GnomeVFSResult result; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); old_list = nautilus_mime_get_short_list_components_for_file (file); old_list = gnome_vfs_mime_remove_component_from_list (old_list, iid, &was_in_list); if (!was_in_list) { result = GNOME_VFS_OK; } else { new_list = gnome_vfs_mime_id_list_from_component_list (old_list); result = nautilus_mime_set_short_list_components_for_file (file, new_list); nautilus_g_list_free_deep (new_list); } gnome_vfs_mime_component_list_free (old_list); return result; } GnomeVFSResult nautilus_mime_extend_all_applications_for_file (NautilusFile *file, GList *applications) { GList *metadata_application_ids; GList *extras; GList *final_applications; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); metadata_application_ids = nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_EXPLICIT_APPLICATION, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID); extras = str_list_difference (applications, metadata_application_ids); final_applications = g_list_concat (g_list_copy (metadata_application_ids), extras); nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_EXPLICIT_APPLICATION, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID, final_applications); return GNOME_VFS_OK; } GnomeVFSResult nautilus_mime_remove_from_all_applications_for_file (NautilusFile *file, GList *applications) { GList *metadata_application_ids; GList *final_applications; g_return_val_if_fail (nautilus_mime_actions_check_if_required_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); metadata_application_ids = nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_EXPLICIT_APPLICATION, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID); final_applications = str_list_difference (metadata_application_ids, applications); nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_EXPLICIT_APPLICATION, NAUTILUS_METADATA_SUBKEY_APPLICATION_ID, final_applications); return GNOME_VFS_OK; } static gint gnome_vfs_mime_application_has_id (GnomeVFSMimeApplication *application, const char *id) { return strcmp (application->id, id); } static gint gnome_vfs_mime_id_matches_application (const char *id, GnomeVFSMimeApplication *application) { return gnome_vfs_mime_application_has_id (application, id); } static gboolean gnome_vfs_mime_application_has_id_not_in_list (GnomeVFSMimeApplication *application, GList *ids) { return g_list_find_custom (ids, application, (GCompareFunc) gnome_vfs_mime_id_matches_application) == NULL; } static gboolean string_not_in_list (const char *str, GList *list) { return g_list_find_custom (list, (gpointer) str, (GCompareFunc) strcmp) == NULL; } static char * extract_prefix_add_suffix (const char *string, const char *separator, const char *suffix) { const char *separator_position; int prefix_length; char *result; separator_position = strstr (string, separator); prefix_length = separator_position == NULL ? strlen (string) : separator_position - string; result = g_malloc (prefix_length + strlen(suffix) + 1); strncpy (result, string, prefix_length); result[prefix_length] = '\0'; strcat (result, suffix); return result; } static char * mime_type_get_supertype (const char *mime_type) { if (mime_type == NULL || mime_type == '\0') { return g_strdup (mime_type); } return extract_prefix_add_suffix (mime_type, "/", "/*"); } /* * The following routine uses metadata associated with the current url * to add content view components specified in the metadata. The * content views are specified in the string as elements inside the appropriate element. */ static GList * get_explicit_content_view_iids_from_metafile (NautilusFile *file) { if (file != NULL) { return nautilus_file_get_metadata_list (file, NAUTILUS_METADATA_KEY_EXPLICIT_COMPONENT, NAUTILUS_METADATA_SUBKEY_COMPONENT_IID); } else { return NULL; } } static char * make_oaf_query_for_explicit_content_view_iids (GList *view_iids) { GList *p; char *iid; char *query; char *old_query; query = NULL; for (p = view_iids; p != NULL; p = p->next) { iid = (char *) p->data; if (query != NULL) { old_query = query; query = g_strconcat (query, " OR ", NULL); g_free (old_query); } else { query = g_strdup ("("); } old_query = query; query = g_strdup_printf ("%s iid=='%s'", old_query, iid); g_free (old_query); } if (query != NULL) { old_query = query; query = g_strconcat (old_query, ")", NULL); g_free (old_query); } else { query = g_strdup ("false"); } return query; } static char * make_oaf_query_with_known_mime_type (const char *mime_type, const char *uri_scheme, GList *explicit_iids, const char *extra_requirements) { char *mime_supertype; char *result; char *explicit_iid_query; mime_supertype = mime_type_get_supertype (mime_type); explicit_iid_query = make_oaf_query_for_explicit_content_view_iids (explicit_iids); result = g_strdup_printf ( /* Check if the component has the interfaces we need. * We can work with either a Nautilus View, or * with a Bonobo Control or Embeddable that supports * one of the three persistence interfaces: * PersistStream, ProgressiveDataSink, or * PersistFile. */ "(((repo_ids.has_all (['IDL:Bonobo/Control:1.0'," "'IDL:Nautilus/View:1.0'])" "OR (repo_ids.has_one (['IDL:Bonobo/Control:1.0'," "'IDL:Bonobo/Embeddable:1.0'])" "AND repo_ids.has_one (['IDL:Bonobo/PersistStream:1.0'," "'IDL:Bonobo/ProgressiveDataSink:1.0'," "'IDL:Bonobo/PersistFile:1.0'])))" /* Check that the component either has a specific * MIME type or URI scheme. If neither is specified, * then we don't trust that to mean "all MIME types * and all schemes". For that, you have to do a * wildcard for the MIME type or for the scheme. */ "AND (bonobo:supported_mime_types.defined ()" "OR bonobo:supported_uri_schemes.defined ()" "OR bonobo:additional_uri_schemes.defined ())" /* One of two possibilties */ /* FIXME bugzilla.eazel.com 2542: this comment is not very clear. */ /* 1 The mime type and URI scheme match the supported attributes. */ "AND (" /* Check that the supported MIME types include the * URI's MIME type or its supertype. */ "((NOT bonobo:supported_mime_types.defined ()" "OR bonobo:supported_mime_types.has ('%s')" "OR bonobo:supported_mime_types.has ('%s')" "OR bonobo:supported_mime_types.has ('*/*'))" /* Check that the supported URI schemes include the * URI's scheme. */ "AND (NOT bonobo:supported_uri_schemes.defined ()" "OR bonobo:supported_uri_schemes.has ('%s')" "OR bonobo:supported_uri_schemes.has ('*')))" /* 2 OR The additional URI schemes include this URI's scheme; if that is the case, this view applies whether or not the mime type is supported. */ "OR (bonobo:additional_uri_schemes.has ('%s')" "OR bonobo:additional_uri_schemes.has ('*')))" /* Check that the component makes it clear that it's * intended for Nautilus by providing a "view_as" * name. We could instead support a default, but * that would make components that are untested with * Nautilus appear. */ "AND nautilus:view_as_name.defined ())" /* Also select iids that were specifically requested for this location, even if they do not otherwise meet the requirements. */ "OR %s)" /* Make it possible to add extra requirements */ " AND (%s)" /* The MIME type, MIME supertype, and URI scheme for * the %s above. */ , mime_type, mime_supertype, uri_scheme, uri_scheme /* The explicit metafile iid query for the %s above. */ , explicit_iid_query /* extra requirements */ , extra_requirements != NULL ? extra_requirements : "true"); g_free (mime_supertype); g_free (explicit_iid_query); return result; } static char * make_oaf_query_with_uri_scheme_only (const char *uri_scheme, GList *explicit_iids, const char *extra_requirements) { char *result; char *explicit_iid_query; explicit_iid_query = make_oaf_query_for_explicit_content_view_iids (explicit_iids); result = g_strdup_printf ( /* Check if the component has the interfaces we need. * We can work with either a Nautilus tView, or * with a Bonobo Control or Embeddable that works on * a file, which is indicated by Bonobo PersistFile. */ "(((repo_ids.has_all(['IDL:Bonobo/Control:1.0'," "'IDL:Nautilus/View:1.0'])" "OR (repo_ids.has_one(['IDL:Bonobo/Control:1.0'," "'IDL:Bonobo/Embeddable:1.0'])" "AND repo_ids.has('IDL:Bonobo/PersistFile:1.0')))" /* Check if the component supports this particular * URI scheme. */ "AND (((bonobo:supported_uri_schemes.has ('%s')" "OR bonobo:supported_uri_schemes.has ('*'))" /* Check that the component doesn't require * particular MIME types. Note that even saying you support "all" */ "AND (NOT bonobo:supported_mime_types.defined ()))" /* FIXME bugzilla.eazel.com 2542: improve the comment explaining this. */ /* This attribute allows uri schemes to be supported even for unsupported mime types or no mime type. */ "OR (bonobo:additional_uri_schemes.has ('%s')" "OR bonobo:additional_uri_schemes.has ('*')))" /* Check that the component makes it clear that it's * intended for Nautilus by providing a "view_as" * name. We could instead support a default, but * that would make components that are untested with * Nautilus appear. */ "AND nautilus:view_as_name.defined ())" /* Also select iids that were specifically requested for this location, even if they do not otherwise meet the requirements. */ "OR %s)" /* Make it possible to add extra requirements */ " AND (%s)" /* The URI scheme for the %s above. */ , uri_scheme, uri_scheme /* The explicit metafile iid query for the %s above. */ , explicit_iid_query, extra_requirements != NULL ? extra_requirements : "true"); return result; } static GHashTable * mime_type_list_to_hash_table (GList *types) { GHashTable *result; GList *p; char *mime_type; result = g_hash_table_new (g_str_hash, g_str_equal); for (p = types; p != NULL; p = p->next) { if (p->data != NULL) { mime_type = (char *) (p->data); if (g_hash_table_lookup (result, mime_type) == NULL) { #ifdef DEBUG_MJS printf ("XXX content mime type: %s\n", mime_type); #endif g_hash_table_insert (result, g_strdup (mime_type), mime_type); } } } return result; } static void free_key (gpointer key, gpointer value, gpointer user_data) { g_free (key); } static void mime_type_hash_table_destroy (GHashTable *table) { g_hash_table_foreach (table, free_key, NULL); g_hash_table_destroy (table); } static gboolean server_matches_content_requirements (OAF_ServerInfo *server, GHashTable *type_table, GList *explicit_iids) { OAF_Property *prop; GNOME_stringlist types; int i; /* Components explicitly requested in the metafile are not capability tested. */ if (g_list_find_custom (explicit_iids, (gpointer) server->iid, (GCompareFunc) strcmp) != NULL) { return TRUE; } prop = oaf_server_info_prop_find (server, "nautilus:required_directory_content_mime_types"); if (prop == NULL || prop->v._d != OAF_P_STRINGV) { return TRUE; } else { types = prop->v._u.value_stringv; for (i = 0; i < types._length; i++) { if (g_hash_table_lookup (type_table, types._buffer[i]) != NULL) { return TRUE; } } } return FALSE; } static char *nautilus_sort_criteria[] = { /* Prefer anything else over the loser view. */ "iid != 'OAFIID:nautilus_content_loser:95901458-c68b-43aa-aaca-870ced11062d'", /* Prefer anything else over the sample view. */ "iid != 'OAFIID:nautilus_sample_content_view:45c746bc-7d64-4346-90d5-6410463b43ae'", /* Sort alphabetically */ "name", NULL}; #if 0 /* Prefer the industrial strengthe html viewer most */ "iid == 'OAFIID:nautilus_mozilla_content_view:1ee70717-57bf-4079-aae5-922abdd576b1'", /* Prefer the gtkhtml viewer next */ "iid == 'OAFIID:ntl_web_browser:0ce1a736-c939-4ac7-b12c-19d72bf1510b'", /* Prefer the icon view next */ "iid == 'OAFIID:ntl_file_manager_icon_view:42681b21-d5ca-4837-87d2-394d88ecc058'", #endif; static GList * nautilus_do_component_query (const char *mime_type, const char *uri_scheme, GList *item_mime_types, GList *explicit_iids, char **extra_sort_criteria, char *extra_requirements, CORBA_Environment *ev) { OAF_ServerInfoList *oaf_result; char *query; GList *retval; char **all_sort_criteria; oaf_result = NULL; query = NULL; if (is_known_mime_type (mime_type)) { query = make_oaf_query_with_known_mime_type (mime_type, uri_scheme, explicit_iids, extra_requirements); } else { query = make_oaf_query_with_uri_scheme_only (uri_scheme, explicit_iids, extra_requirements); } #ifdef DEBUG_MJS printf ("query: \"%s\"\n", query); #endif all_sort_criteria = strv_concat (extra_sort_criteria, nautilus_sort_criteria);; oaf_result = oaf_query (query, all_sort_criteria, ev); g_free (all_sort_criteria); g_free (query); retval = NULL; if (ev->_major == CORBA_NO_EXCEPTION && oaf_result != NULL && oaf_result->_length > 0) { GHashTable *content_types; int i; content_types = mime_type_list_to_hash_table (item_mime_types); for (i = 0; i < oaf_result->_length; i++) { OAF_ServerInfo *server; server = &oaf_result->_buffer[i]; if (server_matches_content_requirements (server, content_types, explicit_iids)) { retval = g_list_append (retval, OAF_ServerInfo_duplicate (server)); } } mime_type_hash_table_destroy (content_types); } CORBA_free (oaf_result); return retval; } static GList * str_list_difference (GList *a, GList *b) { GList *p; GList *retval; retval = NULL; for (p = a; p != NULL; p = p->next) { if (g_list_find_custom (b, p->data, (GCompareFunc) strcmp) == NULL) { retval = g_list_prepend (retval, p->data); } } retval = g_list_reverse (retval); return retval; } static int strv_length (char **a) { int i; for (i = 0; a != NULL && a[i] != NULL; i++) { } return i; } static char ** strv_concat (char **a, char **b) { int a_length; int b_length; int i; int j; char **result; a_length = strv_length (a); b_length = strv_length (b); result = g_new0 (char *, a_length + b_length + 1); j = 0; for (i = 0; a != NULL && a[i] != NULL; i++) { result[j] = a[i]; j++; } for (i = 0; b != NULL && b[i] != NULL; i++) { result[j] = b[i]; j++; } result[j] = NULL; return result; }