/* * gedit-utils.c * This file is part of gedit * * Copyright (C) 1998, 1999 Alex Roberts, Evan Lawrence * Copyright (C) 2000, 2002 Chema Celorio, Paolo Maggi * Copyright (C) 2003-2005 Paolo Maggi * * 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, see . */ #include "gedit-utils.h" #include #include #include #include "gedit-debug.h" gboolean gedit_utils_menu_position_under_tree_view (GtkTreeView *tree_view, GdkRectangle *rect) { GtkTreeSelection *selection; GtkTreeModel *model; gint count_rows; GList *rows; gint widget_x, widget_y; model = gtk_tree_view_get_model (tree_view); g_return_val_if_fail (model != NULL, FALSE); selection = gtk_tree_view_get_selection (tree_view); g_return_val_if_fail (selection != NULL, FALSE); count_rows = gtk_tree_selection_count_selected_rows (selection); if (count_rows != 1) return FALSE; rows = gtk_tree_selection_get_selected_rows (selection, &model); gtk_tree_view_get_cell_area (tree_view, (GtkTreePath *)(rows->data), gtk_tree_view_get_column (tree_view, 0), rect); gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, rect->x, rect->y, &widget_x, &widget_y); rect->x = widget_x; rect->y = widget_y; g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free); return TRUE; } /** * gedit_utils_set_atk_name_description: * @widget: The Gtk widget for which name/description to be set * @name: Atk name string * @description: Atk description string * * This function sets up name and description * for a specified gtk widget. */ void gedit_utils_set_atk_name_description (GtkWidget *widget, const gchar *name, const gchar *description) { AtkObject *aobj; aobj = gtk_widget_get_accessible (widget); if (!(GTK_IS_ACCESSIBLE (aobj))) return; if (name) atk_object_set_name (aobj, name); if (description) atk_object_set_description (aobj, description); } static gchar * uri_get_dirname (const gchar *uri) { gchar *res; gchar *str; g_return_val_if_fail (uri != NULL, NULL); /* CHECK: does it work with uri chaining? - Paolo */ str = g_path_get_dirname (uri); g_return_val_if_fail (str != NULL, g_strdup (".")); if ((strlen (str) == 1) && (*str == '.')) { g_free (str); return NULL; } res = tepl_utils_replace_home_dir_with_tilde (str); g_free (str); return res; } /** * gedit_utils_location_get_dirname_for_display: * @location: the location * * Returns a string suitable to be displayed in the UI indicating * the name of the directory where the file is located. * For remote files it may also contain the hostname etc. * For local files it tries to replace the home dir with ~. * * Returns: (transfer full): a string to display the dirname */ gchar * gedit_utils_location_get_dirname_for_display (GFile *location) { gchar *uri; gchar *res; GMount *mount; g_return_val_if_fail (location != NULL, NULL); /* we use the parse name, that is either the local path * or an uri but which is utf8 safe */ uri = g_file_get_parse_name (location); /* FIXME: this is sync... is it a problem? */ mount = g_file_find_enclosing_mount (location, NULL, NULL); if (mount != NULL) { gchar *mount_name; gchar *path = NULL; gchar *dirname; mount_name = g_mount_get_name (mount); g_object_unref (mount); /* obtain the "path" part of the uri */ tepl_utils_decode_uri (uri, NULL, NULL, NULL, NULL, &path); if (path == NULL) { dirname = uri_get_dirname (uri); } else { dirname = uri_get_dirname (path); } if (dirname == NULL || strcmp (dirname, ".") == 0) { res = mount_name; } else { res = g_strdup_printf ("%s %s", mount_name, dirname); g_free (mount_name); } g_free (path); g_free (dirname); } else { /* fallback for local files or uris without mounts */ res = uri_get_dirname (uri); } g_free (uri); return res; } static gboolean is_valid_scheme_character (gchar c) { return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.'; } static gboolean has_valid_scheme (const gchar *uri) { const gchar *p; p = uri; if (!is_valid_scheme_character (*p)) { return FALSE; } do { p++; } while (is_valid_scheme_character (*p)); return *p == ':'; } gboolean gedit_utils_is_valid_location (GFile *location) { const guchar *p; gchar *uri; gboolean is_valid; if (location == NULL) return FALSE; uri = g_file_get_uri (location); if (!has_valid_scheme (uri)) { g_free (uri); return FALSE; } is_valid = TRUE; /* We expect to have a fully valid set of characters */ for (p = (const guchar *)uri; *p; p++) { if (*p == '%') { ++p; if (!g_ascii_isxdigit (*p)) { is_valid = FALSE; break; } ++p; if (!g_ascii_isxdigit (*p)) { is_valid = FALSE; break; } } else { if (*p <= 32 || *p >= 128) { is_valid = FALSE; break; } } } g_free (uri); return is_valid; } static gchar * make_canonical_uri_from_shell_arg (const gchar *str) { GFile *gfile; gchar *uri; g_return_val_if_fail (str != NULL, NULL); g_return_val_if_fail (*str != '\0', NULL); /* Note for the future: * FIXME: is still still relevant? * * paolo: and flame whoever tells * you that file:///gnome/test_files/hëllò * doesn't work --- that's not a valid URI * * federico: well, another solution that * does not requires patch to _from_shell_args * is to check that the string returned by it * contains only ASCII chars * paolo: hmmmm, isn't there * gnome_vfs_is_uri_valid() or something? * : I will use gedit_utils_is_valid_uri () * */ gfile = g_file_new_for_commandline_arg (str); if (gedit_utils_is_valid_location (gfile)) { uri = g_file_get_uri (gfile); g_object_unref (gfile); return uri; } g_object_unref (gfile); return NULL; } /** * gedit_utils_basename_for_display: * @location: location for which the basename should be displayed * * Returns: (transfer full): the basename of a file suitable for display to users. */ gchar * gedit_utils_basename_for_display (GFile *location) { gchar *name; gchar *hn; gchar *uri; g_return_val_if_fail (G_IS_FILE (location), NULL); uri = g_file_get_uri (location); /* First, try to query the display name, but only on local files */ if (g_file_has_uri_scheme (location, "file")) { GFileInfo *info; info = g_file_query_info (location, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (info) { /* Simply get the display name to use as the basename */ name = g_strdup (g_file_info_get_display_name (info)); g_object_unref (info); } else { /* This is a local file, and therefore we will use * g_filename_display_basename on the local path */ gchar *local_path; local_path = g_file_get_path (location); name = g_filename_display_basename (local_path); g_free (local_path); } } else if (g_file_has_parent (location, NULL) || !tepl_utils_decode_uri (uri, NULL, NULL, &hn, NULL, NULL)) { /* For remote files with a parent (so not just http://foo.com) or remote file for which the decoding of the host name fails, use the _parse_name and take basename of that */ gchar *parse_name; gchar *base; parse_name = g_file_get_parse_name (location); base = g_filename_display_basename (parse_name); name = g_uri_unescape_string (base, NULL); g_free (base); g_free (parse_name); } else { /* display '/ on ' using the decoded host */ gchar *hn_utf8; if (hn != NULL) { hn_utf8 = g_utf8_make_valid (hn, -1); } else { /* we should never get here */ hn_utf8 = g_strdup ("?"); } /* Translators: '/ on ' */ name = g_strdup_printf (_("/ on %s"), hn_utf8); g_free (hn_utf8); g_free (hn); } g_free (uri); return name; } /** * gedit_utils_drop_get_uris: * @selection_data: the #GtkSelectionData from drag_data_received * * Create a list of valid uri's from a uri-list drop. * * Returns: (transfer full): a string array which will hold the uris or * %NULL if there were no valid uris. g_strfreev should be used when * the string array is no longer used */ gchar ** gedit_utils_drop_get_uris (GtkSelectionData *selection_data) { gchar **uris; gint i; gint p = 0; gchar **uri_list; uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data)); uri_list = g_new0(gchar *, g_strv_length (uris) + 1); for (i = 0; uris[i] != NULL; i++) { gchar *uri; uri = make_canonical_uri_from_shell_arg (uris[i]); /* Silently ignore malformed URI/filename */ if (uri != NULL) uri_list[p++] = uri; } if (*uri_list == NULL) { g_free(uri_list); g_strfreev (uris); return NULL; } g_strfreev (uris); return uri_list; } GtkSourceCompressionType gedit_utils_get_compression_type_from_content_type (const gchar *content_type) { if (content_type == NULL) { return GTK_SOURCE_COMPRESSION_TYPE_NONE; } if (g_content_type_is_a (content_type, "application/x-gzip")) { return GTK_SOURCE_COMPRESSION_TYPE_GZIP; } return GTK_SOURCE_COMPRESSION_TYPE_NONE; } /* Copied from nautilus */ static gchar * get_direct_save_filename (GdkDragContext *context) { guchar *prop_text; gint prop_len; if (!gdk_property_get (gdk_drag_context_get_source_window (context), gdk_atom_intern ("XdndDirectSave0", FALSE), gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL, &prop_len, &prop_text) && prop_text != NULL) { return NULL; } /* Zero-terminate the string */ prop_text = g_realloc (prop_text, prop_len + 1); prop_text[prop_len] = '\0'; /* Verify that the file name provided by the source is valid */ if (*prop_text == '\0' || strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL) { gedit_debug_message (DEBUG_UTILS, "Invalid filename provided by XDS drag site"); g_free (prop_text); return NULL; } return (gchar *)prop_text; } gchar * gedit_utils_set_direct_save_filename (GdkDragContext *context) { gchar *uri; gchar *filename; uri = NULL; filename = get_direct_save_filename (context); if (filename != NULL) { gchar *tempdir; gchar *path; tempdir = g_dir_make_tmp ("gedit-drop-XXXXXX", NULL); if (tempdir == NULL) { tempdir = g_strdup (g_get_tmp_dir ()); } path = g_build_filename (tempdir, filename, NULL); uri = g_filename_to_uri (path, NULL, NULL); /* Change the property */ gdk_property_change (gdk_drag_context_get_source_window (context), gdk_atom_intern ("XdndDirectSave0", FALSE), gdk_atom_intern ("text/plain", FALSE), 8, GDK_PROP_MODE_REPLACE, (const guchar *) uri, strlen (uri)); g_free (tempdir); g_free (path); g_free (filename); } return uri; } const gchar * gedit_utils_newline_type_to_string (GtkSourceNewlineType newline_type) { switch (newline_type) { case GTK_SOURCE_NEWLINE_TYPE_LF: return _("Unix/Linux"); case GTK_SOURCE_NEWLINE_TYPE_CR: return _("Mac OS Classic"); case GTK_SOURCE_NEWLINE_TYPE_CR_LF: return _("Windows"); } return NULL; } /* ex:set ts=8 noet: */