[gtksourceview/wip/loader-saver] GtkSourceFileLoader (not finished!)



commit b8b283e717e960281af8f3b94236f1fc4dd8b7cc
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Thu Jan 9 19:05:52 2014 +0100

    GtkSourceFileLoader (not finished!)

 docs/reference/Makefile.am             |    1 +
 gtksourceview/Makefile.am              |    2 +
 gtksourceview/gtksourcefileloader.c    | 1054 ++++++++++++++++++++++++++++++++
 gtksourceview/gtksourcefileloader.h    |  115 ++++
 gtksourceview/gtksourcetypes-private.h |    1 +
 po/POTFILES.in                         |    1 +
 6 files changed, 1174 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 6c72ead..ab6c2ed 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -33,6 +33,7 @@ IGNORE_HFILES =                                       \
        gtksourcecompletionwordsutils.h         \
        gtksourcecontextengine.h                \
        gtksourceengine.h                       \
+       gtksourcefileloader.h                   \
        gtksourcefilesaver.h                    \
        gtksourcegutter-private.h               \
        gtksourcegutterrendererlines.h          \
diff --git a/gtksourceview/Makefile.am b/gtksourceview/Makefile.am
index 937ee3f..7151559 100644
--- a/gtksourceview/Makefile.am
+++ b/gtksourceview/Makefile.am
@@ -60,6 +60,7 @@ libgtksourceview_private_headers = \
        gtksourcecompletion-private.h           \
        gtksourcecontextengine.h                \
        gtksourceengine.h                       \
+       gtksourcefileloader.h                   \
        gtksourcefilesaver.h                    \
        gtksourcegutter-private.h               \
        gtksourcegutterrendererlines.h          \
@@ -83,6 +84,7 @@ libgtksourceview_private_c_files = \
        gtksourcecompletionmodel.c      \
        gtksourcecontextengine.c        \
        gtksourceengine.c               \
+       gtksourcefileloader.c           \
        gtksourcefilesaver.c            \
        gtksourcegutterrendererlines.c  \
        gtksourcegutterrenderermarks.c  \
diff --git a/gtksourceview/gtksourcefileloader.c b/gtksourceview/gtksourcefileloader.c
new file mode 100644
index 0000000..ce99db2
--- /dev/null
+++ b/gtksourceview/gtksourcefileloader.c
@@ -0,0 +1,1054 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* gtksourcefileloader.c
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2005 - Paolo Maggi
+ * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux
+ * Copyright (C) 2008 - Jesse van den Kieboom
+ * Copyright (C) 2014 - Sébastien Wilmet
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/*#include <glib/gstdio.h>*/
+#include <gio/gio.h>
+#include "gtksourcefileloader.h"
+#include "gtksourcebufferoutputstream.h"
+#include "gtksourceview-marshal.h"
+#include "gtksourceview-typebuiltins.h"
+#include "gtksourceview-i18n.h"
+
+/*
+#include "gtk_source-utils.h"
+#include "gtk_source-settings.h"
+*/
+
+#if 0
+#define DEBUG(x) (x)
+#else
+#define DEBUG(x)
+#endif
+
+typedef struct
+{
+       GtkSourceFileLoader *loader;
+       GCancellable *cancellable;
+
+       gssize read;
+       gboolean tried_mount;
+} AsyncData;
+
+enum
+{
+       LOADING,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum
+{
+       PROP_0,
+       PROP_BUFFER,
+       PROP_LOCATION,
+       PROP_ENCODING,
+       PROP_NEWLINE_TYPE,
+       PROP_STREAM,
+       PROP_COMPRESSION_TYPE
+};
+
+#define READ_CHUNK_SIZE 8192
+#define LOADER_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
+                               G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
+                               G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
+                               G_FILE_ATTRIBUTE_STANDARD_SIZE "," \
+                               G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," \
+                               GTK_SOURCE_METADATA_ATTRIBUTE_ENCODING
+
+struct _GtkSourceFileLoaderPrivate
+{
+       /* TODO replace by properties, or remove */
+       GSettings *enc_settings;
+       GSettings *editor_settings;
+
+       GtkSourceBuffer *document; /* TODO rename */
+       gboolean used; /* FIXME is used used? */
+
+       /* Info on the current file */
+       GFileInfo *info;
+       GFile *location;
+       const GtkSourceEncoding *encoding;
+       const GtkSourceEncoding *auto_detected_encoding;
+       GtkSourceNewlineType auto_detected_newline_type;
+       GtkSourceCompressionType auto_detected_compression_type;
+
+       goffset bytes_read;
+
+       GCancellable *cancellable;
+
+       /* TODO rename */
+       GInputStream *stream;
+       GOutputStream *output;
+
+       /* TODO rename the field */
+       /* FIXME is it not better to allocate this field separately? I think the
+        * private struct is allocated with GSlice by GObject, and GSlice is
+        * better to use only for small struct, no?
+        */
+       gchar buffer[READ_CHUNK_SIZE];
+
+       GError *error;
+       gboolean guess_content_type_from_content;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceFileLoader, gtk_source_file_loader, G_TYPE_OBJECT)
+
+static void open_async_read (AsyncData *async);
+static void read_file_chunk (AsyncData *async);
+
+static void
+gtk_source_file_loader_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+       GtkSourceFileLoader *loader = GTK_SOURCE_FILE_LOADER (object);
+
+       switch (prop_id)
+       {
+               case PROP_BUFFER:
+                       g_assert (loader->priv->document == NULL);
+                       loader->priv->document = g_value_get_object (value);
+                       break;
+
+               case PROP_LOCATION:
+                       g_assert (loader->priv->location == NULL);
+                       loader->priv->location = g_value_dup_object (value);
+                       break;
+
+               case PROP_ENCODING:
+                       g_assert (loader->priv->encoding == NULL);
+                       loader->priv->encoding = g_value_get_boxed (value);
+                       break;
+
+               case PROP_NEWLINE_TYPE:
+                       loader->priv->auto_detected_newline_type = g_value_get_enum (value);
+                       break;
+
+               case PROP_STREAM:
+                       g_assert (loader->priv->stream == NULL);
+                       loader->priv->stream = g_value_dup_object (value);
+                       break;
+
+               case PROP_COMPRESSION_TYPE:
+                       loader->priv->auto_detected_compression_type = g_value_get_enum (value);
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+gtk_source_file_loader_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+       GtkSourceFileLoader *loader = GTK_SOURCE_FILE_LOADER (object);
+
+       switch (prop_id)
+       {
+               case PROP_BUFFER:
+                       g_value_set_object (value, loader->priv->document);
+                       break;
+
+               case PROP_LOCATION:
+                       g_value_set_object (value, loader->priv->location);
+                       break;
+
+               case PROP_ENCODING:
+                       g_value_set_boxed (value, gtk_source_file_loader_get_encoding (loader));
+                       break;
+
+               case PROP_NEWLINE_TYPE:
+                       g_value_set_enum (value, loader->priv->auto_detected_newline_type);
+                       break;
+
+               case PROP_STREAM:
+                       g_value_set_object (value, loader->priv->stream);
+                       break;
+
+               case PROP_COMPRESSION_TYPE:
+                       g_value_set_enum (value, loader->priv->auto_detected_compression_type);
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                       break;
+       }
+}
+
+static void
+gtk_source_file_loader_dispose (GObject *object)
+{
+       GtkSourceFileLoaderPrivate *priv;
+
+       priv = GTK_SOURCE_FILE_LOADER (object)->priv;
+
+       if (priv->cancellable != NULL)
+       {
+               g_cancellable_cancel (priv->cancellable);
+               g_object_unref (priv->cancellable);
+               priv->cancellable = NULL;
+       }
+
+       g_clear_error (&priv->error);
+
+       g_clear_object (&priv->stream);
+       g_clear_object (&priv->output);
+       g_clear_object (&priv->info);
+       g_clear_object (&priv->location);
+       g_clear_object (&priv->enc_settings);
+       g_clear_object (&priv->editor_settings);
+
+       G_OBJECT_CLASS (gtk_source_file_loader_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_file_loader_class_init (GtkSourceFileLoaderClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->dispose = gtk_source_file_loader_dispose;
+       object_class->get_property = gtk_source_file_loader_get_property;
+       object_class->set_property = gtk_source_file_loader_set_property;
+
+       g_object_class_install_property (object_class, PROP_BUFFER,
+                                        g_param_spec_object ("buffer",
+                                                             "Buffer",
+                                                             "The associated GtkSourceBuffer",
+                                                             GTK_SOURCE_TYPE_BUFFER,
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (object_class, PROP_LOCATION,
+                                        g_param_spec_object ("location",
+                                                             "Location",
+                                                             "The location of the file to load",
+                                                             G_TYPE_FILE,
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (object_class, PROP_ENCODING,
+                                        g_param_spec_boxed ("encoding",
+                                                            "Encoding",
+                                                            "The encoding of the file",
+                                                            GTK_SOURCE_TYPE_ENCODING,
+                                                            G_PARAM_READWRITE |
+                                                            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 type of line ending",
+                                                           GTK_SOURCE_TYPE_NEWLINE_TYPE,
+                                                           GTK_SOURCE_NEWLINE_TYPE_LF,
+                                                           G_PARAM_READWRITE |
+                                                           G_PARAM_CONSTRUCT |
+                                                           G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (object_class, PROP_COMPRESSION_TYPE,
+                                        g_param_spec_enum ("compression-type",
+                                                           "Compression type",
+                                                           "The compression type",
+                                                           GTK_SOURCE_TYPE_COMPRESSION_TYPE,
+                                                           GTK_SOURCE_COMPRESSION_TYPE_NONE,
+                                                           G_PARAM_READWRITE |
+                                                           G_PARAM_CONSTRUCT |
+                                                           G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (object_class, PROP_STREAM,
+                                        g_param_spec_object ("stream",
+                                                             "Stream",
+                                                             "The input stream to load",
+                                                             G_TYPE_INPUT_STREAM,
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_STATIC_STRINGS));
+
+       signals[LOADING] =
+               g_signal_new ("loading",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (GtkSourceFileLoaderClass, loading),
+                             NULL, NULL,
+                             gtk_source_marshal_VOID__BOOLEAN_POINTER,
+                             G_TYPE_NONE,
+                             2,
+                             G_TYPE_BOOLEAN,
+                             G_TYPE_POINTER);
+}
+
+static void
+gtk_source_file_loader_init (GtkSourceFileLoader *loader)
+{
+       loader->priv = gtk_source_file_loader_get_instance_private (loader);
+}
+
+GtkSourceFileLoader *
+gtk_source_file_loader_new_from_stream (GtkSourceBuffer         *buffer,
+                                       GInputStream            *stream,
+                                       const GtkSourceEncoding *encoding)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
+       g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
+
+       return g_object_new (GTK_SOURCE_TYPE_FILE_LOADER,
+                            "buffer", buffer,
+                            "stream", stream,
+                            "encoding", encoding,
+                            NULL);
+}
+
+GtkSourceFileLoader *
+gtk_source_file_loader_new (GtkSourceBuffer         *buffer,
+                           GFile                   *location,
+                           const GtkSourceEncoding *encoding)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
+
+       return g_object_new (GTK_SOURCE_TYPE_FILE_LOADER,
+                            "buffer", buffer,
+                            "location", location,
+                            "encoding", encoding,
+                            NULL);
+}
+
+static AsyncData *
+async_data_new (GtkSourceFileLoader *loader)
+{
+       AsyncData *async;
+
+       async = g_slice_new (AsyncData);
+       async->loader = loader;
+       async->cancellable = g_object_ref (loader->priv->cancellable);
+       async->tried_mount = FALSE;
+
+       return async;
+}
+
+static void
+async_data_free (AsyncData *async)
+{
+       g_object_unref (async->cancellable);
+       g_slice_free (AsyncData, async);
+}
+
+static void
+loader_load_completed_or_failed (GtkSourceFileLoader *loader,
+                                AsyncData           *async)
+{
+       gtk_source_file_loader_loading (loader, TRUE, loader->priv->error);
+
+       if (async)
+       {
+               async_data_free (async);
+       }
+}
+
+static void
+async_failed (AsyncData *async,
+             GError    *error)
+{
+       g_propagate_error (&async->loader->priv->error, error);
+       loader_load_completed_or_failed (async->loader, async);
+}
+
+static void
+close_input_stream_ready_cb (GInputStream *stream,
+                            GAsyncResult *res,
+                            AsyncData    *async)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       /* check cancelled state manually */
+       if (g_cancellable_is_cancelled (async->cancellable))
+       {
+               async_data_free (async);
+               return;
+       }
+
+       DEBUG ({
+              g_print ("Finished closing input stream\n");
+       });
+
+       if (!g_input_stream_close_finish (stream, res, &error))
+       {
+               DEBUG ({
+                      g_print ("Closing input stream error: %s\n", error->message);
+               });
+
+               async_failed (async, error);
+               return;
+       }
+
+       DEBUG ({
+              g_print ("Close output stream\n");
+       });
+
+       if (!g_output_stream_close (async->loader->priv->output,
+                                   async->cancellable, &error))
+       {
+               async_failed (async, error);
+               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 ((gtk_source_document_output_stream_get_num_fallbacks (GTK_SOURCE_BUFFER_OUTPUT_STREAM 
(async->loader->priv->output)) != 0) &&
+           async->loader->priv->error == NULL)
+       {
+               g_set_error_literal (&async->loader->priv->error,
+                                    GTK_SOURCE_FILE_ERROR,
+                                    GTK_SOURCE_FILE_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);
+}
+
+static void
+write_complete (AsyncData *async)
+{
+       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);
+       }
+}
+
+static void
+write_file_chunk (AsyncData *async)
+{
+       GtkSourceFileLoader *loader;
+       gssize bytes_written;
+       GError *error = NULL;
+
+       loader = async->loader;
+
+       /* We use sync methods on the buffer stream since it is in memory. Using
+        * async would be racy and we can end up with invalidated iters.
+        */
+       bytes_written = g_output_stream_write (loader->priv->output,
+                                              loader->priv->buffer,
+                                              async->read,
+                                              async->cancellable,
+                                              &error);
+
+       DEBUG ({
+              g_print ("Written: %" G_GSSIZE_FORMAT "\n", bytes_written);
+       });
+
+       if (bytes_written == -1)
+       {
+               DEBUG ({
+                      g_print ("Write error: %s\n", error->message);
+               });
+
+               async_failed (async, error);
+               return;
+       }
+
+       /* FIXME: note that this signal blocks the read... check if it isn't a
+        * performance problem.
+        */
+       gtk_source_file_loader_loading (loader, FALSE, NULL);
+
+       read_file_chunk (async);
+}
+
+static void
+async_read_cb (GInputStream *stream,
+              GAsyncResult *res,
+              AsyncData    *async)
+{
+       GtkSourceFileLoader *loader;
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       /* manually check cancelled state */
+       if (g_cancellable_is_cancelled (async->cancellable))
+       {
+               async_data_free (async);
+               return;
+       }
+
+       loader = async->loader;
+
+       async->read = g_input_stream_read_finish (stream, res, &error);
+
+       /* error occurred */
+       if (async->read == -1)
+       {
+               async_failed (async, error);
+               return;
+       }
+
+       /* Check for the extremely unlikely case where the file size overflows. */
+       if (loader->priv->bytes_read + async->read < loader->priv->bytes_read)
+       {
+               g_set_error (&loader->priv->error,
+                            GTK_SOURCE_FILE_ERROR,
+                            GTK_SOURCE_FILE_ERROR_TOO_BIG,
+                            "File too big");
+
+               async_failed (async, loader->priv->error);
+               return;
+       }
+
+       if (loader->priv->guess_content_type_from_content &&
+           async->read > 0 &&
+           loader->priv->bytes_read == 0)
+       {
+               gchar *guessed;
+
+               guessed = g_content_type_guess (NULL,
+                                               (guchar *)loader->priv->buffer,
+                                               async->read,
+                                               NULL);
+
+               if (guessed != NULL)
+               {
+                       g_file_info_set_attribute_string (loader->priv->info,
+                                                         G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+                                                         guessed);
+
+                       g_free (guessed);
+               }
+       }
+
+       /* Bump the size. */
+       loader->priv->bytes_read += async->read;
+
+       /* end of the file, we are done! */
+       if (async->read == 0)
+       {
+               /* flush the stream to ensure proper line ending detection */
+               g_output_stream_flush (loader->priv->output, NULL, NULL);
+
+               loader->priv->auto_detected_encoding =
+                       gtk_source_document_output_stream_get_guessed (GTK_SOURCE_BUFFER_OUTPUT_STREAM 
(loader->priv->output));
+
+               loader->priv->auto_detected_newline_type =
+                       gtk_source_document_output_stream_detect_newline_type 
(GTK_SOURCE_BUFFER_OUTPUT_STREAM (loader->priv->output));
+
+               write_complete (async);
+
+               return;
+       }
+
+       write_file_chunk (async);
+}
+
+static void
+read_file_chunk (AsyncData *async)
+{
+       GtkSourceFileLoader *loader;
+
+       loader = async->loader;
+
+       g_input_stream_read_async (loader->priv->stream,
+                                  loader->priv->buffer,
+                                  READ_CHUNK_SIZE,
+                                  G_PRIORITY_HIGH,
+                                  async->cancellable,
+                                  (GAsyncReadyCallback) async_read_cb,
+                                  async);
+}
+
+static GSList *
+get_candidate_encodings (GtkSourceFileLoader *loader)
+{
+       /* TODO replace this function */
+#if 0
+       const GtkSourceEncoding *metadata;
+       GSList *encodings;
+       gchar **enc_strv;
+
+       enc_strv = g_settings_get_strv (loader->priv->enc_settings,
+                                       GTK_SOURCE_SETTINGS_ENCODING_AUTO_DETECTED);
+
+       encodings = _gtk_source_encoding_strv_to_list ((const gchar * const *)enc_strv);
+       g_strfreev (enc_strv);
+
+       metadata = get_metadata_encoding (loader);
+       if (metadata != NULL)
+       {
+               encodings = g_slist_prepend (encodings, (gpointer)metadata);
+       }
+
+       return encodings;
+#endif
+}
+
+static GInputStream *
+compression_gzip_stream (GtkSourceFileLoader *loader)
+{
+       GZlibDecompressor *decompressor;
+       GInputStream *base_stream;
+
+       decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
+
+       base_stream = g_converter_input_stream_new (loader->priv->stream,
+                                                   G_CONVERTER (decompressor));
+
+       g_object_unref (decompressor);
+
+       loader->priv->auto_detected_compression_type = GTK_SOURCE_COMPRESSION_TYPE_GZIP;
+
+       return base_stream;
+}
+
+static void
+start_stream_read (AsyncData *async)
+{
+       GSList *candidate_encodings;
+       GtkSourceFileLoader *loader;
+       GInputStream *base_stream = NULL;
+       GFileInfo *info;
+       gboolean ensure_trailing_newline;
+
+       loader = async->loader;
+       info = loader->priv->info;
+
+       if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
+       {
+               const gchar *content_type = g_file_info_get_content_type (info);
+
+               switch (gtk_source_utils_get_compression_type_from_content_type (content_type))
+               {
+                       case GTK_SOURCE_COMPRESSION_TYPE_GZIP:
+                               base_stream = compression_gzip_stream (loader);
+                               break;
+
+                       case GTK_SOURCE_COMPRESSION_TYPE_NONE:
+                               /* NOOP */
+                               break;
+               }
+       }
+
+       if (base_stream == NULL)
+       {
+               base_stream = g_object_ref (loader->priv->stream);
+               loader->priv->auto_detected_compression_type = GTK_SOURCE_COMPRESSION_TYPE_NONE;
+       }
+
+       g_object_unref (loader->priv->stream);
+       loader->priv->stream = base_stream;
+
+       /* Get the candidate encodings */
+       if (loader->priv->encoding == NULL)
+       {
+               candidate_encodings = get_candidate_encodings (loader);
+       }
+       else
+       {
+               candidate_encodings = g_slist_prepend (NULL, (gpointer)loader->priv->encoding);
+       }
+
+       /* TODO replace by a property */
+       ensure_trailing_newline = g_settings_get_boolean (loader->priv->editor_settings,
+                                                         "ensure-trailing-newline");
+
+       /* Output stream */
+       loader->priv->output = gtk_source_document_output_stream_new (loader->priv->document,
+                                                                     candidate_encodings,
+                                                                     ensure_trailing_newline);
+
+       g_slist_free (candidate_encodings);
+
+       /* start reading */
+       read_file_chunk (async);
+}
+
+static void
+finish_query_info (AsyncData *async)
+{
+       GtkSourceFileLoader *loader;
+       GFileInfo *info;
+
+       loader = async->loader;
+       info = loader->priv->info;
+
+       if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE) &&
+           g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
+       {
+               g_set_error (&loader->priv->error,
+                            G_IO_ERROR,
+                            G_IO_ERROR_NOT_REGULAR_FILE,
+                            "Not a regular file");
+
+               loader_load_completed_or_failed (loader, async);
+
+               return;
+       }
+
+       start_stream_read (async);
+}
+
+static void
+query_info_cb (GFile        *source,
+              GAsyncResult *res,
+              AsyncData    *async)
+{
+       GFileInfo *info;
+       GError *error = NULL;
+       GtkSourceFileLoaderPrivate *priv;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       /* manually check the cancelled state */
+       if (g_cancellable_is_cancelled (async->cancellable))
+       {
+               async_data_free (async);
+               return;
+       }
+
+       priv = async->loader->priv;
+
+       /* finish the info query */
+       info = g_file_query_info_finish (priv->location, res, &error);
+
+       if (info == NULL)
+       {
+               /* propagate the error and clean up */
+               async_failed (async, error);
+               return;
+       }
+
+       priv->info = info;
+
+       finish_query_info (async);
+}
+
+static void
+mount_ready_callback (GFile        *file,
+                     GAsyncResult *res,
+                     AsyncData    *async)
+{
+       GError *error = NULL;
+       gboolean mounted;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       /* manual check for cancelled state */
+       if (g_cancellable_is_cancelled (async->cancellable))
+       {
+               async_data_free (async);
+               return;
+       }
+
+       mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
+
+       if (!mounted)
+       {
+               async_failed (async, error);
+       }
+       else
+       {
+               /* try again to open the file for reading */
+               open_async_read (async);
+       }
+}
+
+static void
+recover_not_mounted (AsyncData *async)
+{
+       GtkSourceBuffer *doc;
+       GMountOperation *mount_operation;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       doc = gtk_source_file_loader_get_document (async->loader);
+       /* TODO use GtkSourceFile create mount function */
+       mount_operation = _gtk_source_document_create_mount_operation (doc);
+
+       async->tried_mount = TRUE;
+       g_file_mount_enclosing_volume (async->loader->priv->location,
+                                      G_MOUNT_MOUNT_NONE,
+                                      mount_operation,
+                                      async->cancellable,
+                                      (GAsyncReadyCallback) mount_ready_callback,
+                                      async);
+
+       g_object_unref (mount_operation);
+}
+
+static void
+async_read_ready_callback (GObject      *source,
+                          GAsyncResult *res,
+                          AsyncData    *async)
+{
+       GError *error = NULL;
+       GtkSourceFileLoader *loader;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       /* manual check for cancelled state */
+       if (g_cancellable_is_cancelled (async->cancellable))
+       {
+               async_data_free (async);
+               return;
+       }
+
+       loader = async->loader;
+
+       loader->priv->stream = G_INPUT_STREAM (g_file_read_finish (loader->priv->location, res, &error));
+
+       if (error != NULL)
+       {
+               if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount)
+               {
+                       recover_not_mounted (async);
+                       g_error_free (error);
+                       return;
+               }
+
+               g_propagate_error (&loader->priv->error, error);
+               gtk_source_file_loader_loading (loader, TRUE, loader->priv->error);
+
+               async_data_free (async);
+               return;
+       }
+
+       /* Get the file info: note we cannot use
+        * g_file_input_stream_query_info_async since it is not able to get the
+        * content type etc, beside it is not supported by gvfs.
+        * Using the file instead of the stream is slightly racy, but for
+        * loading this is not too bad...
+        */
+       g_file_query_info_async (loader->priv->location,
+                                LOADER_QUERY_ATTRIBUTES,
+                                 G_FILE_QUERY_INFO_NONE,
+                                G_PRIORITY_HIGH,
+                                async->cancellable,
+                                (GAsyncReadyCallback) query_info_cb,
+                                async);
+}
+
+static void
+open_async_read (AsyncData *async)
+{
+       g_file_read_async (async->loader->priv->location,
+                          G_PRIORITY_HIGH,
+                          async->cancellable,
+                          (GAsyncReadyCallback) async_read_ready_callback,
+                          async);
+}
+
+void
+gtk_source_file_loader_loading (GtkSourceFileLoader *loader,
+                               gboolean             completed,
+                               GError              *error)
+{
+       /* The object will be unrefed in the callback of the loading signal
+        * (when completed == TRUE), so we need to prevent finalization.
+        */
+       if (completed)
+       {
+               g_object_ref (loader);
+       }
+
+       g_signal_emit (loader, signals[LOADING], 0, completed, error);
+
+       if (completed)
+       {
+               if (error == NULL)
+               {
+                       DEBUG ({
+                              g_print ("load completed\n");
+                       });
+               }
+               else
+               {
+                       DEBUG ({
+                              g_print ("load failed\n");
+                       });
+               }
+
+               g_object_unref (loader);
+       }
+}
+
+void
+gtk_source_file_loader_load (GtkSourceFileLoader *loader)
+{
+       AsyncData *async;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       g_return_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader));
+
+       /* the loader can be used just once, then it must be thrown away */
+       g_return_if_fail (loader->priv->used == FALSE);
+       loader->priv->used = TRUE;
+
+       /* make sure no load operation is currently running */
+       g_return_if_fail (loader->priv->cancellable == NULL);
+
+       /* loading start */
+       gtk_source_file_loader_loading (loader, FALSE, NULL);
+
+       loader->priv->cancellable = g_cancellable_new ();
+       async = async_data_new (loader);
+
+       if (loader->priv->stream)
+       {
+               loader->priv->guess_content_type_from_content = TRUE;
+               loader->priv->info = g_file_info_new ();
+
+               start_stream_read (async);
+       }
+       else
+       {
+               open_async_read (async);
+       }
+}
+
+gboolean
+gtk_source_file_loader_cancel (GtkSourceFileLoader *loader)
+{
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), FALSE);
+
+       if (loader->priv->cancellable == NULL)
+       {
+               return FALSE;
+       }
+
+       g_cancellable_cancel (loader->priv->cancellable);
+
+       g_set_error (&loader->priv->error,
+                    G_IO_ERROR,
+                    G_IO_ERROR_CANCELLED,
+                    "Operation cancelled");
+
+       loader_load_completed_or_failed (loader, NULL);
+
+       return TRUE;
+}
+
+GtkSourceBuffer *
+gtk_source_file_loader_get_buffer (GtkSourceFileLoader *loader)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), NULL);
+
+       return loader->priv->document;
+}
+
+GFile *
+gtk_source_file_loader_get_location (GtkSourceFileLoader *loader)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), NULL);
+
+       if (loader->priv->location != NULL)
+       {
+               return g_object_ref (loader->priv->location);
+       }
+       else
+       {
+               return NULL;
+       }
+}
+
+goffset
+gtk_source_file_loader_get_bytes_read (GtkSourceFileLoader *loader)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), 0);
+
+       return loader->priv->bytes_read;
+}
+
+const GtkSourceEncoding *
+gtk_source_file_loader_get_encoding (GtkSourceFileLoader *loader)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), NULL);
+
+       if (loader->priv->encoding != NULL)
+       {
+               return loader->priv->encoding;
+       }
+
+       g_return_val_if_fail (loader->priv->auto_detected_encoding != NULL,
+                             gtk_source_encoding_get_current ());
+
+       return loader->priv->auto_detected_encoding;
+}
+
+GtkSourceNewlineType
+gtk_source_file_loader_get_newline_type (GtkSourceFileLoader *loader)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader),
+                             GTK_SOURCE_NEWLINE_TYPE_LF);
+
+       return loader->priv->auto_detected_newline_type;
+}
+
+GtkSourceCompressionType
+gtk_source_file_loader_get_compression_type (GtkSourceFileLoader *loader)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader),
+                             GTK_SOURCE_COMPRESSION_TYPE_NONE);
+
+       return loader->priv->auto_detected_compression_type;
+}
+
+GFileInfo *
+gtk_source_file_loader_get_info (GtkSourceFileLoader *loader)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), NULL);
+
+       return loader->priv->info;
+}
diff --git a/gtksourceview/gtksourcefileloader.h b/gtksourceview/gtksourcefileloader.h
new file mode 100644
index 0000000..5e29f3e
--- /dev/null
+++ b/gtksourceview/gtksourcefileloader.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
+/* gtksourcefileloader.h
+ * This file is part of GtkSourceView
+ *
+ * Copyright (C) 2005 - Paolo Maggi
+ * Copyright (C) 2007 - Paolo Maggi, Steve Frécinaux
+ * Copyright (C) 2008 - Jesse van den Kieboom
+ * Copyright (C) 2014 - Sébastien Wilmet
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GTK_SOURCE_FILE_LOADER_H__
+#define __GTK_SOURCE_FILE_LOADER_H__
+
+#include <gtk/gtk.h>
+#include "gtksourcetypes.h"
+#include "gtksourcetypes-private.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_FILE_LOADER              (gtk_source_file_loader_get_type())
+#define GTK_SOURCE_FILE_LOADER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), 
GTK_SOURCE_TYPE_FILE_LOADER, GtkSourceFileLoader))
+#define GTK_SOURCE_FILE_LOADER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), 
GTK_SOURCE_TYPE_FILE_LOADER, GtkSourceFileLoaderClass))
+#define GTK_SOURCE_IS_FILE_LOADER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), 
GTK_SOURCE_TYPE_FILE_LOADER))
+#define GTK_SOURCE_IS_FILE_LOADER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GTK_SOURCE_TYPE_FILE_LOADER))
+#define GTK_SOURCE_FILE_LOADER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), 
GTK_SOURCE_TYPE_FILE_LOADER, GtkSourceFileLoaderClass))
+
+typedef struct _GtkSourceFileLoaderClass   GtkSourceFileLoaderClass;
+typedef struct _GtkSourceFileLoaderPrivate GtkSourceFileLoaderPrivate;
+
+struct _GtkSourceFileLoader
+{
+       GObject parent;
+
+       GtkSourceFileLoaderPrivate *priv;
+};
+
+struct _GtkSourceFileLoaderClass
+{
+       GObjectClass parent_class;
+
+       /* Signals */
+       void (* loading) (GtkSourceFileLoader *loader,
+                         gboolean             completed,
+                         const GError        *error);
+};
+
+G_GNUC_INTERNAL
+GType                   gtk_source_file_loader_get_type        (void) G_GNUC_CONST;
+
+/* If enconding == NULL, the encoding will be autodetected */
+G_GNUC_INTERNAL
+GtkSourceFileLoader    *gtk_source_file_loader_new             (GtkSourceBuffer         *buffer,
+                                                                GFile                   *location,
+                                                                const GtkSourceEncoding *encoding);
+
+G_GNUC_INTERNAL
+void                    gtk_source_file_loader_loading         (GtkSourceFileLoader     *loader,
+                                                                gboolean                 completed,
+                                                                GError                  *error);
+
+G_GNUC_INTERNAL
+void                    gtk_source_file_loader_load            (GtkSourceFileLoader     *loader);
+
+G_GNUC_INTERNAL
+GtkSourceFileLoader    *gtk_source_file_loader_new_from_stream (GtkSourceBuffer         *buffer,
+                                                                GInputStream            *stream,
+                                                                const GtkSourceEncoding *encoding);
+
+G_GNUC_INTERNAL
+gboolean                gtk_source_file_loader_cancel          (GtkSourceFileLoader     *loader);
+
+G_GNUC_INTERNAL
+GtkSourceBuffer                *gtk_source_file_loader_get_buffer      (GtkSourceFileLoader     *loader);
+
+/* Returns STDIN_URI if loading from stdin */
+#define STDIN_URI "stdin:"
+G_GNUC_INTERNAL
+GFile                  *gtk_source_file_loader_get_location    (GtkSourceFileLoader     *loader);
+
+G_GNUC_INTERNAL
+const GtkSourceEncoding        *gtk_source_file_loader_get_encoding    (GtkSourceFileLoader     *loader);
+
+G_GNUC_INTERNAL
+GtkSourceNewlineType    gtk_source_file_loader_get_newline_type (GtkSourceFileLoader    *loader);
+
+G_GNUC_INTERNAL
+GtkSourceCompressionType gtk_source_file_loader_get_compression_type
+                                                               (GtkSourceFileLoader     *loader);
+
+G_GNUC_INTERNAL
+goffset                         gtk_source_file_loader_get_bytes_read  (GtkSourceFileLoader     *loader);
+
+/* You can get from the info: content_type, time_modified, standard_size,
+ * access_can_write and also the metadata.
+ */
+G_GNUC_INTERNAL
+GFileInfo              *gtk_source_file_loader_get_info        (GtkSourceFileLoader     *loader);
+
+G_END_DECLS
+
+#endif  /* __GTK_SOURCE_FILE_LOADER_H__  */
diff --git a/gtksourceview/gtksourcetypes-private.h b/gtksourceview/gtksourcetypes-private.h
index 9107afe..a1e2aa0 100644
--- a/gtksourceview/gtksourcetypes-private.h
+++ b/gtksourceview/gtksourcetypes-private.h
@@ -32,6 +32,7 @@ typedef struct _GtkSourceCompletionContainer  GtkSourceCompletionContainer;
 typedef struct _GtkSourceCompletionModel       GtkSourceCompletionModel;
 typedef struct _GtkSourceContextEngine         GtkSourceContextEngine;
 typedef struct _GtkSourceEngine                        GtkSourceEngine;
+typedef struct _GtkSourceFileLoader            GtkSourceFileLoader;
 typedef struct _GtkSourceFileSaver             GtkSourceFileSaver;
 typedef struct _GtkSourceGutterRendererLines   GtkSourceGutterRendererLines;
 typedef struct _GtkSourceGutterRendererMarks   GtkSourceGutterRendererMarks;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index daad76a..452ac4b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -127,6 +127,7 @@ gtksourceview/gtksourcecompletionmodel.c
 gtksourceview/gtksourcecontextengine.c
 gtksourceview/gtksourceencoding.c
 gtksourceview/gtksourcefile.c
+gtksourceview/gtksourcefileloader.c
 gtksourceview/gtksourcefilesaver.c
 gtksourceview/gtksourcegutter.c
 gtksourceview/gtksourcegutterrenderer.c


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