[gnome-builder/wip/gtk4-port: 236/343] libide/io: add IdeFileTransfer
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port: 236/343] libide/io: add IdeFileTransfer
- Date: Mon, 4 Apr 2022 20:02:16 +0000 (UTC)
commit 7a734d4ff10c2137940afe2de25a3829a81dbe2f
Author: Christian Hergert <chergert redhat com>
Date: Thu Mar 31 01:56:14 2022 -0700
libide/io: add IdeFileTransfer
Bringing this over from libdazzle back into Builder so we can use it to
do copies from DnD, etc.
src/libide/io/ide-file-transfer.c | 792 ++++++++++++++++++++++++++++++++++++++
src/libide/io/ide-file-transfer.h | 90 +++++
src/libide/io/libide-io.h | 2 +
src/libide/io/meson.build | 30 +-
4 files changed, 911 insertions(+), 3 deletions(-)
---
diff --git a/src/libide/io/ide-file-transfer.c b/src/libide/io/ide-file-transfer.c
new file mode 100644
index 000000000..cc80dd857
--- /dev/null
+++ b/src/libide/io/ide-file-transfer.c
@@ -0,0 +1,792 @@
+/* ide-file-transfer.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-file-transfer"
+
+#include "config.h"
+
+#include "ide-directory-reaper.h"
+#include "ide-file-transfer.h"
+#include "ide-io-enums.h"
+
+#define QUERY_ATTRS (G_FILE_ATTRIBUTE_STANDARD_NAME"," \
+ G_FILE_ATTRIBUTE_STANDARD_TYPE"," \
+ G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK"," \
+ G_FILE_ATTRIBUTE_STANDARD_SIZE)
+#define QUERY_FLAGS (G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
+
+typedef struct
+{
+ GPtrArray *opers;
+
+ IdeFileTransferStat stat_buf;
+
+ IdeFileTransferFlags flags;
+
+ gint64 last_num_bytes;
+
+ guint executed : 1;
+} IdeFileTransferPrivate;
+
+typedef struct
+{
+ /* Unowned pointers */
+ IdeFileTransfer *self;
+ GCancellable *cancellable;
+
+ /* Owned pointers */
+ GFile *src;
+ GFile *dst;
+ GError *error;
+
+ IdeFileTransferFlags flags;
+} Oper;
+
+typedef void (*FileWalkCallback) (GFile *file,
+ GFileInfo *child_info,
+ gpointer user_data);
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ PROP_PROGRESS,
+ N_PROPS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeFileTransfer, ide_file_transfer, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+oper_free (gpointer data)
+{
+ Oper *oper = data;
+
+ oper->self = NULL;
+ oper->cancellable = NULL;
+
+ g_clear_object (&oper->src);
+ g_clear_object (&oper->dst);
+ g_clear_error (&oper->error);
+
+ g_slice_free (Oper, oper);
+}
+
+static void
+ide_file_transfer_finalize (GObject *object)
+{
+ IdeFileTransfer *self = (IdeFileTransfer *)object;
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ g_clear_pointer (&priv->opers, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (ide_file_transfer_parent_class)->finalize (object);
+}
+
+static void
+ide_file_transfer_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeFileTransfer *self = IDE_FILE_TRANSFER (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ g_value_set_flags (value, ide_file_transfer_get_flags (self));
+ break;
+
+ case PROP_PROGRESS:
+ g_value_set_double (value, ide_file_transfer_get_progress (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_file_transfer_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeFileTransfer *self = IDE_FILE_TRANSFER (object);
+
+ switch (prop_id)
+ {
+ case PROP_FLAGS:
+ ide_file_transfer_set_flags (self, g_value_get_flags (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_file_transfer_class_init (IdeFileTransferClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_file_transfer_finalize;
+ object_class->get_property = ide_file_transfer_get_property;
+ object_class->set_property = ide_file_transfer_set_property;
+
+ properties [PROP_FLAGS] =
+ g_param_spec_flags ("flags",
+ "Flags",
+ "The transfer flags for the operation",
+ IDE_TYPE_FILE_TRANSFER_FLAGS,
+ IDE_FILE_TRANSFER_FLAGS_NONE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PROGRESS] =
+ g_param_spec_double ("progress",
+ "Progress",
+ "The transfer progress, from 0 to 1",
+ 0.0, 1.0, 0.0,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_file_transfer_init (IdeFileTransfer *self)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ priv->opers = g_ptr_array_new_with_free_func (oper_free);
+}
+
+IdeFileTransfer *
+ide_file_transfer_new (void)
+{
+ return g_object_new (IDE_TYPE_FILE_TRANSFER, NULL);
+}
+
+void
+ide_file_transfer_add (IdeFileTransfer *self,
+ GFile *src,
+ GFile *dst)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+ Oper *oper;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_FILE_TRANSFER (self));
+ g_return_if_fail (G_IS_FILE (src));
+ g_return_if_fail (G_IS_FILE (dst));
+
+ if (priv->executed)
+ {
+ g_warning ("Cannot add files to transfer after executing");
+ IDE_EXIT;
+ }
+
+ if (g_file_equal (src, dst))
+ {
+ g_warning ("Source and destination cannot be the same");
+ IDE_EXIT;
+ }
+
+ if (g_file_has_prefix (dst, src))
+ {
+ g_warning ("Destination cannot be within source");
+ IDE_EXIT;
+ }
+
+ oper = g_slice_new0 (Oper);
+ oper->src = g_object_ref (src);
+ oper->dst = g_object_ref (dst);
+ oper->self = self;
+
+ g_assert (priv->opers != NULL);
+
+ g_ptr_array_add (priv->opers, oper);
+
+ IDE_EXIT;
+}
+
+IdeFileTransferFlags
+ide_file_transfer_get_flags (IdeFileTransfer *self)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_FILE_TRANSFER (self), 0);
+
+ return priv->flags;
+}
+
+void
+ide_file_transfer_set_flags (IdeFileTransfer *self,
+ IdeFileTransferFlags flags)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_FILE_TRANSFER (self));
+
+ if (priv->executed)
+ {
+ g_warning ("Cannot set flags after executing transfer");
+ IDE_EXIT;
+ }
+
+ if (priv->flags != flags)
+ {
+ priv->flags = flags;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FLAGS]);
+ }
+
+ IDE_EXIT;
+}
+
+gdouble
+ide_file_transfer_get_progress (IdeFileTransfer *self)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_FILE_TRANSFER (self), 0.0);
+
+ if (priv->stat_buf.n_bytes_total != 0)
+ return (gdouble)priv->stat_buf.n_bytes / (gdouble)priv->stat_buf.n_bytes_total;
+
+ return 0.0;
+}
+
+static void
+file_walk_full (GFile *parent,
+ GFileInfo *info,
+ GCancellable *cancellable,
+ FileWalkCallback callback,
+ gpointer user_data)
+{
+ IDE_ENTRY;
+
+ g_assert (!parent || G_IS_FILE (parent));
+ g_assert (G_IS_FILE_INFO (info));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (callback != NULL);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ IDE_EXIT;
+
+ callback (parent, info, user_data);
+
+ if (g_file_info_get_is_symlink (info))
+ IDE_EXIT;
+
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+ {
+ g_autoptr(GFileEnumerator) enumerator = NULL;
+ g_autoptr(GFile) child = NULL;
+ const gchar *name = g_file_info_get_name (info);
+
+ if (name == NULL)
+ IDE_EXIT;
+
+ child = g_file_get_child (parent, name);
+ enumerator = g_file_enumerate_children (child, QUERY_ATTRS, QUERY_FLAGS, cancellable, NULL);
+
+ if (enumerator != NULL)
+ {
+ gpointer infoptr;
+
+ while ((infoptr = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
+ {
+ g_autoptr(GFileInfo) grandchild_info = infoptr;
+
+ file_walk_full (child, grandchild_info, cancellable, callback, user_data);
+ }
+
+ g_file_enumerator_close (enumerator, cancellable, NULL);
+ }
+ }
+
+ IDE_EXIT;
+}
+
+static void
+file_walk (GFile *root,
+ GCancellable *cancellable,
+ FileWalkCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GFile) parent = NULL;
+ g_autoptr(GFileInfo) info = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_FILE (root));
+ g_assert (callback != NULL);
+
+ parent = g_file_get_parent (root);
+ if (g_file_equal (root, parent))
+ g_clear_object (&parent);
+
+ info = g_file_query_info (root, QUERY_ATTRS, QUERY_FLAGS, cancellable, NULL);
+ if (info != NULL)
+ file_walk_full (parent, info, cancellable, callback, user_data);
+
+ IDE_EXIT;
+}
+
+static void
+handle_preflight_cb (GFile *file,
+ GFileInfo *child_info,
+ gpointer user_data)
+{
+ IdeFileTransferStat *stat_buf = user_data;
+ GFileType file_type;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_FILE_INFO (child_info));
+ g_assert (stat_buf != NULL);
+
+ file_type = g_file_info_get_file_type (child_info);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY)
+ {
+ stat_buf->n_dirs_total++;
+ }
+ else if (file_type == G_FILE_TYPE_REGULAR)
+ {
+ stat_buf->n_files_total++;
+ stat_buf->n_bytes_total += g_file_info_get_size (child_info);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+handle_preflight (IdeFileTransfer *self,
+ GPtrArray *opers,
+ GCancellable *cancellable)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_FILE_TRANSFER (self));
+ g_assert (opers != NULL);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ IDE_EXIT;
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ g_assert (oper != NULL);
+ g_assert (IDE_IS_FILE_TRANSFER (oper->self));
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+
+ file_walk (oper->src, cancellable, handle_preflight_cb, &priv->stat_buf);
+
+ if (oper->error != NULL)
+ break;
+ }
+
+ IDE_EXIT;
+}
+
+static void
+ide_file_transfer_progress_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ IdeFileTransfer *self = user_data;
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ priv->stat_buf.n_bytes += (current_num_bytes - priv->last_num_bytes);
+}
+
+static void
+handle_copy_cb (GFile *file,
+ GFileInfo *child_info,
+ gpointer user_data)
+{
+ IdeFileTransferPrivate *priv;
+ g_autoptr(GFile) src = NULL;
+ g_autoptr(GFile) dst = NULL;
+ const gchar *name;
+ Oper *oper = user_data;
+ GFileType file_type;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_FILE_TRANSFER (oper->self));
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_FILE_INFO (child_info));
+
+ if (oper->error != NULL)
+ IDE_EXIT;
+
+ if (g_cancellable_is_cancelled (oper->cancellable))
+ IDE_EXIT;
+
+ priv = ide_file_transfer_get_instance_private (oper->self);
+
+ file_type = g_file_info_get_file_type (child_info);
+ name = g_file_info_get_name (child_info);
+
+ if (name == NULL)
+ IDE_EXIT;
+
+ src = g_file_get_child (file, name);
+
+ if (!g_file_equal (oper->src, src))
+ {
+ g_autofree gchar *relative = NULL;
+
+ relative = g_file_get_relative_path (oper->src, src);
+ dst = g_file_get_child (oper->dst, relative);
+ }
+ else
+ {
+ dst = g_object_ref (oper->dst);
+ }
+
+ priv->last_num_bytes = 0;
+
+ switch (file_type)
+ {
+ case G_FILE_TYPE_DIRECTORY:
+ g_file_make_directory_with_parents (dst, oper->cancellable, &oper->error);
+ break;
+
+ case G_FILE_TYPE_REGULAR:
+ case G_FILE_TYPE_SPECIAL:
+ case G_FILE_TYPE_SHORTCUT:
+ case G_FILE_TYPE_SYMBOLIC_LINK:
+ /* Try to use g_file_move() when we can */
+ if ((oper->flags & IDE_FILE_TRANSFER_FLAGS_MOVE) != 0)
+ g_file_move (src, dst,
+ G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
+ oper->cancellable,
+ ide_file_transfer_progress_cb,
+ oper->self,
+ &oper->error);
+ else
+ g_file_copy (src, dst,
+ G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
+ oper->cancellable,
+ ide_file_transfer_progress_cb,
+ oper->self,
+ &oper->error);
+ break;
+
+ case G_FILE_TYPE_UNKNOWN:
+ case G_FILE_TYPE_MOUNTABLE:
+ default:
+ break;
+ }
+
+ IDE_EXIT;
+}
+
+static void
+handle_copy (IdeFileTransfer *self,
+ GPtrArray *opers,
+ GCancellable *cancellable)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_FILE_TRANSFER (self));
+ g_assert (opers != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (g_cancellable_is_cancelled (cancellable))
+ IDE_EXIT;
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ g_assert (oper != NULL);
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+
+ oper->self = self;
+ oper->cancellable = cancellable;
+
+ if (oper->error == NULL)
+ {
+ file_walk (oper->src, cancellable, handle_copy_cb, oper);
+
+ if (oper->error != NULL)
+ break;
+ }
+ }
+
+ IDE_EXIT;
+}
+
+static void
+handle_removal (IdeFileTransfer *self,
+ GPtrArray *opers,
+ GCancellable *cancellable)
+{
+ g_autoptr(IdeDirectoryReaper) reaper = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_FILE_TRANSFER (self));
+ g_assert (opers != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (g_cancellable_is_cancelled (cancellable))
+ IDE_EXIT;
+
+ reaper = ide_directory_reaper_new ();
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ g_assert (oper != NULL);
+ g_assert (G_IS_FILE (oper->src));
+ g_assert (G_IS_FILE (oper->dst));
+
+ /* Don't delete anything if there was a failure */
+ if (oper->error != NULL)
+ IDE_EXIT;
+
+ if (g_file_query_file_type (oper->src, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) ==
G_FILE_TYPE_DIRECTORY)
+ ide_directory_reaper_add_directory (reaper, oper->src, 0);
+
+ ide_directory_reaper_add_file (reaper, oper->src, 0);
+ }
+
+ ide_directory_reaper_execute (reaper, cancellable, NULL);
+
+ IDE_EXIT;
+}
+
+static gboolean
+ide_file_transfer_do_notify_progress (gpointer data)
+{
+ IdeFileTransfer *self = data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_FILE_TRANSFER (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
+
+ IDE_RETURN (G_SOURCE_CONTINUE);
+}
+
+static void
+ide_file_transfer_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ IdeFileTransfer *self = source_object;
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+ GPtrArray *opers = task_data;
+ guint notify_source;
+
+ IDE_ENTRY;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (IDE_IS_FILE_TRANSFER (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (opers != NULL);
+
+ notify_source = g_timeout_add_full (G_PRIORITY_LOW,
+ 1000 / 4, /* 4x a second */
+ ide_file_transfer_do_notify_progress,
+ g_object_ref (self),
+ g_object_unref);
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ oper->self = self;
+ oper->cancellable = cancellable;
+ oper->flags = priv->flags;
+ }
+
+ handle_preflight (self, opers, cancellable);
+ handle_copy (self, opers, cancellable);
+ if ((priv->flags & IDE_FILE_TRANSFER_FLAGS_MOVE) != 0)
+ handle_removal (self, opers, cancellable);
+
+ for (guint i = 0; i < opers->len; i++)
+ {
+ Oper *oper = g_ptr_array_index (opers, i);
+
+ if (oper->error != NULL)
+ {
+ g_task_return_error (task, g_steal_pointer (&oper->error));
+ IDE_GOTO (cleanup);
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+
+cleanup:
+ g_source_remove (notify_source);
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_file_transfer_execute (IdeFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GError **error)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_FILE_TRANSFER (self), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+
+ task = g_task_new (self, cancellable, NULL, NULL);
+ g_task_set_source_tag (task, ide_file_transfer_execute);
+
+ if (priv->executed)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVAL,
+ "Transfer can only be executed once.");
+ IDE_RETURN (FALSE);
+ }
+
+ if (priv->opers->len == 0)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVAL,
+ "Transfer can only be executed once.");
+ IDE_RETURN (FALSE);
+ }
+
+ g_task_set_check_cancellable (task, TRUE);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_set_priority (task, io_priority);
+ g_task_set_task_data (task, g_steal_pointer (&priv->opers), (GDestroyNotify)g_ptr_array_unref);
+ g_task_run_in_thread_sync (task, ide_file_transfer_worker);
+
+ ret = g_task_propagate_boolean (task, error);
+
+ IDE_RETURN (ret);
+}
+
+void
+ide_file_transfer_execute_async (IdeFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_FILE_TRANSFER (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_file_transfer_execute);
+
+ if (priv->executed)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVAL,
+ "Transfer can only be executed once.");
+ IDE_EXIT;
+ }
+
+ priv->executed = TRUE;
+
+ if (priv->opers->len == 0)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVAL,
+ "No transfers were provided to execute");
+ IDE_EXIT;
+ }
+
+ g_task_set_check_cancellable (task, TRUE);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_set_priority (task, io_priority);
+ g_task_set_task_data (task, g_steal_pointer (&priv->opers), (GDestroyNotify)g_ptr_array_unref);
+ g_task_run_in_thread (task, ide_file_transfer_worker);
+
+ IDE_EXIT;
+}
+
+gboolean
+ide_file_transfer_execute_finish (IdeFileTransfer *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_FILE_TRANSFER (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (g_task_is_valid (G_TASK (result), self), FALSE);
+
+ ret = g_task_propagate_boolean (G_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
+
+/**
+ * ide_file_transfer_stat:
+ * @self: a #IdeFileTransfer
+ * @stat_buf: (out): a #IdeFileTransferStat
+ *
+ * Gets statistics about the transfer progress.
+ *
+ * Since: 3.28
+ */
+void
+ide_file_transfer_stat (IdeFileTransfer *self,
+ IdeFileTransferStat *stat_buf)
+{
+ IdeFileTransferPrivate *priv = ide_file_transfer_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_FILE_TRANSFER (self));
+ g_return_if_fail (stat_buf != NULL);
+
+ *stat_buf = priv->stat_buf;
+}
diff --git a/src/libide/io/ide-file-transfer.h b/src/libide/io/ide-file-transfer.h
new file mode 100644
index 000000000..5426f50d2
--- /dev/null
+++ b/src/libide/io/ide-file-transfer.h
@@ -0,0 +1,90 @@
+/* ide-file-transfer.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#if !defined (IDE_IO_INSIDE) && !defined (IDE_IO_COMPILATION)
+# error "Only <libide-io.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_FILE_TRANSFER (ide_file_transfer_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (IdeFileTransfer, ide_file_transfer, IDE, FILE_TRANSFER, GObject)
+
+struct _IdeFileTransferClass
+{
+ GObjectClass parent_class;
+};
+
+typedef enum
+{
+ IDE_FILE_TRANSFER_FLAGS_NONE = 0,
+ IDE_FILE_TRANSFER_FLAGS_MOVE = 1 << 0,
+} IdeFileTransferFlags;
+
+typedef struct
+{
+ gint64 n_files_total;
+ gint64 n_files;
+ gint64 n_dirs_total;
+ gint64 n_dirs;
+ gint64 n_bytes_total;
+ gint64 n_bytes;
+
+ /*< private >*/
+ gint64 _padding[10];
+} IdeFileTransferStat;
+
+IDE_AVAILABLE_IN_ALL
+IdeFileTransfer *ide_file_transfer_new (void);
+IDE_AVAILABLE_IN_ALL
+IdeFileTransferFlags ide_file_transfer_get_flags (IdeFileTransfer *self);
+IDE_AVAILABLE_IN_ALL
+void ide_file_transfer_set_flags (IdeFileTransfer *self,
+ IdeFileTransferFlags flags);
+IDE_AVAILABLE_IN_ALL
+gdouble ide_file_transfer_get_progress (IdeFileTransfer *self);
+IDE_AVAILABLE_IN_ALL
+void ide_file_transfer_stat (IdeFileTransfer *self,
+ IdeFileTransferStat *stat_buf);
+IDE_AVAILABLE_IN_ALL
+void ide_file_transfer_add (IdeFileTransfer *self,
+ GFile *src,
+ GFile *dest);
+IDE_AVAILABLE_IN_ALL
+void ide_file_transfer_execute_async (IdeFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_file_transfer_execute_finish (IdeFileTransfer *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_file_transfer_execute (IdeFileTransfer *self,
+ gint io_priority,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libide/io/libide-io.h b/src/libide/io/libide-io.h
index 1769b9690..cddcf34ef 100644
--- a/src/libide/io/libide-io.h
+++ b/src/libide/io/libide-io.h
@@ -29,8 +29,10 @@ G_BEGIN_DECLS
#include "ide-content-type.h"
#include "ide-directory-reaper.h"
+#include "ide-file-transfer.h"
#include "ide-gfile.h"
#include "ide-line-reader.h"
+#include "ide-io-enums.h"
#include "ide-marked-content.h"
#include "ide-path.h"
#include "ide-persistent-map-builder.h"
diff --git a/src/libide/io/meson.build b/src/libide/io/meson.build
index dfaa4eddc..7f039c7e1 100644
--- a/src/libide/io/meson.build
+++ b/src/libide/io/meson.build
@@ -1,3 +1,4 @@
+libide_io_header_dir = join_paths(libide_header_dir, 'io')
libide_io_header_subdir = join_paths(libide_header_subdir, 'io')
libide_include_directories += include_directories('.')
@@ -8,6 +9,7 @@ libide_include_directories += include_directories('.')
libide_io_public_headers = [
'ide-content-type.h',
'ide-directory-reaper.h',
+ 'ide-file-transfer.h',
'ide-gfile.h',
'ide-line-reader.h',
'ide-marked-content.h',
@@ -35,6 +37,7 @@ install_headers(libide_io_public_headers, subdir: libide_io_header_subdir)
libide_io_public_sources = [
'ide-content-type.c',
'ide-directory-reaper.c',
+ 'ide-file-transfer.c',
'ide-gfile.c',
'ide-line-reader.c',
'ide-marked-content.c',
@@ -47,8 +50,28 @@ libide_io_public_sources = [
'ide-shell.c',
]
+libide_io_generated_headers = []
libide_io_sources = libide_io_public_sources
+#
+# Enum generation
+#
+
+libide_io_enum_headers = [
+ 'ide-file-transfer.h',
+]
+
+libide_io_enums = gnome.mkenums_simple('ide-io-enums',
+ body_prefix: '#include "config.h"',
+ header_prefix: '#include <libide-core.h>',
+ decorator: '_IDE_EXTERN',
+ sources: libide_io_enum_headers,
+ install_header: true,
+ install_dir: libide_io_header_dir,
+)
+libide_io_generated_headers += [libide_io_enums[1]]
+libide_io_sources += [libide_io_enums[0]]
+
#
# Dependencies
#
@@ -63,9 +86,10 @@ libide_io_deps = [
# Library Definitions
#
-libide_io = static_library('ide-io-' + libide_api_version, libide_io_sources,
- dependencies: libide_io_deps,
- c_args: libide_args + release_args + ['-DIDE_IO_COMPILATION'],
+libide_io = static_library('ide-io-' + libide_api_version,
+ libide_io_sources + libide_io_generated_headers,
+ dependencies: libide_io_deps,
+ c_args: libide_args + release_args + ['-DIDE_IO_COMPILATION'],
)
libide_io_dep = declare_dependency(
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]