[gedit] Added new GeditDocumentInputStream for doc save



commit be71072115d6b78b47683ce8b898bdfdf19c3018
Author: Jesse van den Kieboom <jesse icecrew nl>
Date:   Sat Jan 23 20:13:46 2010 +0100

    Added new GeditDocumentInputStream for doc save

 gedit/Makefile.am                   |    2 +
 gedit/gedit-document-input-stream.c |  425 +++++++++++++++++++++++++++++++++++
 gedit/gedit-document-input-stream.h |   68 ++++++
 gedit/gedit-document-loader.c       |   28 +++
 gedit/gedit-document-loader.h       |    3 +
 gedit/gedit-document-saver.c        |   36 +++-
 gedit/gedit-document-saver.h        |   10 +-
 gedit/gedit-document.c              |   63 +++++-
 gedit/gedit-document.h              |   19 ++
 gedit/gedit-gio-document-loader.c   |   87 +++++++-
 gedit/gedit-gio-document-saver.c    |  250 ++++++++++++---------
 11 files changed, 856 insertions(+), 135 deletions(-)
---
diff --git a/gedit/Makefile.am b/gedit/Makefile.am
index 0c54c47..5757ac0 100644
--- a/gedit/Makefile.am
+++ b/gedit/Makefile.am
@@ -65,6 +65,7 @@ endif
 NOINST_H_FILES =			\
 	gedit-close-button.h		\
 	gedit-dirs.h			\
+	gedit-document-input-stream.h	\
 	gedit-document-loader.h		\
 	gedit-document-saver.h		\
 	gedit-documents-panel.h		\
@@ -151,6 +152,7 @@ libgedit_la_SOURCES = 			\
 	gedit-debug.c			\
 	gedit-dirs.c			\
 	gedit-document.c 		\
+	gedit-document-input-stream.c	\
 	gedit-document-loader.c		\
 	gedit-gio-document-loader.c	\
 	gedit-document-saver.c		\
diff --git a/gedit/gedit-document-input-stream.c b/gedit/gedit-document-input-stream.c
new file mode 100644
index 0000000..0bd5900
--- /dev/null
+++ b/gedit/gedit-document-input-stream.c
@@ -0,0 +1,425 @@
+/*
+ * gedit-document-input-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 "config.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <string.h>
+
+#include "gedit-document-input-stream.h"
+#include "gedit-enum-types.h"
+
+G_DEFINE_TYPE (GeditDocumentInputStream, gedit_document_input_stream, G_TYPE_INPUT_STREAM);
+
+struct _GeditDocumentInputStreamPrivate
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter    pos;
+	guint          bytes_partial;
+
+	GeditDocumentNewlineType newline_type;
+
+	guint newline_added : 1;
+	guint is_initialized : 1;
+};
+
+enum
+{
+	PROP_0,
+	PROP_BUFFER,
+	PROP_NEWLINE_TYPE
+};
+
+static gssize     gedit_document_input_stream_read     (GInputStream      *stream,
+							void              *buffer,
+							gsize              count,
+							GCancellable      *cancellable,
+							GError           **error);
+static gboolean   gedit_document_input_stream_close    (GInputStream      *stream,
+							GCancellable      *cancellable,
+							GError           **error);
+
+static void
+gedit_document_input_stream_set_property (GObject      *object,
+					  guint         prop_id,
+					  const GValue *value,
+					  GParamSpec   *pspec)
+{
+	GeditDocumentInputStream *stream = GEDIT_DOCUMENT_INPUT_STREAM (object);
+
+	switch (prop_id)
+	{
+		case PROP_BUFFER:
+			stream->priv->buffer = GTK_TEXT_BUFFER (g_value_get_object (value));
+			break;
+
+		case PROP_NEWLINE_TYPE:
+			stream->priv->newline_type = g_value_get_enum (value);
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gedit_document_input_stream_get_property (GObject    *object,
+					  guint       prop_id,
+					  GValue     *value,
+					  GParamSpec *pspec)
+{
+	GeditDocumentInputStream *stream = GEDIT_DOCUMENT_INPUT_STREAM (object);
+
+	switch (prop_id)
+	{
+		case PROP_BUFFER:
+			g_value_set_object (value, stream->priv->buffer);
+			break;
+
+		case PROP_NEWLINE_TYPE:
+			g_value_set_enum (value, stream->priv->newline_type);
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gedit_document_input_stream_class_init (GeditDocumentInputStreamClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (GeditDocumentInputStreamPrivate));
+
+	gobject_class->get_property = gedit_document_input_stream_get_property;
+	gobject_class->set_property = gedit_document_input_stream_set_property;
+
+	stream_class->read_fn = gedit_document_input_stream_read;
+	stream_class->close_fn = gedit_document_input_stream_close;
+
+	g_object_class_install_property (gobject_class,
+					 PROP_BUFFER,
+					 g_param_spec_object ("buffer",
+							      "Buffer",
+							      "The buffer which is read",
+							      GTK_TYPE_TEXT_BUFFER,
+							      G_PARAM_READWRITE |
+							      G_PARAM_CONSTRUCT_ONLY));
+
+	/**
+	 * GeditDocumentInputStream:newline-type:
+	 *
+	 * The :newline-type property determines what is considered
+	 * as a line ending when reading complete lines from the stream.
+	 */ 
+	g_object_class_install_property (gobject_class,
+					 PROP_NEWLINE_TYPE,
+					 g_param_spec_enum ("newline-type",
+							    "Newline type",
+							    "The accepted types of line ending",
+							    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE,
+							    GEDIT_DOCUMENT_NEWLINE_TYPE_LF,
+							    G_PARAM_READWRITE |
+							    G_PARAM_STATIC_NAME |
+							    G_PARAM_STATIC_BLURB |
+							    G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gedit_document_input_stream_init (GeditDocumentInputStream *stream)
+{
+	stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+						    GEDIT_TYPE_DOCUMENT_INPUT_STREAM,
+						    GeditDocumentInputStreamPrivate);
+}
+
+static gsize
+get_new_line_size (GeditDocumentInputStream *stream)
+{
+	gsize ret;
+
+	switch (stream->priv->newline_type)
+	{
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR:
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_LF:
+			ret = 1;
+			break;
+
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF:
+			ret = 2;
+			break;
+	}
+
+	return ret;
+}
+
+/**
+ * gedit_document_input_stream_new:
+ * @buffer: a #GtkTextBuffer
+ *
+ * Reads the data from @buffer.
+ *
+ * Returns: a new #GInputStream to read @buffer
+ */
+GInputStream *
+gedit_document_input_stream_new (GtkTextBuffer           *buffer,
+				 GeditDocumentNewlineType type)
+{
+	GeditDocumentInputStream *stream;
+
+	g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+
+	stream = g_object_new (GEDIT_TYPE_DOCUMENT_INPUT_STREAM,
+			       "buffer", buffer,
+			       "newline-type", type,
+			       NULL);
+
+	return G_INPUT_STREAM (stream);
+}
+
+gsize
+gedit_document_input_stream_get_total_size (GeditDocumentInputStream *stream)
+{
+	g_return_val_if_fail (GEDIT_IS_DOCUMENT_INPUT_STREAM (stream), 0);
+
+	return gtk_text_buffer_get_char_count (stream->priv->buffer);
+}
+
+gsize
+gedit_document_input_stream_tell (GeditDocumentInputStream *stream)
+{
+	g_return_val_if_fail (GEDIT_IS_DOCUMENT_INPUT_STREAM (stream), 0);
+
+	/* FIXME: is this potentially inefficient? If yes, we could keep
+	   track of the offset internally, assuming the mark doesn't move
+	   during the operation */
+	if (!stream->priv->is_initialized)
+	{
+		return 0;
+	}
+	else
+	{
+		return gtk_text_iter_get_offset (&stream->priv->pos);
+	}
+}
+
+static const gchar *
+get_new_line (GeditDocumentInputStream *stream)
+{
+	const gchar *ret;
+
+	switch (stream->priv->newline_type)
+	{
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR:
+			ret = "\r";
+			break;
+
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_LF:
+			ret = "\n";
+			break;
+
+		case GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF:
+			ret = "\r\n";
+			break;
+	}
+
+	return ret;
+}
+
+static gsize
+read_line (GeditDocumentInputStream *stream,
+	   gchar                    *outbuf,
+	   gsize                     space_left)
+{
+	GtkTextIter *start, next, end;
+	gchar *buf;
+	gsize read, bytes, newline_size, bytes_to_write;
+	const gchar *newline;
+	gboolean is_last;
+
+	start = &stream->priv->pos;
+
+	end = next = *start;
+	newline = get_new_line (stream);
+	newline_size = get_new_line_size (stream);
+
+	if (gtk_text_iter_is_end (start))
+		return 0;
+
+	/* Check needed for empty lines */
+	if (!gtk_text_iter_ends_line (&end))
+		gtk_text_iter_forward_to_line_end (&end);
+
+	gtk_text_iter_forward_line (&next);
+
+	buf = gtk_text_iter_get_slice (start, &end);
+
+	/* the bytes of a line includes also the newline, so with the
+	   offsets we remove the newline and we add the new newline size */
+	bytes = gtk_text_iter_get_bytes_in_line (start) - stream->priv->bytes_partial;
+
+	/* bytes_in_line includes the newlines, so we remove that assuming that
+	   they are single byte characters */
+	bytes = bytes - (gtk_text_iter_get_offset (&next) - gtk_text_iter_get_offset (&end));
+	is_last = gtk_text_iter_is_end (&end);
+
+	/* bytes_to_write contains the amount of bytes we would like to write.
+	   This means its the amount of bytes in the line (without the newline
+	   in the buffer) + the amount of bytes for the newline we want to
+	   write (newline_size) */
+	bytes_to_write = bytes;
+
+	/* do not add the new newline_size for the last line */
+	if (!is_last)
+		bytes_to_write += newline_size;
+
+	if (bytes_to_write > space_left)
+	{
+		gchar *ptr;
+		gint offset;
+		gsize to_write;
+		gsize next_size;
+
+		/* Here the line does not fit in the buffer, we thus write
+		   the amount of bytes we can still fit, storing the position
+		   for the next read with the mark. Do not try to write the
+		   new newline in this case, it will be handled in the next
+		   iteration */
+		to_write = MIN (space_left, bytes);
+		ptr = buf;
+		next_size = 0;
+		offset = 0;
+
+		do
+		{
+			read = next_size;
+			ptr = g_utf8_next_char (ptr);
+			next_size = ptr - buf;
+
+			++offset;
+		} while (next_size <= to_write);
+
+		memcpy (outbuf, buf, read);
+
+		/* Note: offset is one past what we wrote */
+		gtk_text_iter_forward_chars (start, offset - 1);
+
+		stream->priv->bytes_partial += read;
+	}
+	else
+	{
+		/* First just copy the bytes without the newline */
+		memcpy (outbuf, buf, bytes);
+
+		/* Then add the newline, but not for the last line */
+		if (!is_last)
+		{
+			memcpy (outbuf + bytes, newline, newline_size);
+		}
+
+		read = bytes_to_write;
+		*start = next;
+
+		stream->priv->bytes_partial = 0;
+	}
+
+	g_free (buf);
+	return read;
+}
+
+static gssize
+gedit_document_input_stream_read (GInputStream  *stream,
+				  void          *buffer,
+				  gsize          count,
+				  GCancellable  *cancellable,
+				  GError       **error)
+{
+	GeditDocumentInputStream *dstream;
+	gssize space_left, read, n, newline_size;
+
+	dstream = GEDIT_DOCUMENT_INPUT_STREAM (stream);
+
+	if (count < 6)
+	{
+		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+				     "Not enougth space in destination");
+		return -1;
+	}
+
+	if (g_cancellable_set_error_if_cancelled (cancellable, error))
+		return -1;
+
+	/* Initialize the mark to the first char in the text buffer */
+	if (!dstream->priv->is_initialized)
+	{
+		gtk_text_buffer_get_start_iter (dstream->priv->buffer, &dstream->priv->pos);
+		dstream->priv->is_initialized = TRUE;
+	}
+
+	space_left = count;
+	read = 0;
+
+	do
+	{
+		n = read_line (dstream, buffer + read, space_left);
+		read += n;
+		space_left -= n;
+	} while (space_left > 0 && n != 0 && dstream->priv->bytes_partial == 0);
+
+	/* make sure files are always terminated with \n (see bug #95676). Note
+	   that we strip the trailing \n when loading the file */
+	newline_size = get_new_line_size (dstream);
+
+	if (gtk_text_iter_is_end (&dstream->priv->pos) &&
+	    space_left >= newline_size &&
+	    !dstream->priv->newline_added)
+	{
+		const gchar *newline;
+
+		newline = get_new_line (dstream);
+
+		memcpy (buffer + read, newline, newline_size);
+
+		read += newline_size;
+		dstream->priv->newline_added = TRUE;
+	}
+
+	return read;
+}
+
+static gboolean
+gedit_document_input_stream_close (GInputStream  *stream,
+				   GCancellable  *cancellable,
+				   GError       **error)
+{
+	GeditDocumentInputStream *dstream = GEDIT_DOCUMENT_INPUT_STREAM (stream);
+
+	dstream->priv->newline_added = FALSE;
+
+	return TRUE;
+}
diff --git a/gedit/gedit-document-input-stream.h b/gedit/gedit-document-input-stream.h
new file mode 100644
index 0000000..01f6a49
--- /dev/null
+++ b/gedit/gedit-document-input-stream.h
@@ -0,0 +1,68 @@
+/*
+ * gedit-document-input-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_INPUT_STREAM_H__
+#define __GEDIT_DOCUMENT_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "gedit-document.h"
+
+G_BEGIN_DECLS
+
+#define GEDIT_TYPE_DOCUMENT_INPUT_STREAM		(gedit_document_input_stream_get_type ())
+#define GEDIT_DOCUMENT_INPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStream))
+#define GEDIT_DOCUMENT_INPUT_STREAM_CONST(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStream const))
+#define GEDIT_DOCUMENT_INPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStreamClass))
+#define GEDIT_IS_DOCUMENT_INPUT_STREAM(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM))
+#define GEDIT_IS_DOCUMENT_INPUT_STREAM_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GEDIT_TYPE_DOCUMENT_INPUT_STREAM))
+#define GEDIT_DOCUMENT_INPUT_STREAM_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GEDIT_TYPE_DOCUMENT_INPUT_STREAM, GeditDocumentInputStreamClass))
+
+typedef struct _GeditDocumentInputStream	GeditDocumentInputStream;
+typedef struct _GeditDocumentInputStreamClass	GeditDocumentInputStreamClass;
+typedef struct _GeditDocumentInputStreamPrivate	GeditDocumentInputStreamPrivate;
+
+struct _GeditDocumentInputStream
+{
+	GInputStream parent;
+	
+	GeditDocumentInputStreamPrivate *priv;
+};
+
+struct _GeditDocumentInputStreamClass
+{
+	GInputStreamClass parent_class;
+};
+
+GType				 gedit_document_input_stream_get_type		(void) G_GNUC_CONST;
+
+GInputStream			*gedit_document_input_stream_new		(GtkTextBuffer           *buffer,
+										 GeditDocumentNewlineType type);
+
+gsize				 gedit_document_input_stream_get_total_size	(GeditDocumentInputStream *stream);
+
+gsize				 gedit_document_input_stream_tell		(GeditDocumentInputStream *stream);
+
+G_END_DECLS
+
+#endif /* __GEDIT_DOCUMENT_INPUT_STREAM_H__ */
diff --git a/gedit/gedit-document-loader.c b/gedit/gedit-document-loader.c
index 390c6c1..54d7bba 100644
--- a/gedit/gedit-document-loader.c
+++ b/gedit/gedit-document-loader.c
@@ -41,6 +41,7 @@
 #include "gedit-utils.h"
 #include "gedit-convert.h"
 #include "gedit-marshal.h"
+#include "gedit-enum-types.h"
 
 /* Those are for the the gedit_document_loader_new() factory */
 #include "gedit-gio-document-loader.h"
@@ -64,6 +65,7 @@ enum
 	PROP_DOCUMENT,
 	PROP_URI,
 	PROP_ENCODING,
+	PROP_NEWLINE_TYPE
 };
 
 static void
@@ -88,6 +90,9 @@ gedit_document_loader_set_property (GObject      *object,
 			g_return_if_fail (loader->encoding == NULL);
 			loader->encoding = g_value_get_boxed (value);
 			break;
+		case PROP_NEWLINE_TYPE:
+			loader->auto_detected_newline_type = g_value_get_enum (value);
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -113,6 +118,9 @@ gedit_document_loader_get_property (GObject    *object,
 		case PROP_ENCODING:
 			g_value_set_boxed (value, gedit_document_loader_get_encoding (loader));
 			break;
+		case PROP_NEWLINE_TYPE:
+			g_value_set_enum (value, loader->auto_detected_newline_type);
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -185,6 +193,16 @@ gedit_document_loader_class_init (GeditDocumentLoaderClass *klass)
 							     G_PARAM_CONSTRUCT_ONLY |
 							     G_PARAM_STATIC_STRINGS));
 
+	g_object_class_install_property (object_class, PROP_NEWLINE_TYPE,
+	                                 g_param_spec_enum ("newline-type",
+	                                                    "Newline type",
+	                                                    "The accepted types of line ending",
+	                                                    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE,
+	                                                    GEDIT_DOCUMENT_NEWLINE_TYPE_LF,
+	                                                    G_PARAM_READWRITE |
+	                                                    G_PARAM_STATIC_NAME |
+	                                                    G_PARAM_STATIC_BLURB));
+
 	signals[LOADING] =
 		g_signal_new ("loading",
 			      G_OBJECT_CLASS_TYPE (object_class),
@@ -202,6 +220,7 @@ static void
 gedit_document_loader_init (GeditDocumentLoader *loader)
 {
 	loader->used = FALSE;
+	loader->auto_detected_newline_type = GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT;
 }
 
 void
@@ -321,6 +340,15 @@ gedit_document_loader_get_encoding (GeditDocumentLoader *loader)
 	return loader->auto_detected_encoding;
 }
 
+GeditDocumentNewlineType
+gedit_document_loader_get_newline_type (GeditDocumentLoader *loader)
+{
+	g_return_val_if_fail (GEDIT_IS_DOCUMENT_LOADER (loader),
+			      GEDIT_DOCUMENT_NEWLINE_TYPE_LF);
+
+	return loader->auto_detected_newline_type;
+}
+
 GFileInfo *
 gedit_document_loader_get_info (GeditDocumentLoader *loader)
 {
diff --git a/gedit/gedit-document-loader.h b/gedit/gedit-document-loader.h
index 84bfcfb..a627fba 100644
--- a/gedit/gedit-document-loader.h
+++ b/gedit/gedit-document-loader.h
@@ -66,6 +66,7 @@ struct _GeditDocumentLoader
 	gchar			 *uri;
 	const GeditEncoding	 *encoding;
 	const GeditEncoding	 *auto_detected_encoding;
+	GeditDocumentNewlineType  auto_detected_newline_type;
 };
 
 /*
@@ -116,6 +117,8 @@ const gchar		*gedit_document_loader_get_uri		(GeditDocumentLoader *loader);
 
 const GeditEncoding	*gedit_document_loader_get_encoding	(GeditDocumentLoader *loader);
 
+GeditDocumentNewlineType gedit_document_loader_get_newline_type (GeditDocumentLoader *loader);
+
 goffset			 gedit_document_loader_get_bytes_read	(GeditDocumentLoader *loader);
 
 /* You can get from the info: content_type, time_modified, standard_size, access_can_write 
diff --git a/gedit/gedit-document-saver.c b/gedit/gedit-document-saver.c
index 1e58479..23377a8 100644
--- a/gedit/gedit-document-saver.c
+++ b/gedit/gedit-document-saver.c
@@ -66,6 +66,7 @@ enum {
 	PROP_DOCUMENT,
 	PROP_URI,
 	PROP_ENCODING,
+	PROP_NEWLINE_TYPE,
 	PROP_FLAGS
 };
 
@@ -91,6 +92,9 @@ gedit_document_saver_set_property (GObject      *object,
 			g_return_if_fail (saver->encoding == NULL);
 			saver->encoding = g_value_get_boxed (value);
 			break;
+		case PROP_NEWLINE_TYPE:
+			saver->newline_type = g_value_get_enum (value);
+			break;
 		case PROP_FLAGS:
 			saver->flags = g_value_get_flags (value);
 			break;
@@ -119,6 +123,9 @@ gedit_document_saver_get_property (GObject    *object,
 		case PROP_ENCODING:
 			g_value_set_boxed (value, saver->encoding);
 			break;
+		case PROP_NEWLINE_TYPE:
+			g_value_set_enum (value, saver->newline_type);
+			break;
 		case PROP_FLAGS:
 			g_value_set_flags (value, saver->flags);
 			break;
@@ -194,6 +201,18 @@ gedit_document_saver_class_init (GeditDocumentSaverClass *klass)
 							     G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property (object_class,
+					 PROP_NEWLINE_TYPE,
+					 g_param_spec_enum ("newline-type",
+					                    "Newline type",
+					                    "The accepted types of line ending",
+					                    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE,
+					                    GEDIT_DOCUMENT_NEWLINE_TYPE_LF,
+					                    G_PARAM_READWRITE |
+					                    G_PARAM_STATIC_NAME |
+					                    G_PARAM_STATIC_BLURB |
+					                    G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class,
 					 PROP_FLAGS,
 					 g_param_spec_flags ("flags",
 							     "Flags",
@@ -223,22 +242,18 @@ gedit_document_saver_init (GeditDocumentSaver *saver)
 }
 
 GeditDocumentSaver *
-gedit_document_saver_new (GeditDocument       *doc,
-			  const gchar         *uri,
-			  const GeditEncoding *encoding,
-			  GeditDocumentSaveFlags flags)
+gedit_document_saver_new (GeditDocument           *doc,
+			  const gchar             *uri,
+			  const GeditEncoding     *encoding,
+			  GeditDocumentNewlineType newline_type,
+			  GeditDocumentSaveFlags   flags)
 {
 	GeditDocumentSaver *saver;
 	GType saver_type;
 
 	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), NULL);
 
-#ifndef G_OS_WIN32
-	if (gedit_utils_uri_has_file_scheme (uri))
-		saver_type = GEDIT_TYPE_LOCAL_DOCUMENT_SAVER;
-	else
-#endif
-		saver_type = GEDIT_TYPE_GIO_DOCUMENT_SAVER;
+	saver_type = GEDIT_TYPE_GIO_DOCUMENT_SAVER;
 
 	if (encoding == NULL)
 		encoding = gedit_encoding_get_utf8 ();
@@ -247,6 +262,7 @@ gedit_document_saver_new (GeditDocument       *doc,
 						    "document", doc,
 						    "uri", uri,
 						    "encoding", encoding,
+						    "newline_type", newline_type,
 						    "flags", flags,
 						    NULL));
 
diff --git a/gedit/gedit-document-saver.h b/gedit/gedit-document-saver.h
index 1e4d979..52c1cf2 100644
--- a/gedit/gedit-document-saver.h
+++ b/gedit/gedit-document-saver.h
@@ -62,6 +62,7 @@ struct _GeditDocumentSaver
 
 	gchar			 *uri;
 	const GeditEncoding      *encoding;
+	GeditDocumentNewlineType  newline_type;
 
 	GeditDocumentSaveFlags    flags;
 
@@ -97,10 +98,11 @@ struct _GeditDocumentSaverClass
 GType 		 	 gedit_document_saver_get_type		(void) G_GNUC_CONST;
 
 /* If enconding == NULL, the encoding will be autodetected */
-GeditDocumentSaver 	*gedit_document_saver_new 		(GeditDocument        *doc,
-								 const gchar          *uri,
-								 const GeditEncoding  *encoding,
-								 GeditDocumentSaveFlags flags);
+GeditDocumentSaver 	*gedit_document_saver_new 		(GeditDocument           *doc,
+								 const gchar             *uri,
+								 const GeditEncoding     *encoding,
+								 GeditDocumentNewlineType newline_type,
+								 GeditDocumentSaveFlags   flags);
 
 gchar			*gedit_document_saver_get_document_contents (
 								 GeditDocumentSaver  *saver,
diff --git a/gedit/gedit-document.c b/gedit/gedit-document.c
index d7d09be..b35e952 100644
--- a/gedit/gedit-document.c
+++ b/gedit/gedit-document.c
@@ -118,6 +118,8 @@ struct _GeditDocumentPrivate
 	gchar       *search_text;
 	gint	     num_of_lines_search_text;
 
+	GeditDocumentNewlineType newline_type;
+
 	/* Temp data while loading */
 	GeditDocumentLoader *loader;
 	gboolean             create; /* Create file if uri points 
@@ -153,7 +155,8 @@ enum {
 	PROP_READ_ONLY,
 	PROP_ENCODING,
 	PROP_CAN_SEARCH_AGAIN,
-	PROP_ENABLE_SEARCH_HIGHLIGHTING
+	PROP_ENABLE_SEARCH_HIGHLIGHTING,
+	PROP_NEWLINE_TYPE
 };
 
 enum {
@@ -342,6 +345,9 @@ gedit_document_get_property (GObject    *object,
 		case PROP_ENABLE_SEARCH_HIGHLIGHTING:
 			g_value_set_boolean (value, gedit_document_get_enable_search_highlighting (doc));
 			break;
+		case PROP_NEWLINE_TYPE:
+			g_value_set_enum (value, doc->priv->newline_type);
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -361,6 +367,11 @@ gedit_document_set_property (GObject      *object,
 		case PROP_ENABLE_SEARCH_HIGHLIGHTING:
 			gedit_document_set_enable_search_highlighting (doc,
 								       g_value_get_boolean (value));
+			break;
+		case PROP_NEWLINE_TYPE:
+			gedit_document_set_newline_type (doc,
+							 g_value_get_enum (value));
+			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -485,6 +496,22 @@ gedit_document_class_init (GeditDocumentClass *klass)
 							       G_PARAM_READWRITE |
 							       G_PARAM_STATIC_STRINGS));
 
+	/**
+	 * GeditDocument:newline-type:
+	 *
+	 * The :newline-type property determines what is considered
+	 * as a line ending when saving the document
+	 */
+	g_object_class_install_property (object_class, PROP_NEWLINE_TYPE,
+	                                 g_param_spec_enum ("newline-type",
+	                                                    "Newline type",
+	                                                    "The accepted types of line ending",
+	                                                    GEDIT_TYPE_DOCUMENT_NEWLINE_TYPE,
+	                                                    GEDIT_DOCUMENT_NEWLINE_TYPE_LF,
+	                                                    G_PARAM_READWRITE |
+	                                                    G_PARAM_STATIC_NAME |
+	                                                    G_PARAM_STATIC_BLURB));
+
 	/* This signal is used to update the cursor position is the statusbar,
 	 * it's emitted either when the insert mark is moved explicitely or
 	 * when the buffer changes (insert/delete).
@@ -803,6 +830,8 @@ gedit_document_init (GeditDocument *doc)
 
 	doc->priv->encoding = gedit_encoding_get_utf8 ();
 
+	doc->priv->newline_type = GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT;
+
 	gtk_source_buffer_set_max_undo_levels (GTK_SOURCE_BUFFER (doc), 
 					       gedit_prefs_manager_get_undo_actions_limit ());
 
@@ -1158,9 +1187,13 @@ document_loader_loaded (GeditDocumentLoader *loader,
 		set_encoding (doc, 
 			      gedit_document_loader_get_encoding (loader),
 			      (doc->priv->requested_encoding != NULL));
-		
+
+
 		set_content_type (doc, content_type);
 
+		gedit_document_set_newline_type (doc,
+		                                 gedit_document_loader_get_newline_type (loader));
+
 		/* move the cursor at the requested line if any */
 		if (doc->priv->requested_line_pos > 0)
 		{
@@ -1412,7 +1445,9 @@ 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, uri, encoding, flags);
+	doc->priv->saver = gedit_document_saver_new (doc, uri, encoding,
+						     doc->priv->newline_type,
+						     flags);
 
 	g_signal_connect (doc->priv->saver,
 			  "saving",
@@ -2406,6 +2441,28 @@ gedit_document_get_enable_search_highlighting (GeditDocument *doc)
 }
 
 void
+gedit_document_set_newline_type (GeditDocument           *doc,
+				 GeditDocumentNewlineType newline_type)
+{
+	g_return_if_fail (GEDIT_IS_DOCUMENT (doc));
+
+	if (doc->priv->newline_type != newline_type)
+	{
+		doc->priv->newline_type = newline_type;
+
+		g_object_notify (G_OBJECT (doc), "newline-type");
+	}
+}
+
+GeditDocumentNewlineType
+gedit_document_get_newline_type (GeditDocument *doc)
+{
+	g_return_val_if_fail (GEDIT_IS_DOCUMENT (doc), 0);
+
+	return doc->priv->newline_type;
+}
+
+void
 _gedit_document_set_mount_operation_factory (GeditDocument 	       *doc,
 					    GeditMountOperationFactory	callback,
 					    gpointer	                userdata)
diff --git a/gedit/gedit-document.h b/gedit/gedit-document.h
index 0b8fde9..a5d2730 100644
--- a/gedit/gedit-document.h
+++ b/gedit/gedit-document.h
@@ -63,6 +63,19 @@ G_BEGIN_DECLS
 
 typedef enum
 {
+	GEDIT_DOCUMENT_NEWLINE_TYPE_LF,
+	GEDIT_DOCUMENT_NEWLINE_TYPE_CR,
+	GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF
+} GeditDocumentNewlineType;
+
+#ifdef G_OS_WIN32
+#define GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT GEDIT_DOCUMENT_NEWLINE_TYPE_CR_LF
+#else
+#define GEDIT_DOCUMENT_NEWLINE_TYPE_DEFAULT GEDIT_DOCUMENT_NEWLINE_TYPE_LF
+#endif
+
+typedef enum
+{
 	GEDIT_SEARCH_DONT_SET_FLAGS	= 1 << 0, 
 	GEDIT_SEARCH_ENTIRE_WORD	= 1 << 1,
 	GEDIT_SEARCH_CASE_SENSITIVE	= 1 << 2
@@ -257,6 +270,12 @@ void		 gedit_document_set_enable_search_highlighting
 gboolean	 gedit_document_get_enable_search_highlighting
 						(GeditDocument       *doc);
 
+void		 gedit_document_set_newline_type (GeditDocument           *doc,
+						  GeditDocumentNewlineType newline_type);
+
+GeditDocumentNewlineType
+		 gedit_document_get_newline_type (GeditDocument *doc);
+
 gchar		*gedit_document_get_metadata	(GeditDocument *doc,
 						 const gchar   *key);
 
diff --git a/gedit/gedit-gio-document-loader.c b/gedit/gedit-gio-document-loader.c
index 1a44dbe..fa95673 100644
--- a/gedit/gedit-gio-document-loader.c
+++ b/gedit/gedit-gio-document-loader.c
@@ -270,26 +270,91 @@ append_text_to_document (GeditDocumentLoader *loader,
 	gtk_text_buffer_insert (GTK_TEXT_BUFFER (doc), &end, text, len);
 }
 
+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;
+}
+
 static void
-end_append_text_to_document (GeditDocumentLoader *loader)
+detect_newline_type (GeditDocumentLoader *loader)
 {
-	GtkTextIter start, end;
+	GtkTextIter iter;
+
+	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (loader->document), &iter);
+
+	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);
+	}
+}
+
+static void
+remove_ending_newline (GeditDocumentLoader *loader)
+{
+	GtkTextIter end;
+	GtkTextIter start;
 
-	/* If the last char is a newline, remove it from the buffer (otherwise
-	   GtkTextView shows it as an empty line). See bug #324942. */
 	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (loader->document), &end);
 	start = end;
 
-	if (gtk_text_iter_backward_char (&start))
-	{
-		gunichar c;
+	gtk_text_iter_set_line_offset (&start, 0);
 
-		c = gtk_text_iter_get_char (&start);
+	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);
+		}
 
-		if (g_unichar_break_type (c) == G_UNICODE_BREAK_LINE_FEED)
-			gtk_text_buffer_delete (GTK_TEXT_BUFFER (loader->document),
-						&start, &end);
+		/* Delete the empty line which is from 'start' to 'end' */
+		gtk_text_buffer_delete (GTK_TEXT_BUFFER (loader->document),
+		                        &start,
+		                        &end);
 	}
+}
+
+static void
+end_append_text_to_document (GeditDocumentLoader *loader)
+{
+	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);
 
diff --git a/gedit/gedit-gio-document-saver.c b/gedit/gedit-gio-document-saver.c
index fbd1256..5e138f6 100644
--- a/gedit/gedit-gio-document-saver.c
+++ b/gedit/gedit-gio-document-saver.c
@@ -37,19 +37,22 @@
 #include <gio/gio.h>
 #include <string.h>
 
-#include "gedit-convert.h"
 #include "gedit-gio-document-saver.h"
+#include "gedit-document-input-stream.h"
 #include "gedit-debug.h"
 
+#define WRITE_CHUNK_SIZE 8192
+
 typedef struct
 {
 	GeditGioDocumentSaver *saver;
-	gchar 		      *buffer;
+	gchar 		       buffer[WRITE_CHUNK_SIZE];
 	GCancellable 	      *cancellable;
 	gboolean	       tried_mount;
+	gsize		       written;
+	gsize		       read;
 } AsyncData;
 
-#define WRITE_CHUNK_SIZE 8192
 #define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
 				G_FILE_ATTRIBUTE_TIME_MODIFIED
 				
@@ -74,7 +77,8 @@ struct _GeditGioDocumentSaverPrivate
 
 	GFile			 *gfile;
 	GCancellable		 *cancellable;
-	GFileOutputStream	 *stream;
+	GOutputStream		 *stream;
+	GInputStream		 *input;
 	
 	GError                   *error;
 };
@@ -82,26 +86,36 @@ struct _GeditGioDocumentSaverPrivate
 G_DEFINE_TYPE(GeditGioDocumentSaver, gedit_gio_document_saver, GEDIT_TYPE_DOCUMENT_SAVER)
 
 static void
-gedit_gio_document_saver_finalize (GObject *object)
+gedit_gio_document_saver_dispose (GObject *object)
 {
 	GeditGioDocumentSaverPrivate *priv = GEDIT_GIO_DOCUMENT_SAVER (object)->priv;
 
-	if (priv->cancellable)
+	if (priv->cancellable != NULL)
 	{
 		g_cancellable_cancel (priv->cancellable);
 		g_object_unref (priv->cancellable);
+		priv->cancellable = NULL;
 	}
 
-	if (priv->gfile)
+	if (priv->gfile != NULL)
+	{
 		g_object_unref (priv->gfile);
+		priv->gfile = NULL;
+	}
 
-	if (priv->error)
+	if (priv->error != NULL)
+	{
 		g_error_free (priv->error);
-		
-	if (priv->stream)
+		priv->error = NULL;
+	}
+
+	if (priv->stream != NULL)
+	{
 		g_object_unref (priv->stream);
+		priv->stream = NULL;
+	}
 
-	G_OBJECT_CLASS (gedit_gio_document_saver_parent_class)->finalize (object);
+	G_OBJECT_CLASS (gedit_gio_document_saver_parent_class)->dispose (object);
 }
 
 static AsyncData *
@@ -111,9 +125,10 @@ async_data_new (GeditGioDocumentSaver *gvsaver)
 	
 	async = g_slice_new (AsyncData);
 	async->saver = gvsaver;
-	async->buffer = NULL;
 	async->cancellable = g_object_ref (gvsaver->priv->cancellable);
 	async->tried_mount = FALSE;
+	async->written = 0;
+	async->read = 0;
 	
 	return async;
 }
@@ -122,7 +137,6 @@ static void
 async_data_free (AsyncData *async)
 {
 	g_object_unref (async->cancellable);
-	g_free (async->buffer);
 	g_slice_free (AsyncData, async);
 }
 
@@ -132,7 +146,7 @@ gedit_gio_document_saver_class_init (GeditGioDocumentSaverClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	GeditDocumentSaverClass *saver_class = GEDIT_DOCUMENT_SAVER_CLASS (klass);
 
-	object_class->finalize = gedit_gio_document_saver_finalize;
+	object_class->finalize = gedit_gio_document_saver_dispose;
 
 	saver_class->save = gedit_gio_document_saver_save;
 	saver_class->get_file_size = gedit_gio_document_saver_get_file_size;
@@ -171,9 +185,6 @@ async_failed (AsyncData *async,
 	remote_save_completed_or_failed (async->saver, async);
 }
 
-/* prototype, because they call each other... isn't C lovely */
-static void write_file_chunk (AsyncData *async);
-
 static void
 remote_reget_info_cb (GFile        *source,
 		      GAsyncResult *res,
@@ -321,6 +332,7 @@ remote_get_info_cb (GFileOutputStream *stream,
 		next_callback = (GAsyncReadyCallback) close_async_ready_cb;
 	}
 
+	/* Close the main stream so the file stream is also closed */
 	g_output_stream_close_async (G_OUTPUT_STREAM (saver->priv->stream),
 				     G_PRIORITY_HIGH,
 				     async->cancellable,
@@ -328,17 +340,28 @@ remote_get_info_cb (GFileOutputStream *stream,
 				     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
 write_complete (AsyncData *async)
 {
+	GOutputStream *file_stream;
+
 	/* document is succesfully saved. we know requery for the mime type and
 	 * the mtime. I'm not sure this is actually necessary, can't we just use
 	 * g_content_type_guess (since we have the file name and the data)
 	 */
 	gedit_debug_message (DEBUG_SAVER, "Write complete, query info on stream");
+
+	if (G_IS_FILE_OUTPUT_STREAM (async->saver->priv->stream))
+		file_stream = async->saver->priv->stream;
+	else
+		file_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (async->saver->priv->stream));
 	
 	/* query info on the stream */
-	g_file_output_stream_query_info_async (async->saver->priv->stream, 
+	g_file_output_stream_query_info_async (G_FILE_OUTPUT_STREAM (file_stream),
 					       REMOTE_QUERY_ATTRIBUTES,
 					       G_PRIORITY_HIGH,
 					       async->cancellable,
@@ -348,23 +371,20 @@ write_complete (AsyncData *async)
 
 static void
 async_write_cb (GOutputStream *stream,
-	        GAsyncResult  *res,
-	        AsyncData     *async)
+		GAsyncResult  *res,
+		AsyncData     *async)
 {
-	GError *error = NULL;
 	GeditGioDocumentSaver *gvsaver;
 	gssize bytes_written;
-	
-	gedit_debug (DEBUG_SAVER);
-	
-	/* manually check cancelled state */
+	GError *error = NULL;
+
+	/* Check cancelled state manually */
 	if (g_cancellable_is_cancelled (async->cancellable))
 	{
 		async_data_free (async);
 		return;
 	}
 
-	gvsaver = async->saver;
 	bytes_written = g_output_stream_write_finish (stream, res, &error);
 
 	gedit_debug_message (DEBUG_SAVER, "Written: %" G_GSSIZE_FORMAT, bytes_written);
@@ -375,17 +395,15 @@ async_write_cb (GOutputStream *stream,
 		async_failed (async, error);
 		return;
 	}
-	
-	gvsaver->priv->bytes_written += bytes_written;
-	
-	/* if nothing is written we're done */
-	if (gvsaver->priv->bytes_written == gvsaver->priv->size)
+
+	gvsaver = async->saver;
+	async->written += bytes_written;
+
+	/* write again */
+	if (async->written != async->read)
 	{
-		write_complete (async);
-		return;
+		write_file_chunk (async);
 	}
-	
-	/* otherwise emit progress and write some more */
 
 	/* note that this signal blocks the write... check if it isn't
 	 * a performance problem
@@ -394,7 +412,7 @@ async_write_cb (GOutputStream *stream,
 				     FALSE,
 				     NULL);
 
-	write_file_chunk (async);
+	read_file_chunk (async);
 }
 
 static void
@@ -404,14 +422,9 @@ write_file_chunk (AsyncData *async)
 
 	gvsaver = async->saver;
 
-	gedit_debug_message (DEBUG_SAVER,
-			     "Writing next chunk: %" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
-			     gvsaver->priv->bytes_written, gvsaver->priv->size);
-
 	g_output_stream_write_async (G_OUTPUT_STREAM (gvsaver->priv->stream),
-				     async->buffer + gvsaver->priv->bytes_written,
-				     MIN (WRITE_CHUNK_SIZE, gvsaver->priv->size - 
-				     			    gvsaver->priv->bytes_written),
+				     async->buffer + async->written,
+				     async->read - async->written,
 				     G_PRIORITY_HIGH,
 				     async->cancellable,
 				     (GAsyncReadyCallback) async_write_cb,
@@ -419,11 +432,70 @@ write_file_chunk (AsyncData *async)
 }
 
 static void
+async_read_cb (GInputStream *stream,
+	       GAsyncResult *res,
+	       AsyncData    *async)
+{
+	GeditGioDocumentSaver *gvsaver;
+	GeditDocumentInputStream *dstream;
+	GError *error = NULL;
+
+	if (g_cancellable_is_cancelled (async->cancellable))
+	{
+		async_data_free (async);
+		return;
+	}
+
+	gvsaver = async->saver;
+
+	async->read = g_input_stream_read_finish (stream, res, &error);
+
+	if (error != NULL)
+	{
+		async_failed (async, error);
+		return;
+	}
+
+	/* Check if we finished reading and writing */
+	if (async->read == 0)
+	{
+		write_complete (async);
+		return;
+	}
+
+	/* Get how many chars have been read */
+	dstream = GEDIT_DOCUMENT_INPUT_STREAM (stream);
+	gvsaver->priv->bytes_written += gedit_document_input_stream_tell (dstream);
+
+	write_file_chunk (async);
+}
+
+static void
+read_file_chunk (AsyncData *async)
+{
+	GeditGioDocumentSaver *gvsaver;
+
+	gvsaver = async->saver;
+	async->written = 0;
+
+	g_input_stream_read_async (gvsaver->priv->input,
+				   async->buffer,
+				   WRITE_CHUNK_SIZE,
+				   G_PRIORITY_HIGH,
+				   async->cancellable,
+				   (GAsyncReadyCallback) async_read_cb,
+				   async);
+}
+
+static void
 async_replace_ready_callback (GFile        *source,
 			      GAsyncResult *res,
 			      AsyncData    *async)
 {
 	GeditGioDocumentSaver *gvsaver;
+	GeditDocumentSaver *saver;
+	GCharsetConverter *converter;
+	GFileOutputStream *file_stream;
 	GError *error = NULL;
 
 	/* Check cancelled state manually */
@@ -434,71 +506,49 @@ async_replace_ready_callback (GFile        *source,
 	}
 	
 	gvsaver = async->saver;
-	gvsaver->priv->stream = g_file_replace_finish (source, res, &error);
+	saver = GEDIT_DOCUMENT_SAVER (gvsaver);
+	file_stream = g_file_replace_finish (source, res, &error);
 	
 	/* handle any error that might occur */
-	if (!gvsaver->priv->stream)
+	if (!file_stream)
 	{
 		gedit_debug_message (DEBUG_SAVER, "Opening file failed: %s", error->message);
 		async_failed (async, error);
 		return;
 	}
 
-	/* now that we have the stream, start writing async to it */
-	write_file_chunk (async);
-}
-
-/* returns a pointer to the reallocated buffer */
-static gchar *
-append_new_line (GeditGioDocumentSaver  *gvsaver, 
-		 gchar 		        *buffer, 
-		 gsize 		        *len, 
-		 GError 	       **error)
-{
-	GeditDocumentSaver *saver = GEDIT_DOCUMENT_SAVER (gvsaver);
-	gsize n_len;
-	gchar *n_buffer;
-	gchar *res;
-
-	res = buffer;
+	/* FIXME: manage converter error? */
+	gedit_debug_message (DEBUG_SAVER, "Encoding charset: %s",
+			     gedit_encoding_get_charset (saver->encoding));
 
-	n_buffer = gedit_document_saver_get_end_newline (saver, &n_len);
-	if (n_buffer != NULL)
+	if (saver->encoding != gedit_encoding_get_utf8 ())
 	{
-		gchar *new_buffer;
-
-		new_buffer = g_try_realloc (buffer, *len + n_len + 1);
-
-		if (new_buffer == NULL)
-		{
-			/* we do not error out, just use the buffer
-			   without the ending newline */
-			g_free (n_buffer);
-			g_warning ("Cannot add '\\n' at the end of the file.");
-
-			return res;
-		}
-
-		/* Copy newline */
-		memcpy (new_buffer + *len, n_buffer, n_len);
-		g_free (n_buffer);
-		*len += n_len;
-
-		new_buffer[*len] = '\0';
-
-		res = new_buffer;
+		converter = g_charset_converter_new (gedit_encoding_get_charset (saver->encoding),
+						     "UTF-8",
+						     NULL);
+		gvsaver->priv->stream = g_converter_output_stream_new (G_OUTPUT_STREAM (file_stream),
+								       G_CONVERTER (converter));
+
+		g_object_unref (file_stream);
+		g_object_unref (converter);
 	}
+	else
+	{
+		gvsaver->priv->stream = G_OUTPUT_STREAM (file_stream);
+	}
+	
+	gvsaver->priv->input = gedit_document_input_stream_new (GTK_TEXT_BUFFER (saver->document),
+								saver->newline_type);
+
+	gvsaver->priv->size = gedit_document_input_stream_get_total_size (GEDIT_DOCUMENT_INPUT_STREAM (gvsaver->priv->input));
 
-	return res;
+	read_file_chunk (async);
 }
 
 static void
 begin_write (AsyncData *async)
 {
 	GeditGioDocumentSaver *gvsaver;
-	gchar *buffer;
-	gsize len;
-	GError *error = NULL;
 
 	gedit_debug_message (DEBUG_SAVER, "Start replacing file contents");
 
@@ -506,25 +556,11 @@ begin_write (AsyncData *async)
 	 * backup as of yet
 	 */
 	gvsaver = async->saver;
-	buffer = gedit_document_saver_get_document_contents (GEDIT_DOCUMENT_SAVER (gvsaver), &len, &error);
-	if (buffer != NULL && len > 0)
-	{
-		/* Append new line to buffer */
-		buffer = append_new_line (gvsaver, buffer, &len, &error);
-	}
-
-	if (!buffer)
-	{
-		async_failed (async, error);
-		return;
-	}
-
-	async->buffer = buffer;
-	gvsaver->priv->size = len;
 
 	gedit_debug_message (DEBUG_SAVER, "File contents size: %" G_GINT64_FORMAT, gvsaver->priv->size);
 	gedit_debug_message (DEBUG_SAVER, "Calling replace_async");
 
+	/* FIXME: when do we want to make a backup? */
 	g_file_replace_async (gvsaver->priv->gfile, 
 			      NULL,
 			      FALSE,
@@ -580,9 +616,9 @@ recover_not_mounted (AsyncData *async)
 				       mount_operation,
 				       async->cancellable,
 				       (GAsyncReadyCallback) mount_ready_callback,
-				       async);	
+				       async);
 
-	g_object_unref (mount_operation);			
+	g_object_unref (mount_operation);
 }
 
 static void



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