[gtksourceview/wip/loader-saver: 1/2] Refactor the FileSaver to fit GtkSourceFile API



commit 27cc48754fe61ff3de19bf3b264fc01fd2cb094c
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Fri Dec 13 21:48:13 2013 +0100

    Refactor the FileSaver to fit GtkSourceFile API
    
    It is compilable!
    It uses internally a GTask.
    The workaround for https://bugzilla.gnome.org/show_bug.cgi?id=615110 is
    disabled.
    And I added a lot of FIXME and TODO comments.

 docs/reference/Makefile.am         |    2 +
 gtksourceview/gtksourcefilesaver.c |  609 ++++++++++++++----------------------
 gtksourceview/gtksourcefilesaver.h |   40 +--
 3 files changed, 249 insertions(+), 402 deletions(-)
---
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 1987d7f..eaaee88 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -22,6 +22,7 @@ CFILE_GLOB = $(top_srcdir)/gtksourceview/*.c
 IGNORE_HFILES =                                        \
        config.h                                \
        gtksourcebuffer-private.h               \
+       gtksourcebufferinputstream.h            \
        gtksourcecompletioncontainer.h          \
        gtksourcecompletionmodel.h              \
        gtksourcecompletion-private.h           \
@@ -31,6 +32,7 @@ IGNORE_HFILES =                                       \
        gtksourcecompletionwordsutils.h         \
        gtksourcecontextengine.h                \
        gtksourceengine.h                       \
+       gtksourcefilesaver.h                    \
        gtksourcegutter-private.h               \
        gtksourcegutterrendererlines.h          \
        gtksourcegutterrenderermarks.h          \
diff --git a/gtksourceview/gtksourcefilesaver.c b/gtksourceview/gtksourcefilesaver.c
index fe96dac..1b379a6 100644
--- a/gtksourceview/gtksourcefilesaver.c
+++ b/gtksourceview/gtksourcefilesaver.c
@@ -41,16 +41,8 @@
 
 #define WRITE_CHUNK_SIZE 8192
 
-#define REMOTE_QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
-                               G_FILE_ATTRIBUTE_TIME_MODIFIED
-
-enum
-{
-       SAVING,
-       LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
+#define QUERY_ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
+                        G_FILE_ATTRIBUTE_TIME_MODIFIED
 
 enum
 {
@@ -64,6 +56,7 @@ enum
        PROP_ENSURE_TRAILING_NEWLINE
 };
 
+#if 0
 typedef struct
 {
        GtkSourceFileSaver *saver;
@@ -74,10 +67,10 @@ typedef struct
        gssize read;
        GError *error;
 } AsyncData;
+#endif
 
 struct _GtkSourceFileSaverPrivate
 {
-       GFileInfo *info;
        GtkTextBuffer *buffer;
 
        GFile *location;
@@ -86,31 +79,44 @@ struct _GtkSourceFileSaverPrivate
        GtkSourceCompressionType compression_type;
        GtkSourceFileSaveFlags flags;
 
+       GTask *task;
+
+       GFileProgressCallback progress_cb;
+       gpointer progress_cb_data;
+
        GTimeVal old_mtime;
 
+       /* TODO rename these two fields */
        goffset size;
        goffset bytes_written;
 
+       gchar chunk_buffer[WRITE_CHUNK_SIZE];
+       gssize chunk_bytes_read;
+       gssize chunk_bytes_written;
+
        GCancellable *cancellable;
+       /* TODO find better names for these two fields */
        GOutputStream *stream;
        GInputStream *input;
 
+       GFileInfo *info;
+
        GError *error;
 
        GtkSourceMountOperationFactory mount_operation_factory;
        gpointer mount_operation_userdata;
 
+       /* FIXME is used used? */
        guint used : 1;
        guint ensure_trailing_newline : 1;
+       guint tried_mount : 1;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceFileSaver, gtk_source_file_saver, G_TYPE_OBJECT)
 
-/* prototype, because they call each other... isn't C lovely */
-static void read_file_chunk (AsyncData *async);
-static void write_file_chunk (AsyncData *async);
-
-static void check_modified_async (AsyncData *async);
+static void read_file_chunk (GtkSourceFileSaver *saver);
+static void write_file_chunk (GtkSourceFileSaver *saver);
+static void check_externally_modified (GtkSourceFileSaver *saver);
 
 static void
 gtk_source_file_saver_set_property (GObject      *object,
@@ -208,47 +214,17 @@ gtk_source_file_saver_dispose (GObject *object)
 {
        GtkSourceFileSaverPrivate *priv = GTK_SOURCE_FILE_SAVER (object)->priv;
 
-       if (priv->cancellable != NULL)
-       {
-               g_cancellable_cancel (priv->cancellable);
-               g_clear_object (&priv->cancellable);
-       }
-
        g_clear_error (&priv->error);
 
        g_clear_object (&priv->stream);
        g_clear_object (&priv->input);
        g_clear_object (&priv->info);
        g_clear_object (&priv->location);
+       g_clear_object (&priv->task);
 
        G_OBJECT_CLASS (gtk_source_file_saver_parent_class)->dispose (object);
 }
 
-static AsyncData *
-async_data_new (GtkSourceFileSaver *saver)
-{
-       AsyncData *async;
-
-       async = g_slice_new0 (AsyncData);
-       async->saver = saver;
-       async->cancellable = g_object_ref (saver->priv->cancellable);
-
-       return async;
-}
-
-static void
-async_data_free (AsyncData *async)
-{
-       g_object_unref (async->cancellable);
-
-       if (async->error != NULL)
-       {
-               g_error_free (async->error);
-       }
-
-       g_slice_free (AsyncData, async);
-}
-
 static void
 gtk_source_file_saver_class_init (GtkSourceFileSaverClass *klass)
 {
@@ -330,26 +306,12 @@ gtk_source_file_saver_class_init (GtkSourceFileSaverClass *klass)
                                                               G_PARAM_READWRITE |
                                                               G_PARAM_CONSTRUCT_ONLY |
                                                               G_PARAM_STATIC_STRINGS));
-
-       signals[SAVING] =
-               g_signal_new ("saving",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_LAST,
-                             G_STRUCT_OFFSET (GtkSourceFileSaverClass, saving),
-                             NULL, NULL,
-                             _gtksourceview_marshal_VOID__BOOLEAN_POINTER,
-                             G_TYPE_NONE,
-                             2,
-                             G_TYPE_BOOLEAN,
-                             G_TYPE_POINTER);
 }
 
 static void
 gtk_source_file_saver_init (GtkSourceFileSaver *saver)
 {
        saver->priv = gtk_source_file_saver_get_instance_private (saver);
-
-       saver->priv->cancellable = g_cancellable_new ();
 }
 
 GtkSourceFileSaver *
@@ -363,6 +325,7 @@ gtk_source_file_saver_new (GtkTextBuffer            *buffer,
 {
        g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
 
+       /* TODO move this code to set_property() */
        if (encoding == NULL)
        {
                encoding = gtk_source_encoding_get_utf8 ();
@@ -379,26 +342,6 @@ gtk_source_file_saver_new (GtkTextBuffer            *buffer,
                             NULL);
 }
 
-static void
-remote_save_completed_or_failed (GtkSourceFileSaver *saver,
-                                AsyncData          *async)
-{
-       gtk_source_file_saver_saving (saver, TRUE, saver->priv->error);
-
-       if (async != NULL)
-       {
-               async_data_free (async);
-       }
-}
-
-static void
-async_failed (AsyncData *async,
-             GError    *error)
-{
-       g_propagate_error (&async->saver->priv->error, error);
-       remote_save_completed_or_failed (async->saver, async);
-}
-
 /* BEGIN NOTE:
  *
  * This fixes an issue in GOutputStream that applies the atomic replace save
@@ -420,17 +363,18 @@ async_failed (AsyncData *async,
  *              errors while writing (glib/gio)
  * https://bugzilla.gnome.org/show_bug.cgi?id=602412
  */
+#if 0
 static void
-cancel_output_stream_ready_cb (GOutputStream *stream,
-                               GAsyncResult  *result,
-                               AsyncData     *async)
+cancel_output_stream_ready_cb (GOutputStream      *stream,
+                              GAsyncResult       *result,
+                              GtkSourceFileSaver *saver)
 {
-       GError *error;
+       GError *error = NULL;
 
        g_output_stream_close_finish (stream, result, NULL);
 
        /* check cancelled state manually */
-       if (g_cancellable_is_cancelled (async->cancellable) || async->error == NULL)
+       if (g_cancellable_is_cancelled (saver->priv->cancellable) || async->error == NULL)
        {
                async_data_free (async);
                return;
@@ -474,64 +418,56 @@ cancel_output_stream_and_fail (AsyncData *async,
        g_propagate_error (&async->error, error);
        cancel_output_stream (async);
 }
+#endif
 
 /*
  * END NOTE
  */
 
 static void
-remote_get_info_cb (GFile        *source,
-                   GAsyncResult *res,
-                   AsyncData    *async)
+query_info_cb (GFile              *location,
+              GAsyncResult       *result,
+              GtkSourceFileSaver *saver)
 {
-       GtkSourceFileSaver *saver;
-       GFileInfo *info;
        GError *error = NULL;
 
        DEBUG ({
               g_print ("%s\n", G_STRFUNC);
        });
 
+#if 0
        /* check cancelled state manually */
        if (g_cancellable_is_cancelled (async->cancellable))
        {
                async_data_free (async);
                return;
        }
-
-       saver = async->saver;
+#endif
 
        DEBUG ({
               g_print ("Finished query info on file\n");
        });
 
-       info = g_file_query_info_finish (source, res, &error);
-
-       if (info != NULL)
-       {
-               if (saver->priv->info != NULL)
-               {
-                       g_object_unref (saver->priv->info);
-               }
+       g_clear_object (&saver->priv->info);
+       saver->priv->info = g_file_query_info_finish (location, result, &error);
 
-               saver->priv->info = info;
-       }
-       else
+       if (error != NULL)
        {
                DEBUG ({
                       g_print ("Query info failed: %s\n", error->message);
                });
 
-               g_propagate_error (&saver->priv->error, error);
+               g_task_return_error (saver->priv->task, error);
+               return;
        }
 
-       remote_save_completed_or_failed (saver, async);
+       g_task_return_boolean (saver->priv->task, TRUE);
 }
 
 static void
-close_async_ready_get_info_cb (GOutputStream *stream,
-                              GAsyncResult  *res,
-                              AsyncData     *async)
+close_output_stream_cb (GOutputStream      *output_stream,
+                       GAsyncResult       *result,
+                       GtkSourceFileSaver *saver)
 {
        GError *error = NULL;
 
@@ -539,85 +475,89 @@ close_async_ready_get_info_cb (GOutputStream *stream,
               g_print ("%s\n", G_STRFUNC);
        });
 
+#if 0
        /* check cancelled state manually */
        if (g_cancellable_is_cancelled (async->cancellable))
        {
                async_data_free (async);
                return;
        }
+#endif
 
-       DEBUG ({
-              g_print ("Finished closing stream\n");
-       });
+       g_output_stream_close_finish (output_stream, result, &error);
 
-       if (!g_output_stream_close_finish (stream, res, &error))
+       if (error != NULL)
        {
                DEBUG ({
                       g_print ("Closing stream error: %s\n", error->message);
                });
 
-               async_failed (async, error);
+               g_task_return_error (saver->priv->task, error);
                return;
        }
 
-       /* get the file info: note we cannot use
-        * g_file_output_stream_query_info_async since it is not able to get the
+       /* Get the file info: note we cannot use
+        * g_file_output_stream_query_info_async() since it is not able to get the
         * content type etc, beside it is not supported by gvfs.
         * I'm not sure this is actually necessary, can't we just use
-        * g_content_type_guess (since we have the file name and the data)
+        * g_content_type_guess (since we have the file name and the data).
         */
        DEBUG ({
               g_print ("Query info on file\n");
        });
 
-       g_file_query_info_async (async->saver->priv->location,
-                                REMOTE_QUERY_ATTRIBUTES,
+       g_file_query_info_async (saver->priv->location,
+                                QUERY_ATTRIBUTES,
                                 G_FILE_QUERY_INFO_NONE,
-                                G_PRIORITY_HIGH,
-                                async->cancellable,
-                                (GAsyncReadyCallback) remote_get_info_cb,
-                                async);
+                                g_task_get_priority (saver->priv->task),
+                                g_task_get_cancellable (saver->priv->task),
+                                (GAsyncReadyCallback) query_info_cb,
+                                saver);
 }
 
 static void
-write_complete (AsyncData *async)
+write_complete (GtkSourceFileSaver *saver)
 {
        GError *error = NULL;
 
-       /* first we close the input stream */
        DEBUG ({
               g_print ("Close input stream\n");
        });
 
-       if (!g_input_stream_close (async->saver->priv->input,
-                                  async->cancellable, &error))
+       g_input_stream_close (saver->priv->input,
+                             g_task_get_cancellable (saver->priv->task),
+                             &error);
+
+       if (error != NULL)
        {
                DEBUG ({
                       g_print ("Closing input stream error: %s\n", error->message);
                });
 
+#if 0
                cancel_output_stream_and_fail (async, error);
+#endif
+
+               g_task_return_error (saver->priv->task, error);
                return;
        }
 
-       /* now we close the output stream */
        DEBUG ({
               g_print ("Close output stream\n");
        });
 
-       g_output_stream_close_async (async->saver->priv->stream,
-                                    G_PRIORITY_HIGH,
-                                    async->cancellable,
-                                    (GAsyncReadyCallback)close_async_ready_get_info_cb,
-                                    async);
+       g_output_stream_close_async (saver->priv->stream,
+                                    g_task_get_priority (saver->priv->task),
+                                    g_task_get_cancellable (saver->priv->task),
+                                    (GAsyncReadyCallback) close_output_stream_cb,
+                                    saver);
 }
 
 static void
-async_write_cb (GOutputStream *stream,
-               GAsyncResult  *res,
-               AsyncData     *async)
+write_file_chunk_cb (GOutputStream      *output_stream,
+                    GAsyncResult       *result,
+                    GtkSourceFileSaver *saver)
 {
-       GtkSourceFileSaver *saver;
        gssize bytes_written;
        GError *error = NULL;
 
@@ -625,119 +565,112 @@ async_write_cb (GOutputStream *stream,
               g_print ("%s\n", G_STRFUNC);
        });
 
-       /* Check cancelled state manually */
-       if (g_cancellable_is_cancelled (async->cancellable))
-       {
-               cancel_output_stream (async);
-               return;
-       }
-
-       bytes_written = g_output_stream_write_finish (stream, res, &error);
+       bytes_written = g_output_stream_write_finish (output_stream, result, &error);
 
        DEBUG ({
               g_print ("Written: %" G_GSSIZE_FORMAT "\n", bytes_written);
        });
 
-       if (bytes_written == -1)
+       if (error != NULL)
        {
                DEBUG ({
                       g_print ("Write error: %s\n", error->message);
                });
 
-               cancel_output_stream_and_fail (async, error);
+#if 0
+               cancel_output_stream_and_fail (saver, error);
+#endif
+
+               g_task_return_error (saver->priv->task, error);
                return;
        }
 
-       saver = async->saver;
-       async->written += bytes_written;
+       saver->priv->chunk_bytes_written += bytes_written;
 
-       /* write again */
-       if (async->written != async->read)
+       /* Write again */
+       if (saver->priv->chunk_bytes_read != saver->priv->chunk_bytes_written)
        {
-               write_file_chunk (async);
+               write_file_chunk (saver);
                return;
        }
 
-       /* note that this signal blocks the write... check if it isn't
-        * a performance problem
-        */
-       gtk_source_file_saver_saving (saver,
-                                     FALSE,
-                                     NULL);
-
-       read_file_chunk (async);
+       read_file_chunk (saver);
 }
 
 static void
-write_file_chunk (AsyncData *async)
+write_file_chunk (GtkSourceFileSaver *saver)
 {
-       GtkSourceFileSaver *saver;
-
        DEBUG ({
               g_print ("%s\n", G_STRFUNC);
        });
 
-       saver = async->saver;
-
+       /* FIXME check if a thread is created each time this function is called.
+        * If so, this is a performance problem and should be fixed.
+        */
        g_output_stream_write_async (G_OUTPUT_STREAM (saver->priv->stream),
-                                    async->buffer + async->written,
-                                    async->read - async->written,
-                                    G_PRIORITY_HIGH,
-                                    async->cancellable,
-                                    (GAsyncReadyCallback) async_write_cb,
-                                    async);
+                                    saver->priv->chunk_buffer + saver->priv->chunk_bytes_written,
+                                    saver->priv->chunk_bytes_read - saver->priv->chunk_bytes_written,
+                                    g_task_get_priority (saver->priv->task),
+                                    g_task_get_cancellable (saver->priv->task),
+                                    (GAsyncReadyCallback) write_file_chunk_cb,
+                                    saver);
 }
 
 static void
-read_file_chunk (AsyncData *async)
+read_file_chunk (GtkSourceFileSaver *saver)
 {
-       GtkSourceFileSaver *saver;
-       GtkSourceBufferInputStream *dstream;
+       GtkSourceBufferInputStream *buffer_stream;
        GError *error = NULL;
 
        DEBUG ({
               g_print ("%s\n", G_STRFUNC);
        });
 
-       saver = async->saver;
-       async->written = 0;
+       saver->priv->chunk_bytes_written = 0;
 
-       /* we use sync methods on doc stream since it is in memory. Using async
-          would be racy and we can endup with invalidated iters */
-       async->read = g_input_stream_read (saver->priv->input,
-                                          async->buffer,
-                                          WRITE_CHUNK_SIZE,
-                                          async->cancellable,
-                                          &error);
+       /* We use sync methods on doc stream since it is in memory. Using async
+        * would be racy and we can end up with invalid iters.
+        */
+       saver->priv->chunk_bytes_read = g_input_stream_read (saver->priv->input,
+                                                            saver->priv->chunk_buffer,
+                                                            WRITE_CHUNK_SIZE,
+                                                            g_task_get_cancellable (saver->priv->task),
+                                                            &error);
 
        if (error != NULL)
        {
-               cancel_output_stream_and_fail (async, error);
+#if 0
+               cancel_output_stream_and_fail (saver, error);
+#endif
+
+               g_task_return_error (saver->priv->task, error);
                return;
        }
 
-       /* Check if we finished reading and writing */
-       if (async->read == 0)
+       /* Check if we finished reading and writing. */
+       if (saver->priv->chunk_bytes_read == 0)
        {
-               write_complete (async);
+               write_complete (saver);
                return;
        }
 
-       /* Get how many chars have been read */
-       dstream = GTK_SOURCE_BUFFER_INPUT_STREAM (saver->priv->input);
-       saver->priv->bytes_written = _gtk_source_buffer_input_stream_tell (dstream);
+       /* Get how many chars have been read. */
+       /* FIXME bytes_written, or chars_written? */
+       /* FIXME and the value must be updated after writing the chunk, not
+        * after reading it... */
+       buffer_stream = GTK_SOURCE_BUFFER_INPUT_STREAM (saver->priv->input);
+       saver->priv->bytes_written = _gtk_source_buffer_input_stream_tell (buffer_stream);
 
-       write_file_chunk (async);
+       write_file_chunk (saver);
 }
 
 static void
-async_replace_ready_callback (GFile        *source,
-                             GAsyncResult *res,
-                             AsyncData    *async)
+replace_file_cb (GFile              *location,
+                GAsyncResult       *result,
+                GtkSourceFileSaver *saver)
 {
-       GtkSourceFileSaver *saver;
-       GCharsetConverter *converter;
        GFileOutputStream *file_stream;
+       GCharsetConverter *converter;
        GOutputStream *base_stream;
        GError *error = NULL;
 
@@ -745,24 +678,17 @@ async_replace_ready_callback (GFile        *source,
               g_print ("%s\n", G_STRFUNC);
        });
 
-       /* Check cancelled state manually */
-       if (g_cancellable_is_cancelled (async->cancellable))
-       {
-               async_data_free (async);
-               return;
-       }
-
-       saver = async->saver;
-       file_stream = g_file_replace_finish (source, res, &error);
+       file_stream = g_file_replace_finish (location, result, &error);
 
-       /* handle any error that might occur */
-       if (!file_stream)
+       if (error != NULL)
        {
                DEBUG ({
                       g_print ("Opening file failed: %s\n", error->message);
                });
 
-               async_failed (async, error);
+               /* FIXME check not mounted? */
+
+               g_task_return_error (saver->priv->task, error);
                return;
        }
 
@@ -788,6 +714,7 @@ async_replace_ready_callback (GFile        *source,
        }
 
        /* FIXME: manage converter error? */
+
        DEBUG ({
               g_print ("Encoding charset: %s\n",
                        gtk_source_encoding_get_charset (saver->priv->encoding));
@@ -816,28 +743,20 @@ async_replace_ready_callback (GFile        *source,
 
        saver->priv->size = _gtk_source_buffer_input_stream_get_total_size (GTK_SOURCE_BUFFER_INPUT_STREAM 
(saver->priv->input));
 
-       read_file_chunk (async);
+       read_file_chunk (saver);
 }
 
 static void
-begin_write (AsyncData *async)
+begin_write (GtkSourceFileSaver *saver)
 {
-       GtkSourceFileSaver *saver;
        gboolean make_backup;
 
-       DEBUG ({
-              g_print ("Start replacing file contents\n");
-       });
-
-       /* For remote files we simply use g_file_replace_async. There is no
-        * backup as of yet.
-        */
-       saver = async->saver;
-
        make_backup = (saver->priv->flags & GTK_SOURCE_FILE_SAVE_IGNORE_BACKUP) == 0 &&
                      (saver->priv->flags & GTK_SOURCE_FILE_SAVE_PRESERVE_BACKUP) == 0;
 
        DEBUG ({
+              g_print ("Start replacing file contents\n");
+              /* FIXME is the size already set? */
               g_print ("File contents size: %" G_GINT64_FORMAT "\n", saver->priv->size);
               g_print ("Make backup: %s\n", make_backup ? "yes" : "no");
        });
@@ -846,41 +765,33 @@ begin_write (AsyncData *async)
                              NULL,
                              make_backup,
                              G_FILE_CREATE_NONE,
-                             G_PRIORITY_HIGH,
-                             async->cancellable,
-                             (GAsyncReadyCallback) async_replace_ready_callback,
-                             async);
+                             g_task_get_priority (saver->priv->task),
+                             g_task_get_cancellable (saver->priv->task),
+                             (GAsyncReadyCallback) replace_file_cb,
+                             saver);
 }
 
 static void
-mount_ready_callback (GFile        *file,
-                     GAsyncResult *res,
-                     AsyncData    *async)
+mount_cb (GFile              *location,
+         GAsyncResult       *result,
+         GtkSourceFileSaver *saver)
 {
        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;
-       }
+       g_file_mount_enclosing_volume_finish (location, result, &error);
 
-       mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
-
-       if (!mounted)
+       if (error != NULL)
        {
-               async_failed (async, error);
+               g_task_return_error (saver->priv->task, error);
        }
        else
        {
-               /* try again to get the modified state */
-               check_modified_async (async);
+               /* Try again to get the modified state. */
+               check_externally_modified (saver);
        }
 }
 
@@ -893,31 +804,30 @@ create_mount_operation (GtkSourceFileSaver *saver)
 }
 
 static void
-recover_not_mounted (AsyncData *async)
+recover_not_mounted (GtkSourceFileSaver *saver)
 {
-       GMountOperation *mount_operation = create_mount_operation (async->saver);
+       GMountOperation *mount_operation = create_mount_operation (saver);
 
        DEBUG ({
               g_print ("%s\n", G_STRFUNC);
        });
 
-       async->tried_mount = TRUE;
-       g_file_mount_enclosing_volume (async->saver->priv->location,
+       saver->priv->tried_mount = TRUE;
+       g_file_mount_enclosing_volume (saver->priv->location,
                                       G_MOUNT_MOUNT_NONE,
                                       mount_operation,
-                                      async->cancellable,
-                                      (GAsyncReadyCallback) mount_ready_callback,
-                                      async);
+                                      g_task_get_cancellable (saver->priv->task),
+                                      (GAsyncReadyCallback) mount_cb,
+                                      saver);
 
        g_object_unref (mount_operation);
 }
 
 static void
-check_modification_callback (GFile        *source,
-                            GAsyncResult *res,
-                            AsyncData    *async)
+check_externally_modified_cb (GFile              *location,
+                             GAsyncResult       *result,
+                             GtkSourceFileSaver *saver)
 {
-       GtkSourceFileSaver *saver;
        GError *error = NULL;
        GFileInfo *info;
 
@@ -925,37 +835,35 @@ check_modification_callback (GFile        *source,
               g_print ("%s\n", G_STRFUNC);
        });
 
-       /* manually check cancelled state */
-       if (g_cancellable_is_cancelled (async->cancellable))
+       if (saver->priv->flags & GTK_SOURCE_FILE_SAVE_IGNORE_MTIME)
        {
-               async_data_free (async);
-               return;
+               g_warning ("Useless check for externally modified file.");
        }
 
-       saver = async->saver;
-       info = g_file_query_info_finish (source, res, &error);
-       if (info == NULL)
+       info = g_file_query_info_finish (location, result, &error);
+
+       if (error != NULL)
        {
-               if (error->code == G_IO_ERROR_NOT_MOUNTED && !async->tried_mount)
+               if (error->code == G_IO_ERROR_NOT_MOUNTED && !saver->priv->tried_mount)
                {
-                       recover_not_mounted (async);
+                       recover_not_mounted (saver);
                        g_error_free (error);
                        return;
                }
 
-               /* it's perfectly fine if the file doesn't exist yet */
+               /* It's perfectly fine if the file doesn't exist yet. */
                if (error->code != G_IO_ERROR_NOT_FOUND)
                {
                        DEBUG ({
                               g_print ("Error getting modification: %s\n", error->message);
                        });
 
-                       async_failed (async, error);
+                       g_task_return_error (saver->priv->task, error);
                        return;
                }
        }
 
-       /* check if the mtime is > what we know about it (if we have it) */
+       /* Check if the mtime is greater from what we know about it (if we have it). */
        if (info != NULL && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
        {
                GTimeVal mtime;
@@ -972,14 +880,12 @@ check_modification_callback (GFile        *source,
                               g_print ("File is externally modified\n");
                        });
 
-                       g_set_error (&saver->priv->error,
-                                    GTK_SOURCE_FILE_ERROR,
-                                    GTK_SOURCE_FILE_ERROR_EXTERNALLY_MODIFIED,
-                                    "Externally modified");
-
-                       remote_save_completed_or_failed (saver, async);
                        g_object_unref (info);
 
+                       g_task_return_new_error (saver->priv->task,
+                                                GTK_SOURCE_FILE_ERROR,
+                                                GTK_SOURCE_FILE_ERROR_EXTERNALLY_MODIFIED,
+                                                "Externally modified");
                        return;
                }
        }
@@ -989,150 +895,99 @@ check_modification_callback (GFile        *source,
                g_object_unref (info);
        }
 
-       /* modification check passed, start write */
-       begin_write (async);
+       /* Externally modified check passed, start write. */
+       begin_write (saver);
 }
 
 static void
-check_modified_async (AsyncData *async)
+check_externally_modified (GtkSourceFileSaver *saver)
 {
        DEBUG ({
               g_print ("Check externally modified\n");
        });
 
-       g_file_query_info_async (async->saver->priv->location,
+       g_file_query_info_async (saver->priv->location,
                                 G_FILE_ATTRIBUTE_TIME_MODIFIED,
                                 G_FILE_QUERY_INFO_NONE,
-                                G_PRIORITY_HIGH,
-                                async->cancellable,
-                                (GAsyncReadyCallback) check_modification_callback,
-                                async);
+                                g_task_get_priority (saver->priv->task),
+                                g_task_get_cancellable (saver->priv->task),
+                                (GAsyncReadyCallback)check_externally_modified_cb,
+                                saver);
 }
 
-static gboolean
-save_remote_file_real (GtkSourceFileSaver *saver)
+GFile *
+gtk_source_file_saver_get_location (GtkSourceFileSaver *saver)
 {
-       AsyncData *async;
-
-       DEBUG ({
-              g_print ("Starting  save\n");
-       });
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver), NULL);
 
-       /* First find out if the file is modified externally. This requires
-        * a stat, but I don't think we can do this any other way
-        */
-       async = async_data_new (saver);
+       return g_file_dup (saver->priv->location);
+}
 
-       check_modified_async (async);
+void
+gtk_source_file_saver_set_mount_operation_factory (GtkSourceFileSaver             *saver,
+                                                  GtkSourceMountOperationFactory  callback,
+                                                  gpointer                        user_data)
+{
+       g_return_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver));
 
-       /* return false to stop timeout */
-       return FALSE;
+       saver->priv->mount_operation_factory = callback;
+       saver->priv->mount_operation_userdata = user_data;
 }
 
 void
-gtk_source_file_saver_save (GtkSourceFileSaver *saver,
-                           GTimeVal           *old_mtime)
+gtk_source_file_saver_save_async (GtkSourceFileSaver     *saver,
+                                 gint                    io_priority,
+                                 GTimeVal               *old_mtime,
+                                 GCancellable           *cancellable,
+                                 GFileProgressCallback   progress_callback,
+                                 gpointer                progress_callback_data,
+                                 GAsyncReadyCallback     callback,
+                                 gpointer                user_data)
 {
-       DEBUG ({
-              g_print ("%s\n", G_STRFUNC);
-       });
-
        g_return_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver));
+       g_return_if_fail (saver->priv->task == NULL);
        g_return_if_fail (saver->priv->location != NULL);
+       g_return_if_fail (!saver->priv->used);
 
-       g_return_if_fail (saver->priv->used == FALSE);
        saver->priv->used = TRUE;
 
-       /* CHECK:
-        report async (in an idle handler) or sync (bool ret)
-        async is extra work here, sync is special casing in the caller */
+       saver->priv->task = g_task_new (saver, cancellable, callback, user_data);
+       g_task_set_priority (saver->priv->task, io_priority);
 
        saver->priv->old_mtime = *old_mtime;
+       saver->priv->progress_cb = progress_callback;
+       saver->priv->progress_cb_data = progress_callback_data;
 
-       /* saving start */
-       gtk_source_file_saver_saving (saver, FALSE, NULL);
-
-       g_timeout_add_full (G_PRIORITY_HIGH,
-                           0,
-                           (GSourceFunc) save_remote_file_real,
-                           saver,
-                           NULL);
-}
+       DEBUG ({
+              g_print ("Starting  save\n");
+       });
 
-void
-gtk_source_file_saver_saving (GtkSourceFileSaver *saver,
-                            gboolean            completed,
-                            GError             *error)
-{
-       /* the object will be unrefed in the callback of the saving
-        * signal, so we need to prevent finalization.
+       /* TODO create a thread and use sync GIO functions for the first steps.
+        * Destroy the thread for the main step, reading the buffer and writing
+        * the file. Because the buffer must be read in the main thread.
         */
-       if (completed)
+
+       if (saver->priv->flags & GTK_SOURCE_FILE_SAVE_IGNORE_MTIME)
        {
-               g_object_ref (saver);
+               begin_write (saver);
        }
-
-       g_signal_emit (saver, signals[SAVING], 0, completed, error);
-
-       if (completed)
+       else
        {
-               if (error == NULL)
-               {
-                       DEBUG ({
-                              g_print ("save completed\n");
-                       });
-               }
-               else
-               {
-                       DEBUG ({
-                              g_print ("save failed\n");
-                       });
-               }
-
-               g_object_unref (saver);
+               /* TODO install a FileMonitor in GtkSourceFile, with an
+                * externally-modified property, or a signal.
+                */
+               check_externally_modified (saver);
        }
 }
 
-GFile *
-gtk_source_file_saver_get_location (GtkSourceFileSaver *saver)
+gboolean
+gtk_source_file_saver_save_finish (GtkSourceFileSaver  *saver,
+                                  GAsyncResult        *result,
+                                  GError             **error)
 {
-       g_return_val_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver), NULL);
+       g_return_val_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver), FALSE);
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+       g_return_val_if_fail (g_task_is_valid (result, saver), FALSE);
 
-       return g_file_dup (saver->priv->location);
-}
-
-/* Returns 0 if file size is unknown */
-goffset
-gtk_source_file_saver_get_file_size (GtkSourceFileSaver *saver)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver), 0);
-
-       return saver->priv->size;
-}
-
-goffset
-gtk_source_file_saver_get_bytes_written (GtkSourceFileSaver *saver)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver), 0);
-
-       return saver->priv->bytes_written;
-}
-
-GFileInfo *
-gtk_source_file_saver_get_info (GtkSourceFileSaver *saver)
-{
-       g_return_val_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver), NULL);
-
-       return saver->priv->info;
-}
-
-void
-gtk_source_file_saver_set_mount_operation_factory (GtkSourceFileSaver             *saver,
-                                                  GtkSourceMountOperationFactory  callback,
-                                                  gpointer                        user_data)
-{
-       g_return_if_fail (GTK_SOURCE_IS_FILE_SAVER (saver));
-
-       saver->priv->mount_operation_factory = callback;
-       saver->priv->mount_operation_userdata = user_data;
+       return g_task_propagate_boolean (G_TASK (result), error);
 }
diff --git a/gtksourceview/gtksourcefilesaver.h b/gtksourceview/gtksourcefilesaver.h
index 4f69c01..17fb62f 100644
--- a/gtksourceview/gtksourcefilesaver.h
+++ b/gtksourceview/gtksourcefilesaver.h
@@ -58,17 +58,11 @@ struct _GtkSourceFileSaver
 struct _GtkSourceFileSaverClass
 {
        GObjectClass parent_class;
-
-       /* Signals */
-       void (* saving) (GtkSourceFileSaver  *saver,
-                        gboolean             completed,
-                        const GError        *error);
 };
 
 G_GNUC_INTERNAL
 GType                   gtk_source_file_saver_get_type         (void) G_GNUC_CONST;
 
-/* If enconding == NULL, the encoding will be autodetected */
 G_GNUC_INTERNAL
 GtkSourceFileSaver     *gtk_source_file_saver_new              (GtkTextBuffer                *buffer,
                                                                 GFile                        *location,
@@ -79,33 +73,29 @@ GtkSourceFileSaver  *gtk_source_file_saver_new              (GtkTextBuffer                
*
                                                                 gboolean                      
ensure_trailing_newline);
 
 G_GNUC_INTERNAL
-void                    gtk_source_file_saver_saving           (GtkSourceFileSaver  *saver,
-                                                                gboolean             completed,
-                                                                GError              *error);
-
-G_GNUC_INTERNAL
-void                    gtk_source_file_saver_save             (GtkSourceFileSaver  *saver,
-                                                                GTimeVal            *old_mtime);
-
-G_GNUC_INTERNAL
 GFile                  *gtk_source_file_saver_get_location     (GtkSourceFileSaver  *saver);
 
-/* Returns 0 if file size is unknown */
-G_GNUC_INTERNAL
-goffset                         gtk_source_file_saver_get_file_size    (GtkSourceFileSaver  *saver);
-
-G_GNUC_INTERNAL
-goffset                         gtk_source_file_saver_get_bytes_written (GtkSourceFileSaver *saver);
-
-G_GNUC_INTERNAL
-GFileInfo              *gtk_source_file_saver_get_info         (GtkSourceFileSaver  *saver);
-
 G_GNUC_INTERNAL
 void                    gtk_source_file_saver_set_mount_operation_factory
                                                                (GtkSourceFileSaver             *saver,
                                                                 GtkSourceMountOperationFactory  callback,
                                                                 gpointer                        user_data);
 
+G_GNUC_INTERNAL
+void                    gtk_source_file_saver_save_async       (GtkSourceFileSaver     *saver,
+                                                                gint                    io_priority,
+                                                                GTimeVal               *old_mtime,
+                                                                GCancellable           *cancellable,
+                                                                GFileProgressCallback   progress_callback,
+                                                                gpointer                
progress_callback_data,
+                                                                GAsyncReadyCallback     callback,
+                                                                gpointer                user_data);
+
+G_GNUC_INTERNAL
+gboolean                gtk_source_file_saver_save_finish      (GtkSourceFileSaver  *saver,
+                                                                GAsyncResult        *result,
+                                                                GError             **error);
+
 G_END_DECLS
 
 #endif  /* __GTK_SOURCE_FILE_SAVER_H__  */


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