[gedit] Add invalid char support.
- From: Ignacio Casal Quinteiro <icq src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit] Add invalid char support.
- Date: Sat, 29 Jan 2011 17:54:44 +0000 (UTC)
commit dc31390ad2d252082d555f57f65a80f3da7af027
Author: Ignacio Casal Quinteiro <icq gnome org>
Date: Sun Dec 12 16:04:09 2010 +0100
Add invalid char support.
https://bugzilla.gnome.org/show_bug.cgi?id=502947
gedit/gedit-document-loader.c | 25 ++--
gedit/gedit-document-output-stream.c | 207 ++++++++++++++++++++++------------
gedit/gedit-document.c | 151 ++++++++++++++++++++-----
gedit/gedit-document.h | 8 +-
gedit/gedit-io-error-info-bar.c | 104 +++++++++++++++--
gedit/gedit-io-error-info-bar.h | 2 +
gedit/gedit-tab.c | 83 +++++++++++---
tests/document-output-stream.c | 17 ++-
8 files changed, 456 insertions(+), 141 deletions(-)
---
diff --git a/gedit/gedit-document-loader.c b/gedit/gedit-document-loader.c
index fb30151..0371af0 100644
--- a/gedit/gedit-document-loader.c
+++ b/gedit/gedit-document-loader.c
@@ -491,6 +491,18 @@ close_input_stream_ready_cb (GInputStream *stream,
return;
}
+ /* Check if we needed some fallback char, if so, check if there was
+ a previous error and if not set a fallback used error */
+ if ((gedit_document_output_stream_get_num_fallbacks (GEDIT_DOCUMENT_OUTPUT_STREAM (async->loader->priv->output)) != 0) &&
+ async->loader->priv->error == NULL)
+ {
+ g_set_error_literal (&async->loader->priv->error,
+ GEDIT_DOCUMENT_ERROR,
+ GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK,
+ "There was a conversion error and it was "
+ "needed to use a fallback char");
+ }
+
loader_load_completed_or_failed (async->loader, async);
}
@@ -621,18 +633,7 @@ async_read_cb (GInputStream *stream,
loader->priv->auto_detected_newline_type =
gedit_document_output_stream_detect_newline_type (GEDIT_DOCUMENT_OUTPUT_STREAM (loader->priv->output));
- /* Check if we needed some fallback char, if so, check if there was
- a previous error and if not set a fallback used error */
- /* FIXME Uncomment this when we want to manage conversion fallback */
- /*if ((gedit_document_output_stream_get_num_fallbacks (GEDIT_DOCUMENT_OUTPUT_STREAM (loader->priv->output)) != 0) &&
- loader->priv->error == NULL)
- {
- g_set_error_literal (&loader->priv->error,
- GEDIT_DOCUMENT_ERROR,
- GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK,
- "There was a conversion error and it was "
- "needed to use a fallback char");
- }*/
+
write_complete (async);
diff --git a/gedit/gedit-document-output-stream.c b/gedit/gedit-document-output-stream.c
index 52d7fad..02e6c9d 100644
--- a/gedit/gedit-document-output-stream.c
+++ b/gedit/gedit-document-output-stream.c
@@ -57,6 +57,9 @@ struct _GeditDocumentOutputStreamPrivate
GSList *encodings;
GSList *current_encoding;
+ gint error_offset;
+ guint n_fallback_errors;
+
guint is_utf8 : 1;
guint use_first : 1;
@@ -221,6 +224,8 @@ gedit_document_output_stream_init (GeditDocumentOutputStream *stream)
stream->priv->encodings = NULL;
stream->priv->current_encoding = NULL;
+ stream->priv->error_offset = -1;
+
stream->priv->is_initialized = FALSE;
stream->priv->is_closed = FALSE;
stream->priv->is_utf8 = FALSE;
@@ -244,7 +249,10 @@ get_encoding (GeditDocumentOutputStream *stream)
return (const GeditEncoding *)stream->priv->current_encoding->data;
}
- return NULL;
+ stream->priv->use_first = TRUE;
+ stream->priv->current_encoding = stream->priv->encodings;
+
+ return (const GeditEncoding *)stream->priv->current_encoding->data;
}
static gboolean
@@ -497,77 +505,135 @@ gedit_document_output_stream_get_guessed (GeditDocumentOutputStream *stream)
guint
gedit_document_output_stream_get_num_fallbacks (GeditDocumentOutputStream *stream)
{
- g_return_val_if_fail (GEDIT_IS_DOCUMENT_OUTPUT_STREAM (stream), FALSE);
+ g_return_val_if_fail (GEDIT_IS_DOCUMENT_OUTPUT_STREAM (stream), 0);
+
+ return stream->priv->n_fallback_errors;
+}
- if (stream->priv->charset_conv == NULL)
+static void
+apply_error_tag (GeditDocumentOutputStream *stream)
+{
+ GtkTextIter start;
+
+ if (stream->priv->error_offset == -1)
{
- return FALSE;
+ return;
}
- return g_charset_converter_get_num_fallbacks (stream->priv->charset_conv) != 0;
+ gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (stream->priv->doc),
+ &start, stream->priv->error_offset);
+
+ _gedit_document_apply_error_style (stream->priv->doc,
+ &start, &stream->priv->pos);
+
+ stream->priv->error_offset = -1;
}
-static gboolean
+static void
+insert_fallback (GeditDocumentOutputStream *stream,
+ const gchar *buffer)
+{
+ guint8 out[4];
+ guint8 v;
+ const gchar hex[] = "0123456789ABCDEF";
+
+ /* if we are here is because we are pointing to an invalid char
+ * so we substitute it by an hex value */
+ v = *(guint8 *)buffer;
+ out[0] = '\\';
+ out[1] = hex[(v & 0xf0) >> 4];
+ out[2] = hex[(v & 0x0f) >> 0];
+ out[3] = '\0';
+
+ gtk_text_buffer_insert (GTK_TEXT_BUFFER (stream->priv->doc),
+ &stream->priv->pos, (const gchar *)out, 3);
+
+ stream->priv->n_fallback_errors++;
+}
+
+static void
validate_and_insert (GeditDocumentOutputStream *stream,
const gchar *buffer,
gsize count)
{
- const gchar *end;
- gsize nvalid;
- gboolean valid;
+ GtkTextBuffer *text_buffer;
+ GtkTextIter *iter;
gsize len;
+ text_buffer = GTK_TEXT_BUFFER (stream->priv->doc);
+ iter = &stream->priv->pos;
len = count;
- /* validate */
- valid = g_utf8_validate (buffer, len, &end);
- nvalid = end - buffer;
-
- if (!valid)
+ while (len != 0)
{
- gsize remainder;
+ const gchar *end;
+ gboolean valid;
+ gsize nvalid;
- remainder = len - nvalid;
+ /* validate */
+ valid = g_utf8_validate (buffer, len, &end);
+ nvalid = end - buffer;
- if ((remainder < MAX_UNICHAR_LEN) &&
- (g_utf8_get_char_validated (buffer + nvalid, remainder) == (gunichar)-2))
+ /* Note: this is a workaround for a 'bug' in GtkTextBuffer where
+ inserting first a \r and then in a second insert, a \n,
+ will result in two lines being added instead of a single
+ one */
+
+ if (valid)
{
- stream->priv->buffer = g_strndup (end, remainder);
- stream->priv->buflen = remainder;
- len -= remainder;
+ gchar *ptr;
+
+ ptr = g_utf8_find_prev_char (buffer, buffer + len);
+
+ if (ptr && *ptr == '\r' && ptr - buffer == len - 1)
+ {
+ stream->priv->buffer = g_new (gchar, 1);
+ stream->priv->buffer[0] = '\r';
+ stream->priv->buflen = 1;
+
+ /* Decrease also the len so in the check
+ nvalid == len we get out of this method */
+ --nvalid;
+ --len;
+ }
}
- else
+
+ /* if we've got any valid char we must tag the invalid chars */
+ if (nvalid > 0)
{
- return FALSE;
+ apply_error_tag (stream);
}
- }
- else
- {
- gchar *ptr;
- /* Note: this is a workaround for a 'bug' in GtkTextBuffer where
- inserting first a \r and then in a second insert, a \n,
- will result in two lines being added instead of a single
- one */
+ gtk_text_buffer_insert (text_buffer, iter, buffer, nvalid);
- ptr = g_utf8_find_prev_char (buffer, buffer + len);
+ /* If we inserted all return */
+ if (nvalid == len)
+ {
+ break;
+ }
+
+ buffer += nvalid;
+ len = len - nvalid;
- if (ptr && *ptr == '\r' && ptr - buffer == len - 1)
+ if ((len < MAX_UNICHAR_LEN) &&
+ (g_utf8_get_char_validated (buffer, len) == (gunichar)-2))
{
- stream->priv->buffer = g_new (gchar, 1);
- stream->priv->buffer[0] = '\r';
- stream->priv->buflen = 1;
+ stream->priv->buffer = g_strndup (end, len);
+ stream->priv->buflen = len;
- --len;
+ break;
}
- }
- gtk_text_buffer_insert (GTK_TEXT_BUFFER (stream->priv->doc),
- &stream->priv->pos,
- buffer,
- len);
+ /* we need the start of the chunk of invalid chars */
+ if (stream->priv->error_offset == -1)
+ {
+ stream->priv->error_offset = gtk_text_iter_get_offset (&stream->priv->pos);
+ }
- return TRUE;
+ insert_fallback (stream, buffer);
+ buffer++;
+ len--;
+ }
}
/* If the last char is a newline, remove it from the buffer (otherwise
@@ -775,21 +841,7 @@ gedit_document_output_stream_write (GOutputStream *stream,
freetext = TRUE;
}
- if (!validate_and_insert (ostream, text, len))
- {
- /* TODO: we could escape invalid text and tag it in red
- * and make the doc readonly.
- */
- g_set_error (error, G_IO_ERROR,
- G_IO_ERROR_INVALID_DATA,
- _("Invalid UTF-8 sequence in input"));
-
- if (freetext)
- {
- g_free (text);
- }
- return -1;
- }
+ validate_and_insert (ostream, text, len);
if (freetext)
{
@@ -813,12 +865,33 @@ gedit_document_output_stream_flush (GOutputStream *stream,
return TRUE;
}
- if (ostream->priv->buflen == 0)
+ if (ostream->priv->buflen > 0 && *ostream->priv->buffer != '\r')
{
- return TRUE;
+ /* If we reached here is because the last insertion was a half
+ correct char, which has to be inserted as fallback */
+ gchar *text;
+
+ if (ostream->priv->error_offset == -1)
+ {
+ ostream->priv->error_offset = gtk_text_iter_get_offset (&ostream->priv->pos);
+ }
+
+ text = ostream->priv->buffer;
+ while (ostream->priv->buflen != 0)
+ {
+ insert_fallback (ostream, text);
+ text++;
+ ostream->priv->buflen--;
+ }
+
+ g_free (ostream->priv->buffer);
+ ostream->priv->buffer = NULL;
}
else if (ostream->priv->buflen == 1 && *ostream->priv->buffer == '\r')
{
+ /* The previous chars can be invalid */
+ apply_error_tag (ostream);
+
/* See special case above, flush this */
gtk_text_buffer_insert (GTK_TEXT_BUFFER (ostream->priv->doc),
&ostream->priv->pos,
@@ -828,19 +901,11 @@ gedit_document_output_stream_flush (GOutputStream *stream,
g_free (ostream->priv->buffer);
ostream->priv->buffer = NULL;
ostream->priv->buflen = 0;
-
- return TRUE;
}
- else
- {
- /* Conversion error */
- g_set_error (error,
- G_IO_ERROR,
- G_IO_ERROR_INVALID_DATA,
- _("Incomplete UTF-8 sequence in input"));
- return FALSE;
- }
+ apply_error_tag (ostream);
+
+ return TRUE;
}
static gboolean
diff --git a/gedit/gedit-document.c b/gedit/gedit-document.c
index 0f84a2f..ae2783c 100644
--- a/gedit/gedit-document.c
+++ b/gedit/gedit-document.c
@@ -138,15 +138,17 @@ struct _GeditDocumentPrivate
GeditTextRegion *to_search_region;
GtkTextTag *found_tag;
+ GtkTextTag *error_tag;
+
/* Mount operation factory */
GeditMountOperationFactory mount_operation_factory;
gpointer mount_operation_userdata;
- gint readonly : 1;
- gint last_save_was_manually : 1;
- gint language_set_by_user : 1;
- gint stop_cursor_moved_emission : 1;
- gint dispose_has_run : 1;
+ guint readonly : 1;
+ guint last_save_was_manually : 1;
+ guint language_set_by_user : 1;
+ guint stop_cursor_moved_emission : 1;
+ guint dispose_has_run : 1;
};
enum {
@@ -1336,15 +1338,15 @@ set_readonly (GeditDocument *doc,
}
/**
- * gedit_document_set_readonly:
+ * _gedit_document_set_readonly:
* @doc: a #GeditDocument
- * @readonly: %TRUE to se the document as read-only
+ * @readonly: %TRUE to set the document as read-only
*
* If @readonly is %TRUE sets @doc as read-only.
*/
void
_gedit_document_set_readonly (GeditDocument *doc,
- gboolean readonly)
+ gboolean readonly)
{
gedit_debug (DEBUG_DOCUMENT);
@@ -1704,6 +1706,29 @@ gedit_document_load_cancel (GeditDocument *doc)
return gedit_document_loader_cancel (doc->priv->loader);
}
+static gboolean
+has_invalid_chars (GeditDocument *doc)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+
+ g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), FALSE);
+
+ gedit_debug (DEBUG_DOCUMENT);
+
+ buffer = GTK_TEXT_BUFFER (doc);
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+
+ if (gtk_text_iter_begins_tag (&start, doc->priv->error_tag) ||
+ gtk_text_iter_forward_to_tag_toggle (&start, doc->priv->error_tag))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
document_saver_saving (GeditDocumentSaver *saver,
gboolean completed,
@@ -1790,25 +1815,42 @@ gedit_document_save_real (GeditDocument *doc,
{
g_return_if_fail (doc->priv->saver == NULL);
- /* create a saver, it will be destroyed once saving is complete */
- doc->priv->saver = gedit_document_saver_new (doc,
- location,
- encoding,
- newline_type,
- compression_type,
- flags);
-
- g_signal_connect (doc->priv->saver,
- "saving",
- G_CALLBACK (document_saver_saving),
- doc);
+ if (!(flags & GEDIT_DOCUMENT_SAVE_IGNORE_INVALID_CHARS) && has_invalid_chars (doc))
+ {
+ GError *error = NULL;
- doc->priv->requested_encoding = encoding;
- doc->priv->newline_type = newline_type;
- doc->priv->compression_type = compression_type;
+ g_set_error_literal (&error,
+ GEDIT_DOCUMENT_ERROR,
+ GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK,
+ "The document contains invalid characters");
+
+ g_signal_emit (doc,
+ document_signals[SAVED],
+ 0,
+ error);
+ }
+ else
+ {
+ /* create a saver, it will be destroyed once saving is complete */
+ doc->priv->saver = gedit_document_saver_new (doc,
+ location,
+ encoding,
+ newline_type,
+ compression_type,
+ flags);
+
+ g_signal_connect (doc->priv->saver,
+ "saving",
+ G_CALLBACK (document_saver_saving),
+ doc);
+
+ doc->priv->requested_encoding = encoding;
+ doc->priv->newline_type = newline_type;
+ doc->priv->compression_type = compression_type;
- gedit_document_saver_save (doc->priv->saver,
- &doc->priv->mtime);
+ gedit_document_saver_save (doc->priv->saver,
+ &doc->priv->mtime);
+ }
}
/**
@@ -1855,10 +1897,20 @@ gedit_document_save_as (GeditDocument *doc,
GeditDocumentCompressionType compression_type,
GeditDocumentSaveFlags flags)
{
+ GError *error = NULL;
+
g_return_if_fail (GEDIT_IS_DOCUMENT (doc));
g_return_if_fail (G_IS_FILE (location));
g_return_if_fail (encoding != NULL);
+ if (has_invalid_chars (doc))
+ {
+ g_set_error_literal (&error,
+ GEDIT_DOCUMENT_ERROR,
+ GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK,
+ "The document contains invalid chars");
+ }
+
/* priv->mtime refers to the the old location (if any). Thus, it should be
* ignored when saving as. */
g_signal_emit (doc,
@@ -1868,10 +1920,11 @@ gedit_document_save_as (GeditDocument *doc,
encoding,
newline_type,
compression_type,
- flags | GEDIT_DOCUMENT_SAVE_IGNORE_MTIME);
+ flags | GEDIT_DOCUMENT_SAVE_IGNORE_MTIME,
+ error);
}
-gboolean
+gboolean
gedit_document_is_untouched (GeditDocument *doc)
{
g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), TRUE);
@@ -3100,4 +3153,48 @@ gedit_document_set_metadata (GeditDocument *doc,
}
#endif
+static void
+sync_error_tag (GeditDocument *doc,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ sync_tag_style (doc, doc->priv->error_tag, "def:error");
+}
+
+void
+_gedit_document_apply_error_style (GeditDocument *doc,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ GtkTextBuffer *buffer;
+
+ gedit_debug (DEBUG_DOCUMENT);
+
+ buffer = GTK_TEXT_BUFFER (doc);
+
+ if (doc->priv->error_tag == NULL)
+ {
+ doc->priv->error_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (doc),
+ "invalid-char-style",
+ NULL);
+
+ sync_error_tag (doc, NULL, NULL);
+
+ g_signal_connect (doc,
+ "notify::style-scheme",
+ G_CALLBACK (sync_error_tag),
+ NULL);
+ }
+
+ /* make sure the 'error' tag has the priority over
+ * syntax highlighting tags */
+ text_tag_set_highest_priority (doc->priv->error_tag,
+ GTK_TEXT_BUFFER (doc));
+
+ gtk_text_buffer_apply_tag (buffer,
+ doc->priv->error_tag,
+ start,
+ end);
+}
+
/* ex:set ts=8 noet: */
diff --git a/gedit/gedit-document.h b/gedit/gedit-document.h
index 1be3a27..70c69cd 100644
--- a/gedit/gedit-document.h
+++ b/gedit/gedit-document.h
@@ -109,7 +109,8 @@ typedef enum
{
GEDIT_DOCUMENT_SAVE_IGNORE_MTIME = 1 << 0,
GEDIT_DOCUMENT_SAVE_IGNORE_BACKUP = 1 << 1,
- GEDIT_DOCUMENT_SAVE_PRESERVE_BACKUP = 1 << 2
+ GEDIT_DOCUMENT_SAVE_PRESERVE_BACKUP = 1 << 2,
+ GEDIT_DOCUMENT_SAVE_IGNORE_INVALID_CHARS= 1 << 3
} GeditDocumentSaveFlags;
/* Private structure type */
@@ -323,6 +324,11 @@ void _gedit_document_set_readonly (GeditDocument *doc,
glong _gedit_document_get_seconds_since_last_save_or_load
(GeditDocument *doc);
+void _gedit_document_apply_error_style
+ (GeditDocument *doc,
+ GtkTextIter *start,
+ GtkTextIter *end);
+
/* Note: this is a sync stat: use only on local files */
gboolean _gedit_document_check_externally_modified
(GeditDocument *doc);
diff --git a/gedit/gedit-io-error-info-bar.c b/gedit/gedit-io-error-info-bar.c
index ac8dba5..531d758 100644
--- a/gedit/gedit-io-error-info-bar.c
+++ b/gedit/gedit-io-error-info-bar.c
@@ -505,23 +505,19 @@ create_conversion_error_info_bar (const gchar *primary_text,
different from other main menu access keys (Open, Edit, View...) */
_("Edit Any_way"),
GTK_RESPONSE_YES);
- gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
- /* Translators: the access key chosen for this string should be
- different from other main menu access keys (Open, Edit, View...) */
- _("D_on't Edit"),
- GTK_RESPONSE_NO);
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
GTK_MESSAGE_WARNING);
}
else
{
- gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
- GTK_STOCK_CANCEL,
- GTK_RESPONSE_CANCEL);
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
GTK_MESSAGE_ERROR);
}
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL);
+
hbox_content = gtk_hbox_new (FALSE, 8);
image = gtk_image_new_from_stock ("gtk-dialog-error", GTK_ICON_SIZE_DIALOG);
@@ -631,8 +627,8 @@ gedit_io_loading_error_info_bar_new (GFile *location,
error_message = g_strdup_printf (_("There was a problem opening the file %s."),
uri_for_display);
message_details = g_strconcat (_("The file you opened has some invalid characters. "
- "If you continue editing this file you could make this "
- "document useless."), "\n",
+ "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;
@@ -1225,4 +1221,92 @@ gedit_externally_modified_info_bar_new (GFile *location,
return info_bar;
}
+GtkWidget *
+gedit_invalid_character_info_bar_new (GFile *location)
+{
+ GtkWidget *info_bar;
+ GtkWidget *hbox_content;
+ GtkWidget *image;
+ GtkWidget *vbox;
+ GtkWidget *primary_label;
+ GtkWidget *secondary_label;
+ gchar *primary_markup;
+ gchar *secondary_markup;
+ gchar *primary_text;
+ gchar *full_formatted_uri;
+ gchar *uri_for_display;
+ gchar *temp_uri_for_display;
+ const gchar *secondary_text;
+
+ g_return_val_if_fail (G_IS_FILE (location), NULL);
+
+ full_formatted_uri = g_file_get_parse_name (location);
+
+ /* Truncate the URI so it doesn't get insanely wide. Note that even
+ * though the dialog uses wrapped text, if the URI doesn't contain
+ * white space then the text-wrapping code is too stupid to wrap it.
+ */
+ temp_uri_for_display = gedit_utils_str_middle_truncate (full_formatted_uri,
+ MAX_URI_IN_DIALOG_LENGTH);
+ g_free (full_formatted_uri);
+
+ uri_for_display = g_markup_printf_escaped ("<i>%s</i>", temp_uri_for_display);
+ g_free (temp_uri_for_display);
+
+ info_bar = gtk_info_bar_new ();
+
+ info_bar_add_stock_button_with_text (GTK_INFO_BAR (info_bar),
+ _("S_ave Anyway"),
+ GTK_STOCK_SAVE,
+ GTK_RESPONSE_YES);
+ gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
+ _("D_on't Save"),
+ GTK_RESPONSE_CANCEL);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
+ GTK_MESSAGE_WARNING);
+
+ hbox_content = gtk_hbox_new (FALSE, 8);
+
+ image = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox_content), vbox, TRUE, TRUE, 0);
+
+ primary_text = g_strdup_printf (_("Some invalid chars have been detected while saving %s"),
+ uri_for_display);
+
+ g_free (uri_for_display);
+
+ primary_markup = g_strdup_printf ("<b>%s</b>", primary_text);
+ g_free (primary_text);
+ primary_label = gtk_label_new (primary_markup);
+ g_free (primary_markup);
+ gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
+ gtk_widget_set_can_focus (primary_label, TRUE);
+ gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE);
+
+ secondary_text = _("If you continue saving this file you can corrupt the document. "
+ " Save anyway?");
+ secondary_markup = g_strdup_printf ("<small>%s</small>",
+ secondary_text);
+ secondary_label = gtk_label_new (secondary_markup);
+ g_free (secondary_markup);
+ gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0);
+ gtk_widget_set_can_focus (secondary_label, TRUE);
+ gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (secondary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5);
+
+ gtk_widget_show_all (hbox_content);
+ set_contents (info_bar, hbox_content);
+
+ return info_bar;
+}
+
/* ex:set ts=8 noet: */
diff --git a/gedit/gedit-io-error-info-bar.h b/gedit/gedit-io-error-info-bar.h
index d91e4ab..ecf5ff6 100644
--- a/gedit/gedit-io-error-info-bar.h
+++ b/gedit/gedit-io-error-info-bar.h
@@ -63,6 +63,8 @@ GtkWidget *gedit_unrecoverable_saving_error_info_bar_new (GFile *
GtkWidget *gedit_externally_modified_info_bar_new (GFile *location,
gboolean document_modified);
+GtkWidget *gedit_invalid_character_info_bar_new (GFile *location);
+
G_END_DECLS
#endif /* __GEDIT_IO_ERROR_INFO_BAR_H__ */
diff --git a/gedit/gedit-tab.c b/gedit/gedit-tab.c
index 5443868..dc51748 100644
--- a/gedit/gedit-tab.c
+++ b/gedit/gedit-tab.c
@@ -597,11 +597,9 @@ io_loading_error_info_bar_response (GtkWidget *info_bar,
break;
case GTK_RESPONSE_YES:
/* This means that we want to edit the document anyway */
- set_info_bar (tab, NULL);
- _gedit_document_set_readonly (doc, FALSE);
- break;
- case GTK_RESPONSE_NO:
- /* We don't want to edit the document just show it */
+ tab->priv->not_editable = FALSE;
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (view),
+ TRUE);
set_info_bar (tab, NULL);
break;
default:
@@ -1067,11 +1065,13 @@ document_loaded (GeditDocument *document,
{
GtkWidget *emsg;
- _gedit_document_set_readonly (document, TRUE);
+ /* Set the tab as not editable as we have an error, the
+ user can decide to make it editable again */
+ tab->priv->not_editable = TRUE;
emsg = gedit_io_loading_error_info_bar_new (location,
- tab->priv->tmp_encoding,
- error);
+ tab->priv->tmp_encoding,
+ error);
set_info_bar (tab, emsg);
@@ -1208,8 +1208,8 @@ end_saving (GeditTab *tab)
static void
unrecoverable_saving_error_info_bar_response (GtkWidget *info_bar,
- gint response_id,
- GeditTab *tab)
+ gint response_id,
+ GeditTab *tab)
{
GeditView *view;
@@ -1228,6 +1228,41 @@ unrecoverable_saving_error_info_bar_response (GtkWidget *info_bar,
}
static void
+invalid_character_info_bar_response (GtkWidget *info_bar,
+ gint response_id,
+ GeditTab *tab)
+{
+ if (response_id == GTK_RESPONSE_YES)
+ {
+ GeditDocument *doc;
+
+ doc = gedit_tab_get_document (tab);
+ g_return_if_fail (GEDIT_IS_DOCUMENT (doc));
+
+ set_info_bar (tab, NULL);
+
+ g_return_if_fail (tab->priv->tmp_save_location != NULL);
+ g_return_if_fail (tab->priv->tmp_encoding != NULL);
+
+ gedit_tab_set_state (tab, GEDIT_TAB_STATE_SAVING);
+
+ /* don't bug the user again with this... */
+ tab->priv->save_flags |= GEDIT_DOCUMENT_SAVE_IGNORE_INVALID_CHARS;
+
+ g_return_if_fail (tab->priv->auto_save_timeout <= 0);
+
+ /* Force saving */
+ gedit_document_save (doc, tab->priv->save_flags);
+ }
+ else
+ {
+ unrecoverable_saving_error_info_bar_response (info_bar,
+ response_id,
+ tab);
+ }
+}
+
+static void
no_backup_error_info_bar_response (GtkWidget *info_bar,
gint response_id,
GeditTab *tab)
@@ -1357,9 +1392,12 @@ document_saved (GeditDocument *document,
g_return_if_fail (tab->priv->tmp_save_location != NULL);
g_return_if_fail (tab->priv->tmp_encoding != NULL);
g_return_if_fail (tab->priv->auto_save_timeout <= 0);
-
- g_timer_destroy (tab->priv->timer);
- tab->priv->timer = NULL;
+
+ if (tab->priv->timer != NULL)
+ {
+ g_timer_destroy (tab->priv->timer);
+ tab->priv->timer = NULL;
+ }
tab->priv->times_called = 0;
set_info_bar (tab, NULL);
@@ -1402,6 +1440,21 @@ document_saved (GeditDocument *document,
G_CALLBACK (no_backup_error_info_bar_response),
tab);
}
+ else if (error->domain == GEDIT_DOCUMENT_ERROR &&
+ error->code == GEDIT_DOCUMENT_ERROR_CONVERSION_FALLBACK)
+ {
+ /* If we have any invalid char in the document we must warn the user
+ as it can make the document useless if it is saved */
+ emsg = gedit_invalid_character_info_bar_new (tab->priv->tmp_save_location);
+ g_return_if_fail (emsg != NULL);
+
+ set_info_bar (tab, emsg);
+
+ g_signal_connect (emsg,
+ "response",
+ G_CALLBACK (invalid_character_info_bar_response),
+ tab);
+ }
else if (error->domain == GEDIT_DOCUMENT_ERROR ||
(error->domain == G_IO_ERROR &&
error->code != G_IO_ERROR_INVALID_DATA &&
@@ -2223,11 +2276,11 @@ _gedit_tab_save (GeditTab *tab)
/* uri used in error messages, will be freed in document_saved */
tab->priv->tmp_save_location = gedit_document_get_location (doc);
- tab->priv->tmp_encoding = gedit_document_get_encoding (doc);
+ tab->priv->tmp_encoding = gedit_document_get_encoding (doc);
if (tab->priv->auto_save_timeout > 0)
remove_auto_save_timeout (tab);
-
+
gedit_document_save (doc, save_flags);
}
diff --git a/tests/document-output-stream.c b/tests/document-output-stream.c
index 591a434..5b7a313 100644
--- a/tests/document-output-stream.c
+++ b/tests/document-output-stream.c
@@ -62,11 +62,6 @@ test_consecutive_write (const gchar *inbuf,
g_assert_no_error (err);
- g_object_get (G_OBJECT (doc), "text", &b, NULL);
-
- g_assert_cmpstr (inbuf, ==, b);
- g_free (b);
-
type = gedit_document_output_stream_detect_newline_type (GEDIT_DOCUMENT_OUTPUT_STREAM (out));
g_assert (type == newline_type);
@@ -152,6 +147,17 @@ test_boundary ()
g_object_unref (out);
}
+static void
+test_invalid_utf8 ()
+{
+ test_consecutive_write ("foobar\n\xef\xbf\xbe", "foobar\n\\EF\\BF\\BE", 10,
+ GEDIT_DOCUMENT_NEWLINE_TYPE_LF);
+ test_consecutive_write ("foobar\n\xef\xbf\xbezzzzzz\n", "foobar\n\\EF\\BF\\BEzzzzzz", 10,
+ GEDIT_DOCUMENT_NEWLINE_TYPE_LF);
+ test_consecutive_write ("\xef\xbf\xbezzzzzz\n", "\\EF\\BF\\BEzzzzzz", 10,
+ GEDIT_DOCUMENT_NEWLINE_TYPE_LF);
+}
+
/* SMART CONVERSION */
#define TEXT_TO_CONVERT "this is some text to make the tests"
@@ -395,6 +401,7 @@ int main (int argc,
g_test_add_func ("/document-output-stream/consecutive_tnewline", test_consecutive_tnewline);
g_test_add_func ("/document-output-stream/big-char", test_big_char);
g_test_add_func ("/document-output-stream/test-boundary", test_boundary);
+ g_test_add_func ("/document-output-stream/test-invalid-utf8", test_invalid_utf8);
g_test_add_func ("/document-output-stream/smart conversion: utf8-utf8", test_utf8_utf8);
g_test_add_func ("/document-output-stream/smart conversion: guessed", test_guessed);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]