[gtksourceview/wip/loader-saver: 73/73] GtkSourceFileLoader (compilable)



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

    GtkSourceFileLoader (compilable)

 docs/reference/Makefile.am             |    1 +
 gtksourceview/Makefile.am              |    2 +
 gtksourceview/gtksourcefile.c          |   45 ++-
 gtksourceview/gtksourcefileloader.c    |  807 ++++++++++++++++++++++++++++++++
 gtksourceview/gtksourcefileloader.h    |  114 +++++
 gtksourceview/gtksourcetypes-private.h |    1 +
 po/POTFILES.in                         |    1 +
 7 files changed, 969 insertions(+), 2 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/gtksourcefile.c b/gtksourceview/gtksourcefile.c
index 1470b3a..a7070d3 100644
--- a/gtksourceview/gtksourcefile.c
+++ b/gtksourceview/gtksourcefile.c
@@ -20,6 +20,7 @@
  */
 
 #include "gtksourcefile.h"
+#include "gtksourcefileloader.h"
 #include "gtksourcefilesaver.h"
 #include "gtksourcebuffer.h"
 #include "gtksourceencoding.h"
@@ -48,6 +49,7 @@ struct _GtkSourceFilePrivate
        gpointer mount_operation_userdata;
 
        GtkSourceFileSaver *saver;
+       GtkSourceFileLoader *loader;
 
        /* The time when the file was last modified, from our point of view. The
         * file may be externally modified.
@@ -502,7 +504,7 @@ _gtk_source_file_create_mount_operation (GtkSourceFile *file)
                g_mount_operation_new ();
 }
 
-/* FIXME add load flags parameter for future expension? */
+/* FIXME add load flags parameter for future expansion? */
 void
 gtk_source_file_load_async (GtkSourceFile         *file,
                            gint                   io_priority,
@@ -513,6 +515,32 @@ gtk_source_file_load_async (GtkSourceFile         *file,
                            gpointer               user_data)
 {
        g_return_if_fail (GTK_SOURCE_IS_FILE (file));
+
+       if (file->priv->loader != NULL)
+       {
+               g_task_report_new_error (file,
+                                        callback,
+                                        user_data,
+                                        file,
+                                        G_IO_ERROR,
+                                        G_IO_ERROR_PENDING,
+                                        "Another load operation is already running on the file \"%s\".",
+                                        g_file_get_parse_name (file->priv->location));
+
+               return;
+       }
+
+       file->priv->loader = gtk_source_file_loader_new (file, file->priv->ensure_trailing_newline);
+
+       gtk_source_file_loader_set_candidate_encodings (file->priv->loader, NULL);
+
+       gtk_source_file_loader_load_async (file->priv->loader,
+                                          io_priority,
+                                          cancellable,
+                                          progress_callback,
+                                          progress_callback_data,
+                                          callback,
+                                          user_data);
 }
 
 gboolean
@@ -520,10 +548,23 @@ gtk_source_file_load_finish (GtkSourceFile  *file,
                             GAsyncResult   *result,
                             GError        **error)
 {
+       gboolean ok;
+
        g_return_val_if_fail (GTK_SOURCE_IS_FILE (file), FALSE);
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-       return TRUE;
+       if (g_async_result_is_tagged (result, file))
+       {
+               return g_task_propagate_boolean (G_TASK (result), error);
+       }
+
+       g_return_val_if_fail (file->priv->loader != NULL, FALSE);
+
+       ok = gtk_source_file_loader_load_finish (file->priv->loader, result, error);
+
+       g_clear_object (&file->priv->loader);
+
+       return ok;
 }
 
 /**
diff --git a/gtksourceview/gtksourcefileloader.c b/gtksourceview/gtksourcefileloader.c
new file mode 100644
index 0000000..36b24a3
--- /dev/null
+++ b/gtksourceview/gtksourcefileloader.c
@@ -0,0 +1,807 @@
+/* -*- 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 "gtksourceencoding.h"
+#include "gtksourceview-typebuiltins.h"
+
+#if 0
+#define DEBUG(x) (x)
+#else
+#define DEBUG(x)
+#endif
+
+enum
+{
+       PROP_0,
+       PROP_FILE,
+       PROP_INPUT_STREAM,
+       PROP_NEWLINE_TYPE, /* FIXME needed? */
+       PROP_COMPRESSION_TYPE, /* FIXME needed? */
+       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;
+
+       /* The value of the "input-stream" property. */
+       GInputStream *input_stream_property;
+
+       GSList *candidate_encodings;
+
+       GFileInfo *info;
+       const GtkSourceEncoding *encoding;
+       const GtkSourceEncoding *auto_detected_encoding;
+       GtkSourceNewlineType auto_detected_newline_type;
+       GtkSourceCompressionType auto_detected_compression_type;
+
+       GTask *task;
+
+       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;
+
+       GInputStream *input_stream;
+       GOutputStream *output_stream;
+
+       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_location (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_FILE:
+                       g_assert (loader->priv->file == NULL);
+                       loader->priv->file = g_value_get_object (value);
+                       break;
+
+               case PROP_INPUT_STREAM:
+                       g_assert (loader->priv->input_stream_property == NULL);
+                       loader->priv->input_stream_property = g_value_dup_object (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_FILE:
+                       g_value_set_object (value, loader->priv->file);
+                       break;
+
+               case PROP_INPUT_STREAM:
+                       g_value_set_object (value, loader->priv->input_stream_property);
+                       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->input_stream_property);
+       g_clear_object (&priv->input_stream);
+       g_clear_object (&priv->output_stream);
+       g_clear_object (&priv->info);
+
+       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_FILE,
+                                        g_param_spec_object ("file",
+                                                             "File",
+                                                             "The associated GtkSourceFile",
+                                                             GTK_SOURCE_TYPE_FILE,
+                                                             G_PARAM_READWRITE |
+                                                             G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (object_class, PROP_INPUT_STREAM,
+                                        g_param_spec_object ("input-stream",
+                                                             "Input 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_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_cb (GInputStream        *input_stream,
+                      GAsyncResult        *result,
+                      GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+       GtkSourceBufferOutputStream *output_stream;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       g_input_stream_close_finish (input_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_stream,
+                              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.
+        */
+       output_stream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (loader->priv->output_stream);
+       if (gtk_source_buffer_output_stream_get_num_fallbacks (output_stream) > 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)
+{
+       g_input_stream_close_async (loader->priv->input_stream,
+                                   g_task_get_priority (loader->priv->task),
+                                   g_task_get_cancellable (loader->priv->task),
+                                   (GAsyncReadyCallback) close_input_stream_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_stream,
+                                              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
+read_cb (GInputStream        *input_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 (input_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)
+       {
+               GtkSourceBufferOutputStream *output_stream;
+
+               output_stream = GTK_SOURCE_BUFFER_OUTPUT_STREAM (loader->priv->output_stream);
+
+               /* Flush the stream to ensure proper line ending detection. */
+               g_output_stream_flush (loader->priv->output_stream, NULL, NULL);
+
+               loader->priv->auto_detected_encoding =
+                       gtk_source_buffer_output_stream_get_guessed (output_stream);
+
+               loader->priv->auto_detected_newline_type =
+                       gtk_source_buffer_output_stream_detect_newline_type (output_stream);
+
+               write_complete (loader);
+               return;
+       }
+
+       write_file_chunk (loader);
+}
+
+static void
+read_file_chunk (GtkSourceFileLoader *loader)
+{
+       g_input_stream_read_async (loader->priv->input_stream,
+                                  loader->priv->chunk_buffer,
+                                  READ_CHUNK_SIZE,
+                                  g_task_get_priority (loader->priv->task),
+                                  g_task_get_cancellable (loader->priv->task),
+                                  (GAsyncReadyCallback) read_cb,
+                                  loader);
+}
+
+static void
+add_gzip_decompressor_stream (GtkSourceFileLoader *loader)
+{
+       GZlibDecompressor *decompressor;
+       GInputStream *new_input_stream;
+
+       decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
+
+       new_input_stream = g_converter_input_stream_new (loader->priv->input_stream,
+                                                        G_CONVERTER (decompressor));
+
+       g_object_unref (loader->priv->input_stream);
+       g_object_unref (decompressor);
+
+       loader->priv->input_stream = new_input_stream;
+}
+
+static void
+create_streams (GtkSourceFileLoader *loader)
+{
+       GtkSourceBuffer *source_buffer;
+
+       loader->priv->auto_detected_compression_type = GTK_SOURCE_COMPRESSION_TYPE_NONE;
+
+       if (loader->priv->input_stream_property != NULL)
+       {
+               loader->priv->input_stream = g_object_ref (loader->priv->input_stream_property);
+       }
+       else 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 (loader->priv->info);
+
+               switch (get_compression_type_from_content_type (content_type))
+               {
+                       case GTK_SOURCE_COMPRESSION_TYPE_GZIP:
+                               add_gzip_decompressor_stream (loader);
+                               loader->priv->auto_detected_compression_type = 
GTK_SOURCE_COMPRESSION_TYPE_GZIP;
+                               break;
+
+                       case GTK_SOURCE_COMPRESSION_TYPE_NONE:
+                               /* NOOP */
+                               break;
+               }
+       }
+
+       g_return_if_fail (loader->priv->input_stream != NULL);
+
+       source_buffer = gtk_source_file_get_buffer (loader->priv->file);
+
+       loader->priv->output_stream = gtk_source_buffer_output_stream_new (source_buffer,
+                                                                          loader->priv->candidate_encodings,
+                                                                          
loader->priv->remove_trailing_newline);
+
+       /* start reading */
+       read_file_chunk (loader);
+}
+
+static void
+query_info_cb (GFile               *location,
+              GAsyncResult        *result,
+              GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       loader->priv->info = g_file_query_info_finish (location, result, &error);
+
+       if (error != NULL)
+       {
+               g_task_return_error (loader->priv->task, error);
+               return;
+       }
+
+       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;
+       }
+
+       create_streams (loader);
+}
+
+static void
+mount_cb (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_location (loader);
+       }
+}
+
+static void
+recover_not_mounted (GtkSourceFileLoader *loader)
+{
+       GMountOperation *mount_operation;
+       GFile *location;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       mount_operation = _gtk_source_file_create_mount_operation (loader->priv->file);
+
+       loader->priv->tried_mount = TRUE;
+
+       location = gtk_source_file_get_location (loader->priv->file);
+
+       g_file_mount_enclosing_volume (location,
+                                      G_MOUNT_MOUNT_NONE,
+                                      mount_operation,
+                                      g_task_get_cancellable (loader->priv->task),
+                                      (GAsyncReadyCallback) mount_cb,
+                                      loader);
+
+       g_object_unref (mount_operation);
+       g_object_unref (location);
+}
+
+static void
+open_location_cb (GFile               *location,
+                 GAsyncResult        *result,
+                 GtkSourceFileLoader *loader)
+{
+       GError *error = NULL;
+
+       DEBUG ({
+              g_print ("%s\n", G_STRFUNC);
+       });
+
+       loader->priv->input_stream = G_INPUT_STREAM (g_file_read_finish (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 (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_location (GtkSourceFileLoader *loader)
+{
+       GFile *location = gtk_source_file_get_location (loader->priv->file);
+
+       g_file_read_async (location,
+                          g_task_get_priority (loader->priv->task),
+                          g_task_get_cancellable (loader->priv->task),
+                          (GAsyncReadyCallback) open_location_cb,
+                          loader);
+
+       g_object_unref (location);
+}
+
+GtkSourceFileLoader *
+gtk_source_file_loader_new (GtkSourceFile *file,
+                           gboolean       remove_trailing_newline)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE (file), NULL);
+
+       return g_object_new (GTK_SOURCE_TYPE_FILE_LOADER,
+                            "file", file,
+                            "remove-trailing-newline", remove_trailing_newline,
+                            NULL);
+}
+
+GtkSourceFileLoader *
+gtk_source_file_loader_new_from_stream (GtkSourceFile *file,
+                                       GInputStream  *stream,
+                                       gboolean       remove_trailing_newline)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE (file), NULL);
+       g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
+
+       return g_object_new (GTK_SOURCE_TYPE_FILE_LOADER,
+                            "file", file,
+                            "stream-to-read", stream,
+                            "remove-trailing-newline", remove_trailing_newline,
+                            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 ("Start loading\n");
+       });
+
+       if (loader->priv->input_stream_property != NULL)
+       {
+               loader->priv->guess_content_type_from_content = TRUE;
+               loader->priv->info = g_file_info_new ();
+
+               create_streams (loader);
+       }
+       else
+       {
+               open_location (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..5c247e3
--- /dev/null
+++ b/gtksourceview/gtksourcefileloader.h
@@ -0,0 +1,114 @@
+/* -*- 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"
+#include "gtksourcefile.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;
+
+G_GNUC_INTERNAL
+GtkSourceFileLoader    *gtk_source_file_loader_new             (GtkSourceFile           *file,
+                                                                gboolean                 
remove_trailing_newline);
+
+G_GNUC_INTERNAL
+GtkSourceFileLoader    *gtk_source_file_loader_new_from_stream (GtkSourceFile           *file,
+                                                                GInputStream            *stream,
+                                                                gboolean                 
remove_trailing_newline);
+
+G_GNUC_INTERNAL
+void                    gtk_source_file_loader_set_candidate_encodings
+                                                               (GtkSourceFileLoader     *loader,
+                                                                GSList                  
*candidate_encodings);
+
+G_GNUC_INTERNAL
+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_GNUC_INTERNAL
+gboolean                gtk_source_file_loader_load_finish     (GtkSourceFileLoader     *loader,
+                                                                GAsyncResult            *result,
+                                                                GError                 **error);
+
+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 c3a8d5a..3658a75 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -132,6 +132,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]