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



commit 986bfd91646171bbd4a7f6356998b012751e51e6
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    |  841 ++++++++++++++++++++++++++++++++
 gtksourceview/gtksourcefileloader.h    |  109 ++++
 gtksourceview/gtksourcetypes-private.h |    1 +
 po/POTFILES.in                         |    1 +
 6 files changed, 955 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..7e6b4c9
--- /dev/null
+++ b/gtksourceview/gtksourcefileloader.c
@@ -0,0 +1,841 @@
+/* -*- 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 "gtksourcefile.h"
+#include "gtksourceview-typebuiltins.h"
+
+#if 0
+#define DEBUG(x) (x)
+#else
+#define DEBUG(x)
+#endif
+
+enum
+{
+       PROP_0,
+       PROP_BUFFER,
+       PROP_LOCATION,
+       PROP_STREAM,
+       PROP_ENCODING,
+       PROP_NEWLINE_TYPE,
+       PROP_COMPRESSION_TYPE,
+       PROP_REMOVE_TRAILING_NEWLINE
+};
+
+#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
+
+struct _GtkSourceFileLoaderPrivate
+{
+       GtkSourceFile *file;
+       GtkSourceBuffer *source_buffer;
+
+       GSList *candidate_encodings;
+
+       GTask *task;
+
+       GFileInfo *info;
+       GFile *location;
+       const GtkSourceEncoding *encoding;
+       const GtkSourceEncoding *auto_detected_encoding;
+       GtkSourceNewlineType auto_detected_newline_type;
+       GtkSourceCompressionType auto_detected_compression_type;
+
+       goffset total_bytes_read;
+       GFileProgressCallback progress_cb;
+       gpointer progress_cb_data;
+
+       /* 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 chunk_buffer[READ_CHUNK_SIZE];
+       gssize chunk_bytes_read;
+
+       /* TODO rename */
+       GInputStream *stream;
+       GOutputStream *output;
+
+       guint guess_content_type_from_content : 1;
+       guint remove_trailing_newline : 1;
+       guint tried_mount : 1;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceFileLoader, gtk_source_file_loader, G_TYPE_OBJECT)
+
+static void open_async_read (GtkSourceFileLoader *loader);
+static void read_file_chunk (GtkSourceFileLoader *loader);
+
+static GtkSourceCompressionType
+get_compression_type_from_content_type (const gchar *content_type)
+{
+       if (content_type == NULL)
+       {
+               return GTK_SOURCE_COMPRESSION_TYPE_NONE;
+       }
+
+       if (g_content_type_is_a (content_type, "application/x-gzip"))
+       {
+               return GTK_SOURCE_COMPRESSION_TYPE_GZIP;
+       }
+
+       return GTK_SOURCE_COMPRESSION_TYPE_NONE;
+}
+
+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_STREAM:
+                       g_assert (loader->priv->stream == NULL);
+                       loader->priv->stream = 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_COMPRESSION_TYPE:
+                       loader->priv->auto_detected_compression_type = g_value_get_enum (value);
+                       break;
+
+               case PROP_REMOVE_TRAILING_NEWLINE:
+                       loader->priv->remove_trailing_newline = g_value_get_boolean (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_STREAM:
+                       g_value_set_object (value, loader->priv->stream);
+                       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_COMPRESSION_TYPE:
+                       g_value_set_enum (value, loader->priv->auto_detected_compression_type);
+                       break;
+
+               case PROP_REMOVE_TRAILING_NEWLINE:
+                       g_value_set_boolean (value, loader->priv->remove_trailing_newline);
+                       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;
+
+       g_clear_object (&priv->task);
+       g_clear_object (&priv->stream);
+       g_clear_object (&priv->output);
+       g_clear_object (&priv->info);
+       g_clear_object (&priv->location);
+
+       g_slist_free (priv->candidate_encodings);
+       priv->candidate_encodings = NULL;
+
+       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_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));
+
+       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_REMOVE_TRAILING_NEWLINE,
+                                        g_param_spec_boolean ("remove-trailing-newline",
+                                                              "Remove Trailing Newline",
+                                                              "Remove the trailing newline if present",
+                                                              TRUE,
+                                                              G_PARAM_READWRITE |
+                                                              G_PARAM_CONSTRUCT_ONLY |
+                                                              G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gtk_source_file_loader_init (GtkSourceFileLoader *loader)
+{
+       loader->priv = gtk_source_file_loader_get_instance_private (loader);
+}
+
+static void
+close_input_stream_ready_cb (GInputStream        *stream,
+                            GAsyncResult        *result,
+                            GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       g_input_stream_close_finish (stream, result, &error);
+
+       if (error != NULL)
+       {
+               DEBUG ({
+                      g_print ("Closing input stream error: %s\n", error->message);
+               });
+
+               g_task_return_error (loader->priv->task, error);
+               return;
+       }
+
+       DEBUG ({
+              g_print ("Close output stream\n");
+       });
+
+       g_output_stream_close (loader->priv->output,
+                              g_task_get_cancellable (loader->priv->task),
+                              &error);
+
+       if (error != NULL)
+       {
+               g_task_return_error (loader->priv->task, 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 
(loader->priv->output)) > 0)
+       {
+               g_task_return_new_error (loader->priv->task,
+                                        GTK_SOURCE_FILE_ERROR,
+                                        GTK_SOURCE_FILE_ERROR_CONVERSION_FALLBACK,
+                                        "There was a conversion error and it was "
+                                        "needed to use a fallback char");
+               return;
+       }
+
+       g_task_return_boolean (loader->priv->task, TRUE);
+}
+
+static void
+write_complete (GtkSourceFileLoader *loader)
+{
+       if (loader->priv->stream != NULL)
+       {
+               g_input_stream_close_async (loader->priv->stream,
+                                           g_task_get_priority (loader->priv->task),
+                                           g_task_get_cancellable (loader->priv->task),
+                                           (GAsyncReadyCallback) close_input_stream_ready_cb,
+                                           loader);
+       }
+}
+
+static void
+write_file_chunk (GtkSourceFileLoader *loader)
+{
+       gssize bytes_written;
+       GError *error = NULL;
+
+       /* 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->chunk_buffer,
+                                              loader->priv->chunk_bytes_read,
+                                              g_task_get_cancellable (loader->priv->task),
+                                              &error);
+
+       DEBUG ({
+              g_print ("Written: %" G_GSSIZE_FORMAT "\n", bytes_written);
+       });
+
+       if (error != NULL)
+       {
+               DEBUG ({
+                      g_print ("Write error: %s\n", error->message);
+               });
+
+               g_task_return_error (loader->priv->task, error);
+               return;
+       }
+
+       /* FIXME: note that calling the progress callback blocks the read...
+        * Check if it isn't a performance problem.
+        */
+       if (loader->priv->progress_cb != NULL)
+       {
+               /* TODO call the cb */
+       }
+
+       read_file_chunk (loader);
+}
+
+static void
+async_read_cb (GInputStream        *stream,
+              GAsyncResult        *result,
+              GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       loader->priv->chunk_bytes_read = g_input_stream_read_finish (stream, result, &error);
+
+       if (error != NULL)
+       {
+               g_task_return_error (loader->priv->task, error);
+               return;
+       }
+
+       /* Check for the extremely unlikely case where the file size overflows. */
+       if (loader->priv->total_bytes_read + loader->priv->chunk_bytes_read < loader->priv->total_bytes_read)
+       {
+               g_task_return_new_error (loader->priv->task,
+                                        GTK_SOURCE_FILE_ERROR,
+                                        GTK_SOURCE_FILE_ERROR_TOO_BIG,
+                                        "File too big");
+               return;
+       }
+
+       if (loader->priv->guess_content_type_from_content &&
+           loader->priv->chunk_bytes_read > 0 &&
+           loader->priv->total_bytes_read == 0)
+       {
+               gchar *guessed;
+
+               guessed = g_content_type_guess (NULL,
+                                               (guchar *)loader->priv->chunk_buffer,
+                                               loader->priv->chunk_bytes_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->total_bytes_read += loader->priv->chunk_bytes_read;
+
+       /* End of the file, we are done! */
+       if (loader->priv->chunk_bytes_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 (loader);
+               return;
+       }
+
+       write_file_chunk (loader);
+}
+
+static void
+read_file_chunk (GtkSourceFileLoader *loader)
+{
+       g_input_stream_read_async (loader->priv->stream,
+                                  loader->priv->chunk_buffer,
+                                  READ_CHUNK_SIZE,
+                                  g_task_get_priority (loader->priv->task),
+                                  g_task_get_cancellable (loader->priv->task),
+                                  (GAsyncReadyCallback) async_read_cb,
+                                  loader);
+}
+
+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 (GtkSourceFileLoader *loader)
+{
+       GInputStream *base_stream = NULL;
+
+       if (g_file_info_has_attribute (loader->priv->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
+       {
+               const gchar *content_type = g_file_info_get_content_type (info);
+
+               switch (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;
+
+       loader->priv->output = gtk_source_document_output_stream_new (loader->priv->source_buffer,
+                                                                     loader->priv->candidate_encodings,
+                                                                     loader->priv->remove_trailing_newline);
+
+       /* start reading */
+       read_file_chunk (loader);
+}
+
+static void
+finish_query_info (GtkSourceFileLoader *loader)
+{
+       if (g_file_info_has_attribute (loader->priv->info, G_FILE_ATTRIBUTE_STANDARD_TYPE) &&
+           g_file_info_get_file_type (loader->priv->info) != G_FILE_TYPE_REGULAR)
+       {
+               g_task_return_new_error (loader->priv->task,
+                                        G_IO_ERROR,
+                                        G_IO_ERROR_NOT_REGULAR_FILE,
+                                        "Not a regular file");
+               return;
+       }
+
+       start_stream_read (loader);
+}
+
+static void
+query_info_cb (GFile               *source,
+              GAsyncResult        *result,
+              GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       loader->priv->info = g_file_query_info_finish (loader->priv->location, result, &error);
+
+       if (error != NULL)
+       {
+               g_task_return_error (loader->priv->task, error);
+               return;
+       }
+
+       finish_query_info (loader);
+}
+
+static void
+mount_ready_callback (GFile               *file,
+                     GAsyncResult        *result,
+                     GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       g_file_mount_enclosing_volume_finish (file, result, &error);
+
+       if (error != NULL)
+       {
+               g_task_return_error (loader->priv->task, error);
+       }
+       else
+       {
+               /* try again to open the file for reading */
+               open_async_read (loader);
+       }
+}
+
+static void
+recover_not_mounted (GtkSourceFileLoader *loader)
+{
+       GMountOperation *mount_operation;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       mount_operation = _gtk_source_file_create_mount_operation (loader->priv->file);
+
+       loader->priv->tried_mount = TRUE;
+
+       g_file_mount_enclosing_volume (loader->priv->location,
+                                      G_MOUNT_MOUNT_NONE,
+                                      mount_operation,
+                                      g_task_get_cancellable (loader->priv->task),
+                                      (GAsyncReadyCallback) mount_ready_callback,
+                                      loader);
+
+       g_object_unref (mount_operation);
+}
+
+static void
+async_read_ready_callback (GObject             *source,
+                          GAsyncResult        *result,
+                          GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       loader->priv->stream = G_INPUT_STREAM (g_file_read_finish (loader->priv->location, result, &error));
+
+       if (error != NULL)
+       {
+               if (error->code == G_IO_ERROR_NOT_MOUNTED && !loader->priv->tried_mount)
+               {
+                       recover_not_mounted (loader);
+                       g_error_free (error);
+                       return;
+               }
+
+               g_task_return_error (loader->priv->task, error);
+               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_task_get_priority (loader->priv->task),
+                                g_task_get_cancellable (loader->priv->task),
+                                (GAsyncReadyCallback) query_info_cb,
+                                loader);
+}
+
+static void
+open_async_read (GtkSourceFileLoader *loader)
+{
+       g_file_read_async (loader->priv->location,
+                          g_task_get_priority (loader->priv->task),
+                          g_task_get_cancellable (loader->priv->task),
+                          (GAsyncReadyCallback) async_read_ready_callback,
+                          loader);
+}
+
+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);
+}
+
+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);
+}
+
+void
+gtk_source_file_loader_set_candidate_encodings (GtkSourceFileLoader *loader,
+                                               GSList              *candidate_encodings)
+{
+       g_return_if_fail (loader->priv->candidate_encodings == NULL);
+
+       loader->priv->candidate_encodings = g_slist_copy (candidate_encodings);
+}
+
+void
+gtk_source_file_loader_load_async (GtkSourceFileLoader   *loader,
+                                  gint                   io_priority,
+                                  GCancellable          *cancellable,
+                                  GFileProgressCallback  progress_callback,
+                                  gpointer               progress_callback_data,
+                                  GAsyncReadyCallback    callback,
+                                  gpointer               user_data)
+{
+       g_return_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader));
+       g_return_if_fail (loader->priv->task == NULL);
+
+       loader->priv->task = g_task_new (loader->priv->file, cancellable, callback, user_data);
+       g_task_set_priority (loader->priv->task, io_priority);
+
+       loader->priv->progress_cb = progress_callback;
+       loader->priv->progress_cb_data = progress_callback_data;
+
+       DEBUG ({
+              g_print ("Starting load\n");
+       });
+
+       if (loader->priv->stream != NULL)
+       {
+               loader->priv->guess_content_type_from_content = TRUE;
+               loader->priv->info = g_file_info_new ();
+
+               start_stream_read (loader);
+       }
+       else
+       {
+               open_async_read (loader);
+       }
+}
+
+gboolean
+gtk_source_file_loader_load_finish (GtkSourceFileLoader  *loader,
+                                   GAsyncResult         *result,
+                                   GError              **error)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), FALSE);
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+       g_return_val_if_fail (g_task_is_valid (result, loader->priv->file), FALSE);
+
+       return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+gtk_source_file_loader_cancel (GtkSourceFileLoader *loader)
+{
+       GCancellable *cancellable;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader), FALSE);
+
+       if (loader->priv->task == NULL)
+       {
+               return FALSE;
+       }
+
+       cancellable = g_task_get_cancellable (loader->priv->task);
+
+       if (cancellable == NULL)
+       {
+               return FALSE;
+       }
+
+       g_cancellable_cancel (cancellable);
+       g_task_return_error_if_cancelled (loader->priv->task);
+       return TRUE;
+}
+
+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..f54ea8a
--- /dev/null
+++ b/gtksourceview/gtksourcefileloader.h
@@ -0,0 +1,109 @@
+/* -*- 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
+GtkSourceFileLoader    *gtk_source_file_loader_new_from_stream (GtkSourceBuffer         *buffer,
+                                                                GInputStream            *stream,
+                                                                const GtkSourceEncoding *encoding);
+
+G_GNUC_INTERNAL
+void                    gtk_source_file_loader_set_candidate_encodings
+                                                               (GtkSourceFileLoader     *loader,
+                                                                GSList                  
*candidate_encodings);
+
+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
+gboolean                gtk_source_file_loader_cancel          (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);
+
+/* 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]