summaryrefslogtreecommitdiffstats
path: root/gedit/gedit-io-error-info-bar.c
diff options
context:
space:
mode:
Diffstat (limited to 'gedit/gedit-io-error-info-bar.c')
-rw-r--r--gedit/gedit-io-error-info-bar.c542
1 files changed, 542 insertions, 0 deletions
diff --git a/gedit/gedit-io-error-info-bar.c b/gedit/gedit-io-error-info-bar.c
new file mode 100644
index 0000000..8acb332
--- /dev/null
+++ b/gedit/gedit-io-error-info-bar.c
@@ -0,0 +1,542 @@
+/*
+ * This file is part of gedit
+ *
+ * Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "gedit-io-error-info-bar.h"
+#include <tepl/tepl.h>
+#include <glib/gi18n.h>
+#include "gedit-encodings-combo-box.h"
+
+/* Verbose error reporting for file I/O operations (load, save, revert).
+ *
+ * For testing, not all I/O errors are easy to reproduce. Here is some
+ * documentation:
+ *
+ * G_IO_ERROR_NOT_REGULAR_FILE:
+ * Open for example /dev/null from the command line.
+ *
+ * G_IO_ERROR_IS_DIRECTORY:
+ * The easiest is from the command line.
+ *
+ * G_IO_ERROR_TIMED_OUT:
+ * For example configure your router to add an http(s) domain on the block list
+ * in the firewall. Then pass on the command line such an https:// address
+ * (normally GIO/GVfs is able to open https:// files) and wait 2 min.
+ * There are also probably online services that mock that behavior.
+ */
+
+static gboolean
+is_recoverable_error (const GError *error)
+{
+ if (error->domain == G_IO_ERROR)
+ {
+ switch (error->code)
+ {
+ case G_IO_ERROR_PERMISSION_DENIED:
+ case G_IO_ERROR_NOT_FOUND:
+ case G_IO_ERROR_HOST_NOT_FOUND:
+ case G_IO_ERROR_TIMED_OUT:
+ case G_IO_ERROR_NOT_MOUNTABLE_FILE:
+ case G_IO_ERROR_NOT_MOUNTED:
+ case G_IO_ERROR_BUSY:
+ return TRUE;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_gio_error (const GError *error,
+ gint code)
+{
+ return error->domain == G_IO_ERROR && error->code == code;
+}
+
+static GtkWidget *
+create_io_loading_error_info_bar (const gchar *primary_msg,
+ const gchar *secondary_msg,
+ gboolean recoverable_error)
+{
+ TeplInfoBar *info_bar;
+
+ info_bar = tepl_info_bar_new_simple (GTK_MESSAGE_ERROR,
+ primary_msg,
+ secondary_msg);
+
+ if (recoverable_error)
+ {
+ /* Since there are several buttons, don't use
+ * gtk_info_bar_set_show_close_button().
+ */
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ _("_Retry"),
+ GTK_RESPONSE_OK);
+
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ _("_Cancel"),
+ GTK_RESPONSE_CLOSE);
+ }
+ else
+ {
+ gtk_info_bar_set_show_close_button (GTK_INFO_BAR (info_bar), TRUE);
+ }
+
+ return GTK_WIDGET (info_bar);
+}
+
+static void
+get_detailed_error_messages (GFile *location,
+ const gchar *uri,
+ const GError *error,
+ gchar **primary_msg,
+ gchar **secondary_msg)
+{
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ *secondary_msg = g_strdup (_("File not found."));
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+ {
+ gchar *uri_scheme = NULL;
+
+ if (location != NULL)
+ {
+ uri_scheme = g_file_get_uri_scheme (location);
+ }
+
+ if (uri_scheme != NULL &&
+ g_utf8_validate (uri_scheme, -1, NULL))
+ {
+ /* How to reproduce this case: from the command line,
+ * try to open a URI such as: foo://example.net/file
+ */
+
+ /* Translators: %s is a URI scheme (like for example http:, ftp:, etc.) */
+ *secondary_msg = g_strdup_printf (_("“%s:” locations are not supported."),
+ uri_scheme);
+ }
+
+ g_free (uri_scheme);
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
+ {
+ *secondary_msg = g_strdup (_("The location of the file cannot be accessed."));
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME))
+ {
+ *primary_msg = g_strdup_printf (_("“%s” is not a valid location."), uri);
+ *secondary_msg = g_strdup (_("Please check that you typed the "
+ "location correctly and try again."));
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND))
+ {
+ /* How to reproduce this case: from the command line, try to
+ * open a URI such as: ssh://something-that-does-not-exist/file
+ *
+ * But this case is also hit for legitimate network addresses
+ * when the proxy is set up wrong.
+ */
+ gchar *file_uri = NULL;
+ gchar *hn = NULL;
+ gboolean uri_decoded = FALSE;
+
+ if (location != NULL)
+ {
+ file_uri = g_file_get_uri (location);
+ }
+
+ if (file_uri != NULL)
+ {
+ uri_decoded = tepl_utils_decode_uri (file_uri, NULL, NULL, &hn, NULL, NULL);
+ }
+
+ if (uri_decoded && hn != NULL)
+ {
+ gchar *host_name;
+ gchar *msg1;
+ const gchar *msg2;
+
+ host_name = g_utf8_make_valid (hn, -1);
+
+ msg1 = g_strdup_printf (_("Hostname “%s” not known."), host_name);
+ msg2 = _("The problem could come from the proxy settings.");
+
+ *secondary_msg = g_strconcat (msg1, "\n", msg2, NULL);
+
+ g_free (host_name);
+ g_free (msg1);
+ }
+
+ g_free (file_uri);
+ g_free (hn);
+ }
+
+ if (*primary_msg == NULL && *secondary_msg == NULL)
+ {
+ *secondary_msg = g_strdup (error->message);
+ }
+}
+
+GtkWidget *
+gedit_unrecoverable_reverting_error_info_bar_new (GFile *location,
+ const GError *error)
+{
+ gchar *uri;
+ gchar *primary_msg = NULL;
+ gchar *secondary_msg = NULL;
+ GtkWidget *info_bar;
+
+ g_return_val_if_fail (G_IS_FILE (location), NULL);
+ g_return_val_if_fail (error != NULL, NULL);
+
+ uri = g_file_get_parse_name (location);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ secondary_msg = g_strdup (_("File not found. "
+ "Perhaps it has recently been deleted."));
+ }
+ else
+ {
+ get_detailed_error_messages (location, uri, error, &primary_msg, &secondary_msg);
+ }
+
+ if (primary_msg == NULL)
+ {
+ primary_msg = g_strdup_printf (_("Could not revert the file “%s”."), uri);
+ }
+
+ info_bar = create_io_loading_error_info_bar (primary_msg,
+ secondary_msg,
+ FALSE);
+
+ g_free (uri);
+ g_free (primary_msg);
+ g_free (secondary_msg);
+ return info_bar;
+}
+
+static void
+add_encodings_combo_box (TeplInfoBar *info_bar)
+{
+ GtkWidget *hgrid;
+ gchar *label_markup;
+ GtkWidget *label;
+ GtkWidget *combo_box;
+
+ hgrid = gtk_grid_new ();
+ gtk_grid_set_column_spacing (GTK_GRID (hgrid), 6);
+
+ label_markup = g_strdup_printf ("<small>%s</small>",
+ _("Ch_aracter Encoding:"));
+ label = gtk_label_new_with_mnemonic (label_markup);
+ g_free (label_markup);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+
+ combo_box = gedit_encodings_combo_box_new (TRUE);
+ g_object_set_data (G_OBJECT (info_bar),
+ "gedit-info-bar-encoding-combo-box",
+ combo_box);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo_box);
+
+ gtk_container_add (GTK_CONTAINER (hgrid), label);
+ gtk_container_add (GTK_CONTAINER (hgrid), combo_box);
+ gtk_widget_show_all (hgrid);
+
+ tepl_info_bar_add_content_widget (info_bar, hgrid, TEPL_INFO_BAR_LOCATION_ALONGSIDE_ICON);
+}
+
+static GtkWidget *
+create_conversion_error_info_bar (const gchar *primary_msg,
+ const gchar *secondary_msg,
+ gboolean edit_anyway)
+{
+ GtkMessageType msg_type;
+ TeplInfoBar *info_bar;
+
+ msg_type = edit_anyway ? GTK_MESSAGE_WARNING : GTK_MESSAGE_ERROR;
+
+ info_bar = tepl_info_bar_new_simple (msg_type, primary_msg, secondary_msg);
+
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ _("_Retry"),
+ GTK_RESPONSE_OK);
+
+ if (edit_anyway)
+ {
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ _("_Edit Anyway"),
+ GTK_RESPONSE_YES);
+ }
+
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ _("_Cancel"),
+ GTK_RESPONSE_CLOSE);
+
+ add_encodings_combo_box (info_bar);
+
+ return GTK_WIDGET (info_bar);
+}
+
+GtkWidget *
+gedit_io_loading_error_info_bar_new (GFile *location,
+ const GtkSourceEncoding *encoding,
+ const GError *error)
+{
+ gchar *uri;
+ gchar *primary_msg = NULL;
+ gchar *secondary_msg = NULL;
+ gboolean edit_anyway = FALSE;
+ gboolean convert_error = FALSE;
+ GtkWidget *info_bar;
+
+ g_return_val_if_fail (error != NULL, NULL);
+
+ if (location != NULL)
+ {
+ uri = g_file_get_parse_name (location);
+ }
+ else
+ {
+ uri = g_strdup ("stdin");
+ }
+
+ if (is_gio_error (error, G_IO_ERROR_TOO_MANY_LINKS))
+ {
+ secondary_msg = g_strdup (_("The number of followed links is limited and the actual "
+ "file could not be found within this limit."));
+ }
+ else if (is_gio_error (error, G_IO_ERROR_PERMISSION_DENIED))
+ {
+ secondary_msg = g_strdup (_("You do not have the permissions necessary to open the file."));
+ }
+ else if ((is_gio_error (error, G_IO_ERROR_INVALID_DATA) && encoding == NULL) ||
+ (error->domain == GTK_SOURCE_FILE_LOADER_ERROR &&
+ error->code == GTK_SOURCE_FILE_LOADER_ERROR_ENCODING_AUTO_DETECTION_FAILED))
+ {
+ secondary_msg = g_strconcat (_("Unable to detect the character encoding."), "\n",
+ _("Please check that you are not trying to open a binary file."), "\n",
+ _("Select a character encoding from the menu and try again."), NULL);
+ convert_error = TRUE;
+ }
+ else if (error->domain == GTK_SOURCE_FILE_LOADER_ERROR &&
+ error->code == GTK_SOURCE_FILE_LOADER_ERROR_CONVERSION_FALLBACK)
+ {
+ primary_msg = g_strdup_printf (_("There was a problem opening the file “%s”."), uri);
+ secondary_msg = g_strconcat (_("The file you opened has some invalid characters. "
+ "If you continue editing this file you could corrupt this "
+ "document."), "\n",
+ _("You can also choose another character encoding and try again."),
+ NULL);
+ edit_anyway = TRUE;
+ convert_error = TRUE;
+ }
+ else if (is_gio_error (error, G_IO_ERROR_INVALID_DATA) && encoding != NULL)
+ {
+ gchar *encoding_name = gtk_source_encoding_to_string (encoding);
+
+ primary_msg = g_strdup_printf (_("Could not open the file “%s” using the “%s” character encoding."),
+ uri,
+ encoding_name);
+ secondary_msg = g_strconcat (_("Please check that you are not trying to open a binary file."), "\n",
+ _("Select a different character encoding from the menu and try again."), NULL);
+ convert_error = TRUE;
+
+ g_free (encoding_name);
+ }
+ else
+ {
+ get_detailed_error_messages (location, uri, error, &primary_msg, &secondary_msg);
+ }
+
+ if (primary_msg == NULL)
+ {
+ primary_msg = g_strdup_printf (_("Could not open the file “%s”."), uri);
+ }
+
+ if (convert_error)
+ {
+ info_bar = create_conversion_error_info_bar (primary_msg,
+ secondary_msg,
+ edit_anyway);
+ }
+ else
+ {
+ info_bar = create_io_loading_error_info_bar (primary_msg,
+ secondary_msg,
+ is_recoverable_error (error));
+ }
+
+ g_free (uri);
+ g_free (primary_msg);
+ g_free (secondary_msg);
+ return info_bar;
+}
+
+GtkWidget *
+gedit_conversion_error_while_saving_info_bar_new (GFile *location,
+ const GtkSourceEncoding *encoding)
+{
+ gchar *uri;
+ gchar *encoding_name;
+ gchar *primary_msg = NULL;
+ gchar *secondary_msg = NULL;
+ GtkWidget *info_bar;
+
+ g_return_val_if_fail (G_IS_FILE (location), NULL);
+ g_return_val_if_fail (encoding != NULL, NULL);
+
+ uri = g_file_get_parse_name (location);
+ encoding_name = gtk_source_encoding_to_string (encoding);
+
+ primary_msg = g_strdup_printf (_("Could not save the file “%s” using the “%s” character encoding."),
+ uri,
+ encoding_name);
+ secondary_msg = g_strconcat (_("The document contains one or more characters that cannot be encoded "
+ "using the specified character encoding."), "\n",
+ _("Select a different character encoding from the menu and try again."), NULL);
+
+ info_bar = create_conversion_error_info_bar (primary_msg,
+ secondary_msg,
+ FALSE);
+
+ g_free (uri);
+ g_free (encoding_name);
+ g_free (primary_msg);
+ g_free (secondary_msg);
+ return info_bar;
+}
+
+const GtkSourceEncoding *
+gedit_conversion_error_info_bar_get_encoding (GtkWidget *info_bar)
+{
+ gpointer combo_box;
+
+ g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
+
+ combo_box = g_object_get_data (G_OBJECT (info_bar),
+ "gedit-info-bar-encoding-combo-box");
+ if (combo_box != NULL)
+ {
+ return gedit_encodings_combo_box_get_selected_encoding (GEDIT_ENCODINGS_COMBO_BOX (combo_box));
+ }
+
+ return NULL;
+}
+
+GtkWidget *
+gedit_unrecoverable_saving_error_info_bar_new (GFile *location,
+ const GError *error)
+{
+ gchar *uri;
+ gchar *primary_msg = NULL;
+ gchar *secondary_msg = NULL;
+ TeplInfoBar *info_bar;
+
+ g_return_val_if_fail (G_IS_FILE (location), NULL);
+ g_return_val_if_fail (error != NULL, NULL);
+
+ uri = g_file_get_parse_name (location);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+ {
+ gchar *uri_scheme = g_file_get_uri_scheme (location);
+
+ if (uri_scheme != NULL &&
+ g_utf8_validate (uri_scheme, -1, NULL))
+ {
+ /* Translators: %s is a URI scheme (like for example http:, ftp:, etc.) */
+ secondary_msg = g_strdup_printf (_("Cannot handle “%s:” locations in write mode. "
+ "Please check that you typed the "
+ "location correctly and try again."),
+ uri_scheme);
+ }
+ else
+ {
+ secondary_msg = g_strdup (_("Cannot handle this location in write mode. "
+ "Please check that you typed the "
+ "location correctly and try again."));
+ }
+
+ g_free (uri_scheme);
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME))
+ {
+ secondary_msg = g_strdup_printf (_("“%s” is not a valid location. "
+ "Please check that you typed the "
+ "location correctly and try again."),
+ uri);
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
+ {
+ secondary_msg = g_strdup (_("You do not have the permissions necessary to save the file. "
+ "Please check that you typed the "
+ "location correctly and try again."));
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE))
+ {
+ secondary_msg = g_strdup (_("There is not enough disk space to save the file. "
+ "Please free some disk space and try again."));
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_READ_ONLY))
+ {
+ secondary_msg = g_strdup (_("You are trying to save the file on a read-only disk. "
+ "Please check that you typed the location "
+ "correctly and try again."));
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ secondary_msg = g_strdup (_("A file with the same name already exists. "
+ "Please use a different name."));
+ }
+ else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FILENAME_TOO_LONG))
+ {
+ secondary_msg = g_strdup (_("The disk where you are trying to save the file has "
+ "a limitation on length of the file names. "
+ "Please use a shorter name."));
+ }
+ else
+ {
+ get_detailed_error_messages (location,
+ uri,
+ error,
+ &primary_msg,
+ &secondary_msg);
+ }
+
+ if (primary_msg == NULL)
+ {
+ primary_msg = g_strdup_printf (_("Could not save the file “%s”."), uri);
+ }
+
+ info_bar = tepl_info_bar_new_simple (GTK_MESSAGE_ERROR, primary_msg, secondary_msg);
+ gtk_info_bar_set_show_close_button (GTK_INFO_BAR (info_bar), TRUE);
+
+ g_free (uri);
+ g_free (primary_msg);
+ g_free (secondary_msg);
+ return GTK_WIDGET (info_bar);
+}
+
+/* ex:set ts=8 noet: */