[gedit] Add GeditDocumentOutputStream and unit tests.



commit 981647dc1f27e6688564e8fbfbb86b7267cec549
Author: Ignacio Casal Quinteiro <icq gnome org>
Date:   Mon Feb 8 15:14:25 2010 +0100

    Add GeditDocumentOutputStream and unit tests.

 gedit/Makefile.am                    |    2 +
 gedit/gedit-document-output-stream.c |  281 ++++++++++++++++++++++++++++++++++
 gedit/gedit-document-output-stream.h |   64 ++++++++
 gedit/gedit-gio-document-loader.c    |  266 ++++++++++++++++----------------
 tests/Makefile.am                    |    4 +
 tests/document-output-stream.c       |  106 +++++++++++++
 6 files changed, 591 insertions(+), 132 deletions(-)
---
diff --git a/gedit/Makefile.am b/gedit/Makefile.am
index e308c48..9c61383 100644
--- a/gedit/Makefile.am
+++ b/gedit/Makefile.am
@@ -67,6 +67,7 @@ NOINST_H_FILES =			\
 	gedit-dirs.h			\
 	gedit-document-input-stream.h	\
 	gedit-document-loader.h		\
+	gedit-document-output-stream.h	\
 	gedit-document-saver.h		\
 	gedit-documents-panel.h		\
 	gedit-gio-document-loader.h	\
@@ -144,6 +145,7 @@ libgedit_la_SOURCES = 			\
 	gedit-document.c 		\
 	gedit-document-input-stream.c	\
 	gedit-document-loader.c		\
+	gedit-document-output-stream.c	\
 	gedit-gio-document-loader.c	\
 	gedit-document-saver.c		\
 	gedit-gio-document-saver.c	\
diff --git a/gedit/gedit-document-output-stream.c b/gedit/gedit-document-output-stream.c
new file mode 100644
index 0000000..898df54
--- /dev/null
+++ b/gedit/gedit-document-output-stream.c
@@ -0,0 +1,281 @@
+/*
+ * gedit-document-output-stream.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2010 - Ignacio Casal Quinteiro
+ *
+ * gedit 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.
+ *
+ * gedit 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 gedit; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+
+#include "gedit-document-output-stream.h"
+
+
+#define GEDIT_DOCUMENT_OUTPUT_STREAM_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStreamPrivate))
+
+struct _GeditDocumentOutputStreamPrivate
+{
+	GeditDocument *doc;
+	GtkTextIter    pos;
+
+	guint is_initialized : 1;
+	guint is_closed : 1;
+};
+
+enum
+{
+	PROP_0,
+	PROP_DOCUMENT
+};
+
+G_DEFINE_TYPE (GeditDocumentOutputStream, gedit_document_output_stream, G_TYPE_OUTPUT_STREAM)
+
+static gssize	gedit_document_output_stream_write (GOutputStream            *stream,
+						    const void               *buffer,
+						    gsize                     count,
+						    GCancellable             *cancellable,
+						    GError                  **error);
+
+static gboolean	gedit_document_output_stream_close (GOutputStream     *stream,
+						    GCancellable      *cancellable,
+						    GError           **error);
+
+static void
+gedit_document_output_stream_set_property (GObject      *object,
+					   guint         prop_id,
+					   const GValue *value,
+					   GParamSpec   *pspec)
+{
+	GeditDocumentOutputStream *stream = GEDIT_DOCUMENT_OUTPUT_STREAM (object);
+
+	switch (prop_id)
+	{
+		case PROP_DOCUMENT:
+			stream->priv->doc = GEDIT_DOCUMENT (g_value_get_object (value));
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gedit_document_output_stream_get_property (GObject    *object,
+					   guint       prop_id,
+					   GValue     *value,
+					   GParamSpec *pspec)
+{
+	GeditDocumentOutputStream *stream = GEDIT_DOCUMENT_OUTPUT_STREAM (object);
+
+	switch (prop_id)
+	{
+		case PROP_DOCUMENT:
+			g_value_set_object (value, stream->priv->doc);
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gedit_document_output_stream_class_init (GeditDocumentOutputStreamClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+	object_class->get_property = gedit_document_output_stream_get_property;
+	object_class->set_property = gedit_document_output_stream_set_property;
+
+	stream_class->write_fn = gedit_document_output_stream_write;
+	stream_class->close_fn = gedit_document_output_stream_close;
+
+	g_object_class_install_property (object_class,
+					 PROP_DOCUMENT,
+					 g_param_spec_object ("document",
+							      "Document",
+							      "The document which is written",
+							      GEDIT_TYPE_DOCUMENT,
+							      G_PARAM_READWRITE |
+							      G_PARAM_CONSTRUCT_ONLY));
+
+	g_type_class_add_private (object_class, sizeof (GeditDocumentOutputStreamPrivate));
+}
+
+static void
+gedit_document_output_stream_init (GeditDocumentOutputStream *stream)
+{
+	stream->priv = GEDIT_DOCUMENT_OUTPUT_STREAM_GET_PRIVATE (stream);
+
+	stream->priv->is_initialized = FALSE;
+	stream->priv->is_closed = FALSE;
+}
+
+static GeditDocumentNewlineType
+get_newline_type (GtkTextIter *end)
+{
+	GeditDocumentNewlineType res;
+	GtkTextIter copy;
+	gunichar c;
+
+	copy = *end;
+	c = gtk_text_iter_get_char (&copy);
+
+	GtkTextIter tt = copy;
+	gtk_text_iter_forward_chars (&tt, 2);
+
+	if (g_unichar_break_type (c) == G_UNICODE_BREAK_CARRIAGE_RETURN)
+	{
+		if (gtk_text_iter_forward_char (&copy) &&
+		    g_unichar_break_type (gtk_text_iter_get_char (&copy)) == G_UNICODE_BREAK_LINE_FEED)
+		{
+			res = GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF;
+		}
+		else
+		{
+			res = GEDIT_DOCUMENT_NEWLINE_TYPE_CR;
+		}
+	}
+	else
+	{
+		res = GEDIT_DOCUMENT_NEWLINE_TYPE_LF;
+	}
+
+	return res;
+}
+
+GOutputStream *
+gedit_document_output_stream_new (GeditDocument *doc)
+{
+	return G_OUTPUT_STREAM (g_object_new (GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM,
+					      "document", doc, NULL));
+}
+
+GeditDocumentNewlineType
+gedit_document_output_stream_detect_newline_type (GeditDocumentOutputStream *stream)
+{
+	GeditDocumentNewlineType type;
+	GtkTextIter iter;
+
+	g_return_val_if_fail (GEDIT_IS_DOCUMENT_OUTPUT_STREAM (stream),
+			      GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT);
+
+	type = GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT;
+
+	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (stream->priv->doc), &iter);
+
+	if (!gtk_text_iter_backward_line (&iter))
+	{
+		gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (stream->priv->doc),
+						&iter);
+	}
+
+	if (gtk_text_iter_ends_line (&iter) || gtk_text_iter_forward_to_line_end (&iter))
+	{
+		type = get_newline_type (&iter);
+	}
+
+	return type;
+}
+
+/* If the last char is a newline, remove it from the buffer (otherwise
+   GtkTextView shows it as an empty line). See bug #324942. */
+static void
+remove_ending_newline (GeditDocumentOutputStream *stream)
+{
+	GtkTextIter end;
+	GtkTextIter start;
+
+	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (stream->priv->doc), &end);
+	start = end;
+
+	gtk_text_iter_set_line_offset (&start, 0);
+
+	if (gtk_text_iter_ends_line (&start) &&
+	    gtk_text_iter_backward_line (&start))
+	{
+		if (!gtk_text_iter_ends_line (&start))
+		{
+			gtk_text_iter_forward_to_line_end (&start);
+		}
+
+		/* Delete the empty line which is from 'start' to 'end' */
+		gtk_text_buffer_delete (GTK_TEXT_BUFFER (stream->priv->doc),
+		                        &start,
+		                        &end);
+	}
+}
+
+static void
+end_append_text_to_document (GeditDocumentOutputStream *stream)
+{
+	remove_ending_newline (stream);
+
+	gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (stream->priv->doc),
+				      FALSE);
+
+	gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (stream->priv->doc));
+}
+
+static gssize
+gedit_document_output_stream_write (GOutputStream            *stream,
+				    const void               *buffer,
+				    gsize                     count,
+				    GCancellable             *cancellable,
+				    GError                  **error)
+{
+	GeditDocumentOutputStream *ostream = GEDIT_DOCUMENT_OUTPUT_STREAM (stream);
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return -1;
+
+	if (!ostream->priv->is_initialized)
+	{
+		/* Init the undoable action */
+		gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (ostream->priv->doc));
+
+		/* clear the buffer */
+		gtk_text_buffer_set_text (GTK_TEXT_BUFFER (ostream->priv->doc),
+					  "", 0);
+
+		gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (ostream->priv->doc),
+						&ostream->priv->pos);
+		ostream->priv->is_initialized = TRUE;
+	}
+
+	gtk_text_buffer_insert (GTK_TEXT_BUFFER (ostream->priv->doc),
+				&ostream->priv->pos, buffer, count);
+
+	return count;
+}
+
+static gboolean
+gedit_document_output_stream_close (GOutputStream     *stream,
+				    GCancellable      *cancellable,
+				    GError           **error)
+{
+	GeditDocumentOutputStream *ostream = GEDIT_DOCUMENT_OUTPUT_STREAM (stream);
+
+	if (!ostream->priv->is_closed && ostream->priv->is_initialized)
+	{
+		end_append_text_to_document (ostream);
+		ostream->priv->is_closed = TRUE;
+	}
+
+	return TRUE;
+}
diff --git a/gedit/gedit-document-output-stream.h b/gedit/gedit-document-output-stream.h
new file mode 100644
index 0000000..a3f99fc
--- /dev/null
+++ b/gedit/gedit-document-output-stream.h
@@ -0,0 +1,64 @@
+/*
+ * gedit-document-output-stream.h
+ * This file is part of gedit
+ *
+ * Copyright (C) 2010 - Ignacio Casal Quinteiro
+ *
+ * gedit 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.
+ *
+ * gedit 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 gedit; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef __GEDIT_DOCUMENT_OUTPUT_STREAM_H__
+#define __GEDIT_DOCUMENT_OUTPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include "gedit-document.h"
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM		(gedit_document_output_stream_get_type ())
+#define GEDIT_DOCUMENT_OUTPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStream))
+#define GEDIT_DOCUMENT_OUTPUT_STREAM_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStream const))
+#define GEDIT_DOCUMENT_OUTPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStreamClass))
+#define GEDIT_IS_DOCUMENT_OUTPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM))
+#define GEDIT_IS_DOCUMENT_OUTPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM))
+#define GEDIT_DOCUMENT_OUTPUT_STREAM_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_DOCUMENT_OUTPUT_STREAM, GeditDocumentOutputStreamClass))
+
+typedef struct _GeditDocumentOutputStream		GeditDocumentOutputStream;
+typedef struct _GeditDocumentOutputStreamClass		GeditDocumentOutputStreamClass;
+typedef struct _GeditDocumentOutputStreamPrivate	GeditDocumentOutputStreamPrivate;
+
+struct _GeditDocumentOutputStream
+{
+	GOutputStream parent;
+
+	GeditDocumentOutputStreamPrivate *priv;
+};
+
+struct _GeditDocumentOutputStreamClass
+{
+	GOutputStreamClass parent_class;
+};
+
+GType			 gedit_document_output_stream_get_type		(void) G_GNUC_CONST;
+
+GOutputStream		*gedit_document_output_stream_new		(GeditDocument *doc);
+
+GeditDocumentNewlineType gedit_document_output_stream_detect_newline_type (GeditDocumentOutputStream *stream);
+
+G_END_DECLS
+
+#endif /* __GEDIT_DOCUMENT_OUTPUT_STREAM_H__ */
diff --git a/gedit/gedit-gio-document-loader.c b/gedit/gedit-gio-document-loader.c
index ac5b8c4..9b26e53 100644
--- a/gedit/gedit-gio-document-loader.c
+++ b/gedit/gedit-gio-document-loader.c
@@ -39,6 +39,7 @@
 #include <gio/gio.h>
 
 #include "gedit-gio-document-loader.h"
+#include "gedit-document-output-stream.h"
 #include "gedit-smart-charset-converter.h"
 #include "gedit-prefs-manager.h"
 #include "gedit-debug.h"
@@ -48,6 +49,9 @@ typedef struct
 {
 	GeditGioDocumentLoader *loader;
 	GCancellable 	       *cancellable;
+
+	gssize			read;
+	gssize			written;
 	gboolean		tried_mount;
 } AsyncData;
 
@@ -80,13 +84,12 @@ struct _GeditGioDocumentLoaderPrivate
 	/* Handle for remote files */
 	GCancellable 	 *cancellable;
 	GInputStream	 *stream;
+	GOutputStream    *output;
 	GeditSmartCharsetConverter *converter;
 
 	gchar             buffer[READ_CHUNK_SIZE];
 
 	GError           *error;
-
-	guint		  started_insert : 1;
 };
 
 G_DEFINE_TYPE(GeditGioDocumentLoader, gedit_gio_document_loader, GEDIT_TYPE_DOCUMENT_LOADER)
@@ -111,6 +114,12 @@ gedit_gio_document_loader_dispose (GObject *object)
 		priv->stream = NULL;
 	}
 
+	if (priv->output != NULL)
+	{
+		g_object_unref (priv->output);
+		priv->output = NULL;
+	}
+
 	if (priv->converter != NULL)
 	{
 		g_object_unref (priv->converter);
@@ -161,7 +170,6 @@ gedit_gio_document_loader_init (GeditGioDocumentLoader *gvloader)
 
 	gvloader->priv->converter = NULL;
 	gvloader->priv->error = NULL;
-	gvloader->priv->started_insert = FALSE;
 }
 
 static AsyncData *
@@ -229,25 +237,11 @@ get_metadata_encoding (GeditDocumentLoader *loader)
 static void
 remote_load_completed_or_failed (GeditGioDocumentLoader *gvloader, AsyncData *async)
 {
-	GeditDocumentLoader *loader;
-
-	loader = GEDIT_DOCUMENT_LOADER (gvloader);
-
-	if (async)
-		async_data_free (async);
-
-	if (gvloader->priv->stream)
-		g_input_stream_close_async (G_INPUT_STREAM (gvloader->priv->stream),
-					    G_PRIORITY_HIGH, NULL, NULL, NULL);
-
-	gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (gvloader),
+	gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (async->loader),
 				       TRUE,
-				       gvloader->priv->error);
+				       async->loader->priv->error);
 }
 
-/* prototype, because they call each other... isn't C lovely */
-static void	read_file_chunk		(AsyncData *async);
-
 static void
 async_failed (AsyncData *async, GError *error)
 {
@@ -256,110 +250,145 @@ async_failed (AsyncData *async, GError *error)
 }
 
 static void
-append_text_to_document (GeditDocumentLoader *loader,
-			 const gchar         *text,
-			 gint                 len)
+close_output_stream_ready_cb (GOutputStream *stream,
+			      GAsyncResult  *res,
+			      AsyncData     *async)
 {
-	GeditDocument *doc = loader->document;
-	GtkTextIter end;
-
-	/* Insert text in the buffer */
-	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (doc), &end);
+	GError *error = NULL;
 	
-	gtk_text_buffer_insert (GTK_TEXT_BUFFER (doc), &end, text, len);
-}
-
-static GeditDocumentNewlineType
-get_newline_type (GtkTextIter *end)
-{
-	GeditDocumentNewlineType res;
-	GtkTextIter copy;
-	gunichar c;
+	/* check cancelled state manually */
+	if (g_cancellable_is_cancelled (async->cancellable))
+	{
+		async_data_free (async);
+		return;
+	}
+	
+	gedit_debug_message (DEBUG_SAVER, "Finished closing stream");
+	
+	if (!g_output_stream_close_finish (stream, res, &error))
+	{
+		gedit_debug_message (DEBUG_SAVER, "Closing stream error: %s", error->message);
 
-	copy = *end;
-	c = gtk_text_iter_get_char (&copy);
+		async_failed (async, error);
+		return;
+	}
 
-	GtkTextIter tt = copy;
-	gtk_text_iter_forward_chars (&tt, 2);
+	remote_load_completed_or_failed (async->loader, async);
+}
 
-	if (g_unichar_break_type (c) == G_UNICODE_BREAK_CARRIAGE_RETURN)
+static void
+close_input_stream_ready_cb (GInputStream *stream,
+			     GAsyncResult  *res,
+			     AsyncData     *async)
+{
+	GError *error = NULL;
+	
+	/* check cancelled state manually */
+	if (g_cancellable_is_cancelled (async->cancellable))
 	{
-		if (gtk_text_iter_forward_char (&copy) &&
-		    g_unichar_break_type (gtk_text_iter_get_char (&copy)) == G_UNICODE_BREAK_LINE_FEED)
-		{
-			res = GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF;
-		}
-		else
-		{
-			res = GEDIT_DOCUMENT_NEWLINE_TYPE_CR;
-		}
+		async_data_free (async);
+		return;
 	}
-	else
+	
+	gedit_debug_message (DEBUG_SAVER, "Finished closing input stream");
+	
+	if (!g_input_stream_close_finish (stream, res, &error))
 	{
-		res = GEDIT_DOCUMENT_NEWLINE_TYPE_LF;
+		gedit_debug_message (DEBUG_SAVER, "Closing input stream error: %s", error->message);
+
+		async_failed (async, error);
+		return;
 	}
 
-	return res;
+	/* now we close the output stream */
+	gedit_debug_message (DEBUG_SAVER, "Close output stream");
+	g_output_stream_close_async (async->loader->priv->output,
+				     G_PRIORITY_HIGH,
+				     async->cancellable,
+				     (GAsyncReadyCallback)close_output_stream_ready_cb,
+				     async);
 }
 
 static void
-detect_newline_type (GeditDocumentLoader *loader)
+write_complete (AsyncData *async)
 {
-	GtkTextIter iter;
+	GeditDocumentLoader *loader;
 
-	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (loader->document), &iter);
+	loader = GEDIT_DOCUMENT_LOADER (async->loader);
 
-	if (!gtk_text_iter_backward_line (&iter))
-	{
-		gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (loader->document), &iter);
-	}
-
-	if (gtk_text_iter_ends_line (&iter) || gtk_text_iter_forward_to_line_end (&iter))
-	{
-		loader->auto_detected_newline_type = get_newline_type (&iter);
-	}
+	if (async->loader->priv->stream)
+		g_input_stream_close_async (G_INPUT_STREAM (async->loader->priv->stream),
+					    G_PRIORITY_HIGH,
+					    async->cancellable,
+					    (GAsyncReadyCallback)close_input_stream_ready_cb,
+					    async);
 }
 
+/* prototype, because they call each other... isn't C lovely */
+static void	read_file_chunk		(AsyncData *async);
+static void	write_file_chunk	(AsyncData *async);
+
 static void
-remove_ending_newline (GeditDocumentLoader *loader)
+async_write_cb (GOutputStream *stream,
+		GAsyncResult  *res,
+		AsyncData     *async)
 {
-	GtkTextIter end;
-	GtkTextIter start;
+	GeditGioDocumentLoader *gvloader;
+	gssize bytes_written;
+	GError *error = NULL;
+
+	/* Check cancelled state manually */
+	if (g_cancellable_is_cancelled (async->cancellable))
+	{
+		async_data_free (async);
+		return;
+	}
 
-	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (loader->document), &end);
-	start = end;
+	bytes_written = g_output_stream_write_finish (stream, res, &error);
 
-	gtk_text_iter_set_line_offset (&start, 0);
+	gedit_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written);
 
-	if (gtk_text_iter_ends_line (&start) &&
-	    gtk_text_iter_backward_line (&start))
+	if (bytes_written == -1)
 	{
-		if (!gtk_text_iter_ends_line (&start))
-		{
-			gtk_text_iter_forward_to_line_end (&start);
-		}
+		gedit_debug_message (DEBUG_SAVER, "Write error: %s", error->message);
+		async_failed (async, error);
+		return;
+	}
 
-		/* Delete the empty line which is from 'start' to 'end' */
-		gtk_text_buffer_delete (GTK_TEXT_BUFFER (loader->document),
-		                        &start,
-		                        &end);
+	gvloader = async->loader;
+	async->written += bytes_written;
+
+	/* write again */
+	if (async->written != async->read)
+	{
+		write_file_chunk (async);
+		return;
 	}
+
+	/* note that this signal blocks the read... check if it isn't
+	 * a performance problem
+	 */
+	gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (gvloader),
+				       FALSE,
+				       NULL);
+
+	read_file_chunk (async);
 }
 
 static void
-end_append_text_to_document (GeditDocumentLoader *loader)
+write_file_chunk (AsyncData *async)
 {
-	detect_newline_type (loader);
-
-	/* If the last char is a newline, remove it from the buffer (otherwise
-		GtkTextView shows it as an empty line). See bug #324942. */
-	remove_ending_newline (loader);
-
-	gtk_text_buffer_set_modified (GTK_TEXT_BUFFER (loader->document), FALSE);
+	GeditGioDocumentLoader *gvloader;
 
-	gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (loader->document));
+	gvloader = async->loader;
 
-	GEDIT_GIO_DOCUMENT_LOADER (loader)->priv->started_insert = FALSE;
+	g_output_stream_write_async (G_OUTPUT_STREAM (gvloader->priv->output),
+				     gvloader->priv->buffer + async->written,
+				     async->read - async->written,
+				     G_PRIORITY_HIGH,
+				     async->cancellable,
+				     (GAsyncReadyCallback) async_write_cb,
+				     async);
 }
 
 static void
@@ -370,7 +399,6 @@ async_read_cb (GInputStream *stream,
 	gedit_debug (DEBUG_LOADER);
 	GeditGioDocumentLoader *gvloader;
 	GeditDocumentLoader *loader;
-	gssize bytes_read;
 	GError *error = NULL;
 
 	gvloader = async->loader;
@@ -379,44 +407,44 @@ async_read_cb (GInputStream *stream,
 	/* manually check cancelled state */
 	if (g_cancellable_is_cancelled (async->cancellable))
 	{
-		end_append_text_to_document (GEDIT_DOCUMENT_LOADER (gvloader));
 		remote_load_completed_or_failed (gvloader, async);
 		return;
 	}
 
-	bytes_read = g_input_stream_read_finish (stream, res, &error);
+	async->read = g_input_stream_read_finish (stream, res, &error);
+	async->written = 0;
 	
 	/* error occurred */
-	if (bytes_read == -1)
+	if (async->read == -1)
 	{
 		async_failed (async, error);
 		return;
 	}
 
 	/* Check for the extremely unlikely case where the file size overflows. */
-	if (gvloader->priv->bytes_read + bytes_read < gvloader->priv->bytes_read)
+	if (gvloader->priv->bytes_read + async->read < gvloader->priv->bytes_read)
 	{
 		g_set_error (&gvloader->priv->error,
 			     GEDIT_DOCUMENT_ERROR,
 			     GEDIT_DOCUMENT_ERROR_TOO_BIG,
 			     "File too big");
 
-		end_append_text_to_document (GEDIT_DOCUMENT_LOADER (gvloader));
-		remote_load_completed_or_failed (gvloader, async);
-
+		async_failed (async, gvloader->priv->error);
 		return;
 	}
 
 	/* Bump the size. */
-	gvloader->priv->bytes_read += bytes_read;
+	gvloader->priv->bytes_read += async->read;
 
 	/* end of the file, we are done! */
-	if (bytes_read == 0)
+	if (async->read == 0)
 	{
 		GEDIT_DOCUMENT_LOADER (gvloader)->auto_detected_encoding =
 			gedit_smart_charset_converter_get_guessed (gvloader->priv->converter);
 
 		loader->auto_detected_encoding = gedit_smart_charset_converter_get_guessed (gvloader->priv->converter);
+		loader->auto_detected_newline_type =
+			gedit_document_output_stream_detect_newline_type (GEDIT_DOCUMENT_OUTPUT_STREAM (gvloader->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 */
@@ -431,27 +459,12 @@ async_read_cb (GInputStream *stream,
 					     "needed to use a fallback char");
 		}*/
 
-		end_append_text_to_document (GEDIT_DOCUMENT_LOADER (gvloader));
-
-		remote_load_completed_or_failed (gvloader, async);
+		write_complete (async);
 
 		return;
 	}
 
-	append_text_to_document (GEDIT_DOCUMENT_LOADER (gvloader),
-				 gvloader->priv->buffer,
-				 bytes_read);
-
-	/* otherwise emit progress and read some more */
-
-	/* note that this signal blocks the read... check if it isn't
-	 * a performance problem
-	 */
-	gedit_document_loader_loading (GEDIT_DOCUMENT_LOADER (gvloader),
-				       FALSE,
-				       NULL);
-
-	read_file_chunk (async);
+	write_file_chunk (async);
 }
 
 static void
@@ -461,20 +474,6 @@ read_file_chunk (AsyncData *async)
 	
 	gvloader = async->loader;
 
-	if (!gvloader->priv->started_insert)
-	{
-		GeditDocumentLoader *loader;
-
-		loader = GEDIT_DOCUMENT_LOADER (gvloader);
-
-		/* Init the undoable action */
-		gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (loader->document));
-		gvloader->priv->started_insert = TRUE;
-
-		/* clear the buffer */
-		gtk_text_buffer_set_text (GTK_TEXT_BUFFER (loader->document), "", 0);
-	}
-
 	g_input_stream_read_async (G_INPUT_STREAM (gvloader->priv->stream),
 				   gvloader->priv->buffer,
 				   READ_CHUNK_SIZE,
@@ -551,6 +550,9 @@ finish_query_info (AsyncData *async)
 
 	gvloader->priv->stream = utf8_stream;
 
+	/* Output stream */
+	gvloader->priv->output = gedit_document_output_stream_new (loader->document);
+
 	/* start reading */
 	read_file_chunk (async);
 }
diff --git a/tests/Makefile.am b/tests/Makefile.am
index dd6ee4c..f54ce8c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -11,6 +11,10 @@ TEST_PROGS			+= document-input-stream
 document_input_stream_SOURCES	= document-input-stream.c
 document_input_stream_LDADD	= $(progs_ldadd)
 
+TEST_PROGS			+= document-output-stream
+document_output_stream_SOURCES	= document-output-stream.c
+document_output_stream_LDADD	= $(progs_ldadd)
+
 TEST_PROGS			+= document-loader
 document_loader_SOURCES		= document-loader.c
 document_loader_LDADD		= $(progs_ldadd)
diff --git a/tests/document-output-stream.c b/tests/document-output-stream.c
new file mode 100644
index 0000000..57b6db5
--- /dev/null
+++ b/tests/document-output-stream.c
@@ -0,0 +1,106 @@
+/*
+ * document-output-stream.c
+ * This file is part of gedit
+ *
+ * Copyright (C) 2010 - Ignacio Casal Quinteiro
+ *
+ * gedit 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.
+ *
+ * gedit 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 gedit; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+
+#include "gedit-document-output-stream.h"
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <string.h>
+
+static void
+test_consecutive_write (const gchar *inbuf,
+			const gchar *outbuf,
+			gsize write_chunk_len)
+{
+	GeditDocument *doc;
+	GOutputStream *out;
+	gsize len;
+	gssize n, w;
+	GError *err = NULL;
+	gchar *b;
+
+	doc = gedit_document_new ();
+	out = gedit_document_output_stream_new (doc);
+
+	n = 0;
+
+	do
+	{
+		len = MIN (write_chunk_len, strlen (inbuf + n));
+		w = g_output_stream_write (out, inbuf + n, len, NULL, &err);
+		g_assert_cmpint (w, >=, 0);
+		g_assert_no_error (err);
+
+		n += w;
+	} while (w != 0);
+
+	g_object_get (G_OBJECT (doc), "text", &b, NULL);
+
+	g_assert_cmpstr (inbuf, ==, b);
+	g_free (b);
+
+	g_output_stream_close (out, NULL, &err);
+	g_assert_no_error (err);
+
+	g_object_get (G_OBJECT (doc), "text", &b, NULL);
+
+	g_assert_cmpstr (outbuf, ==, b);
+	g_free (b);
+
+	g_assert (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc)) == FALSE);
+}
+
+static void
+test_empty ()
+{
+	test_consecutive_write ("", "", 10);
+}
+
+static void
+test_consecutive ()
+{
+	test_consecutive_write ("hello\nhow\nare\nyou", "hello\nhow\nare\nyou", 2);
+}
+
+static void
+test_consecutive_tnewline ()
+{
+	test_consecutive_write ("hello\nhow\nare\nyou\n", "hello\nhow\nare\nyou", 2);
+	test_consecutive_write ("hello\r\nhow\r\nare\r\nyou\r\n", "hello\r\nhow\r\nare\r\nyou", 2);
+}
+
+int main (int   argc,
+          char *argv[])
+{
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	gedit_prefs_manager_app_init ();
+
+	g_test_add_func ("/document-output-stream/empty", test_empty);
+
+	g_test_add_func ("/document-output-stream/consecutive", test_consecutive);
+	g_test_add_func ("/document-output-stream/consecutive_tnewline", test_consecutive_tnewline);
+
+	return g_test_run ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]