/* -*- 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 "nautilus-file-attributes.h" #include "nautilus-file.h" #include "nautilus-glib-extensions.h" #include "nautilus-lib-self-check-functions.h" #include "nautilus-metadata.h" #include #include #include #include #include static int gnome_vfs_mime_application_has_id (GnomeVFSMimeApplication *application, const char *id); static int 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 *mime_type_get_supertype (const char *mime_type); static GList *get_explicit_content_view_iids_from_metafile (NautilusFile *file); static gboolean server_has_content_requirements (OAF_ServerInfo *server); static gboolean application_supports_uri_scheme (gpointer data, gpointer uri_scheme); static GList *nautilus_do_component_query (const char *mime_type, const char *uri_scheme, GList *content_mime_types, gboolean ignore_content_mime_types, GList *explicit_iids, char **extra_sort_criteria, char *extra_requirements, CORBA_Environment *ev); static GList *str_list_difference (GList *a, GList *b); 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, "GNOME_VFS_MIME_TYPE_UNKNOWN") == 0) { return FALSE; } return TRUE; } static gboolean nautilus_mime_actions_check_if_minimum_attributes_ready (NautilusFile *file) { GList *attributes; gboolean ready; attributes = nautilus_mime_actions_get_minimum_file_attributes (); ready = nautilus_file_check_if_ready (file, attributes); g_list_free (attributes); return ready; } static gboolean nautilus_mime_actions_check_if_full_attributes_ready (NautilusFile *file) { GList *attributes; gboolean ready; attributes = nautilus_mime_actions_get_full_file_attributes (); ready = nautilus_file_check_if_ready (file, attributes); g_list_free (attributes); return ready; } GList * nautilus_mime_actions_get_minimum_file_attributes (void) { GList *attributes; attributes = NULL; attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_METADATA); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_MIME_TYPE); return attributes; } GList * nautilus_mime_actions_get_full_file_attributes (void) { GList *attributes; attributes = nautilus_mime_actions_get_minimum_file_attributes (); attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES); return attributes; } GnomeVFSMimeActionType nautilus_mime_get_default_action_type_for_file (NautilusFile *file) { char *mime_type; char *action_type_string; GnomeVFSMimeActionType action_type; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return 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_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; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return 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; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return NULL; } used_user_chosen_info = TRUE; default_application_string = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_APPLICATION, NULL); /* FIXME bugzilla.eazel.com 5085: should fall back to normal default if user-specified default is bogus */ if (default_application_string == NULL) { mime_type = nautilus_file_get_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 char ** nautilus_mime_get_default_component_sort_conditions (NautilusFile *file, char *default_component_string) { char **sort_conditions; char *supertype; char *mime_type; GList *short_list; GList *p; char *prev; sort_conditions = g_new0 (char *, 5); mime_type = nautilus_file_get_mime_type (file); 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 ("prefer_by_list_order (iid, ['"); 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, "'])", 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; g_free (mime_type); g_free (supertype); return sort_conditions; } 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; char *extra_requirements; gboolean used_user_chosen_info; gboolean metadata_default; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return NULL; } used_user_chosen_info = TRUE; info_list = NULL; CORBA_exception_init (&ev); mime_type = nautilus_file_get_mime_type (file); uri_scheme = nautilus_file_get_uri_scheme (file); explicit_iids = get_explicit_content_view_iids_from_metafile (file); if (!nautilus_mime_actions_check_if_full_attributes_ready (file) || !nautilus_file_get_directory_item_mime_types (file, &item_mime_types)) { item_mime_types = NULL; } default_component_string = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_DEFAULT_COMPONENT, NULL); if (default_component_string == NULL) { metadata_default = FALSE; if (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); } } } else { metadata_default = TRUE; } sort_conditions = nautilus_mime_get_default_component_sort_conditions (file, default_component_string); /* If the default is specified in the per-uri metadata, respect the setting regardless of content type requirements */ if (metadata_default) { extra_requirements = g_strconcat ("iid == '", default_component_string, "'", NULL); info_list = nautilus_do_component_query (mime_type, uri_scheme, item_mime_types, TRUE, explicit_iids, sort_conditions, extra_requirements, &ev); g_free (extra_requirements); } if (info_list == NULL) { info_list = nautilus_do_component_query (mime_type, uri_scheme, item_mime_types, FALSE, explicit_iids, sort_conditions, NULL, &ev); } if (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 { server = NULL; } nautilus_g_list_free_deep (item_mime_types); g_strfreev (sort_conditions); 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; char *uri_scheme; GList *result; GList *removed; GList *metadata_application_add_ids; GList *metadata_application_remove_ids; GList *p; GnomeVFSMimeApplication *application; CORBA_Environment ev; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return NULL; } mime_type = nautilus_file_get_mime_type (file); result = gnome_vfs_mime_get_short_list_applications (mime_type); g_free (mime_type); /* First remove applications that cannot support this location */ uri_scheme = nautilus_file_get_uri_scheme (file); g_assert (uri_scheme != NULL); result = nautilus_g_list_partition (result, application_supports_uri_scheme, uri_scheme, &removed); gnome_vfs_mime_application_list_free (removed); 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); 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; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return NULL; } CORBA_exception_init (&ev); uri_scheme = nautilus_file_get_uri_scheme (file); explicit_iids = get_explicit_content_view_iids_from_metafile (file); if (!nautilus_mime_actions_check_if_full_attributes_ready (file) || !nautilus_file_get_directory_item_mime_types (file, &item_mime_types)) { item_mime_types = NULL; } 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_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); } } /* By copying the iids using g_list_prepend, we've reversed the short list order. We need to use the order to determine the first available component, so reverse it now to maintain original ordering */ iids = g_list_reverse (iids); 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, FALSE, explicit_iids, NULL, extra_requirements, &ev); g_free (extra_requirements); } nautilus_g_list_free_deep (item_mime_types); gnome_vfs_mime_component_list_free (servers); g_list_free (iids); g_free (uri_scheme); g_free (mime_type); return result; } /* FIXME bugzilla.eazel.com 5086: 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; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return NULL; } mime_type = nautilus_file_get_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; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return 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_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; } gboolean nautilus_mime_actions_file_needs_full_file_attributes (NautilusFile *file) { char *mime_type; char *uri_scheme; GList *info_list; GList *explicit_iids; GList *p; CORBA_Environment ev; gboolean needs_full_attributes; g_return_val_if_fail (nautilus_mime_actions_check_if_minimum_attributes_ready (file), FALSE); if (!nautilus_file_is_directory (file)) { return FALSE; } CORBA_exception_init (&ev); uri_scheme = nautilus_file_get_uri_scheme (file); mime_type = nautilus_file_get_mime_type (file); explicit_iids = get_explicit_content_view_iids_from_metafile (file); info_list = nautilus_do_component_query (mime_type, uri_scheme, NULL, TRUE, explicit_iids, NULL, NULL, &ev); needs_full_attributes = FALSE; for (p = info_list; p != NULL; p = p->next) { needs_full_attributes |= server_has_content_requirements ((OAF_ServerInfo *) (p->data)); } gnome_vfs_mime_component_list_free (info_list); nautilus_g_list_free_deep (explicit_iids); g_free (uri_scheme); g_free (mime_type); CORBA_exception_free (&ev); return needs_full_attributes; } 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; if (!nautilus_mime_actions_check_if_minimum_attributes_ready (file)) { return NULL; } CORBA_exception_init (&ev); uri_scheme = nautilus_file_get_uri_scheme (file); mime_type = nautilus_file_get_mime_type (file); explicit_iids = get_explicit_content_view_iids_from_metafile (file); if (!nautilus_mime_actions_check_if_full_attributes_ready (file) || !nautilus_file_get_directory_item_mime_types (file, &item_mime_types)) { item_mime_types = NULL; } info_list = nautilus_do_component_query (mime_type, uri_scheme, item_mime_types, FALSE, explicit_iids, NULL, NULL, &ev); nautilus_g_list_free_deep (explicit_iids); nautilus_g_list_free_deep (item_mime_types); 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_minimum_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_minimum_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_minimum_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); /* get per-mime short list */ mime_type = nautilus_file_get_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_minimum_attributes_ready (file), GNOME_VFS_ERROR_GENERIC); /* get per-mime short list */ mime_type = nautilus_file_get_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_minimum_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_minimum_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_minimum_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_minimum_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_minimum_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_minimum_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 int gnome_vfs_mime_application_has_id (GnomeVFSMimeApplication *application, const char *id) { return strcmp (application->id, id); } static int 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 ? (int) 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_has_content_requirements (OAF_ServerInfo *server) { OAF_Property *prop; prop = oaf_server_info_prop_find (server, "nautilus:required_directory_content_mime_types"); if (prop == NULL || prop->v._d != OAF_P_STRINGV) { return FALSE; } else { return TRUE; } } static gboolean server_matches_content_requirements (OAF_ServerInfo *server, GHashTable *type_table, GList *explicit_iids) { OAF_Property *prop; GNOME_stringlist types; guint 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; } if (!server_has_content_requirements (server)) { return TRUE; } else { prop = oaf_server_info_prop_find (server, "nautilus:required_directory_content_mime_types"); 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}; static GList * nautilus_do_component_query (const char *mime_type, const char *uri_scheme, GList *item_mime_types, gboolean ignore_content_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; guint 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 (ignore_content_mime_types || server_matches_content_requirements (server, content_types, explicit_iids)) { /* Hack to suppress the Bonobo_Sample_Text component, since the Nautilus text * view is a superset and it's confusing for the user to be presented with both */ if (server->iid != NULL && strcmp (server->iid, "OAFIID:Bonobo_Sample_Text") != 0) { 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; } static gboolean application_supports_uri_scheme (gpointer data, gpointer uri_scheme) { GnomeVFSMimeApplication *application; g_assert (data != NULL); application = (GnomeVFSMimeApplication *) data; /* The default supported uri scheme is "file" */ if (application->supported_uri_schemes == NULL && strcmp ((const char *)uri_scheme, "file") == 0) { return TRUE; } return (g_list_find_custom (application->supported_uri_schemes, uri_scheme, (GCompareFunc) strcmp) != NULL); }