[gnome-builder] transfers: refactory IdeTransfer and IdeTransferManager



commit 932abefcf28de127e3fa6641cba387e377546847
Author: Christian Hergert <chergert redhat com>
Date:   Sat Feb 18 18:09:55 2017 -0800

    transfers: refactory IdeTransfer and IdeTransferManager
    
    These were super difficult to track down issues because of how the
    transfer was an interface and all the nastiness that comes with it.
    
    So this changes IdeTransfer to a GObject and drastically simplifies the
    transfer manager to remove the unused queuing/max-queue-depth property.
    
    Additionally, there is a signal that is emitted when we want the animation
    to occur, so we don't have to use hueristics there. Additionally we can
    drop all the "pump row messages/progress" crap now that we can rely on
    proper properties from the transfer base class.

 libide/buildsystem/ide-build-stage-transfer.c |    4 +-
 libide/transfers/ide-transfer-manager.c       |  402 +++++-------------------
 libide/transfers/ide-transfer-manager.h       |    7 -
 libide/transfers/ide-transfer-row.c           |   60 ++---
 libide/transfers/ide-transfer-row.h           |    1 -
 libide/transfers/ide-transfer.c               |  420 ++++++++++++++++++++++---
 libide/transfers/ide-transfer.h               |   47 ++-
 libide/transfers/ide-transfers-button.c       |   53 +---
 plugins/flatpak/gbp-flatpak-transfer.c        |  171 ++++------
 plugins/flatpak/gbp-flatpak-transfer.h        |    2 +-
 plugins/rustup/rustup_plugin/__init__.py      |   17 +-
 11 files changed, 598 insertions(+), 586 deletions(-)
---
diff --git a/libide/buildsystem/ide-build-stage-transfer.c b/libide/buildsystem/ide-build-stage-transfer.c
index 5edefe3..a71346e 100644
--- a/libide/buildsystem/ide-build-stage-transfer.c
+++ b/libide/buildsystem/ide-build-stage-transfer.c
@@ -79,7 +79,7 @@ ide_build_stage_transfer_execute_async (IdeBuildStage       *stage,
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, ide_build_stage_transfer_execute_async);
 
-  if (ide_transfer_has_completed (self->transfer))
+  if (ide_transfer_get_completed (self->transfer))
     {
       g_task_return_boolean (task, TRUE);
       return;
@@ -173,7 +173,7 @@ ide_build_stage_transfer_class_init (IdeBuildStageTransferClass *klass)
                          "The transfer to perform as part of the stage",
                          IDE_TYPE_TRANSFER,
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
-  
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
diff --git a/libide/transfers/ide-transfer-manager.c b/libide/transfers/ide-transfer-manager.c
index dc5af2f..0a6585c 100644
--- a/libide/transfers/ide-transfer-manager.c
+++ b/libide/transfers/ide-transfer-manager.c
@@ -29,15 +29,10 @@
 struct _IdeTransferManager
 {
   GObject    parent_instance;
-  guint      max_active;
   GPtrArray *transfers;
 };
 
-static void list_model_iface_init                 (GListModelInterface *iface);
-static void ide_transfer_manager_pump             (IdeTransferManager  *self);
-static void ide_transfer_manager_execute_complete (IdeTransferManager *self,
-                                                   GTask              *task,
-                                                   const GError       *reason);
+static void list_model_iface_init (GListModelInterface *iface);
 
 G_DEFINE_TYPE_EXTENDED (IdeTransferManager, ide_transfer_manager, IDE_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
@@ -45,7 +40,6 @@ G_DEFINE_TYPE_EXTENDED (IdeTransferManager, ide_transfer_manager, IDE_TYPE_OBJEC
 enum {
   PROP_0,
   PROP_HAS_ACTIVE,
-  PROP_MAX_ACTIVE,
   PROP_PROGRESS,
   N_PROPS
 };
@@ -53,159 +47,13 @@ enum {
 enum {
   TRANSFER_COMPLETED,
   TRANSFER_FAILED,
+  ALL_TRANSFERS_COMPLETED,
   N_SIGNALS
 };
 
 static GParamSpec *properties [N_PROPS];
 static guint signals [N_SIGNALS];
 
-#define GET_BOOLEAN(obj,name)     (NULL != g_object_get_data(G_OBJECT(obj), name))
-#define SET_BOOLEAN(obj,name,val) (g_object_set_data(G_OBJECT(obj), name, GINT_TO_POINTER(val)))
-
-static void
-transfer_cancel (IdeTransfer *transfer)
-{
-  GCancellable *cancellable;
-
-  g_assert (IDE_IS_TRANSFER (transfer));
-
-  cancellable = g_object_get_data (G_OBJECT (transfer), "IDE_TRANSFER_CANCELLABLE");
-  if (G_IS_CANCELLABLE (cancellable) && !g_cancellable_is_cancelled (cancellable))
-    g_cancellable_cancel (cancellable);
-}
-
-static gboolean
-transfer_get_active (IdeTransfer *transfer)
-{
-  return GET_BOOLEAN (transfer, "IDE_TRANSFER_ACTIVE");
-}
-
-static void
-transfer_set_active (IdeTransfer *transfer,
-                     gboolean     active)
-{
-  SET_BOOLEAN (transfer, "IDE_TRANSFER_ACTIVE", active);
-}
-
-static void
-transfer_set_completed (IdeTransfer *transfer,
-                        gboolean     completed)
-{
-  SET_BOOLEAN (transfer, "IDE_TRANSFER_COMPLETED", completed);
-}
-
-static guint
-ide_transfer_manager_count_active (IdeTransferManager *self)
-{
-  guint active = 0;
-
-  g_assert (IDE_IS_TRANSFER_MANAGER (self));
-
-  for (guint i = 0; i < self->transfers->len; i++)
-    {
-      IdeTransfer *transfer = g_ptr_array_index (self->transfers, i);
-
-      if (transfer_get_active (transfer) && !ide_transfer_has_completed (transfer))
-        active++;
-    }
-
-  return active;
-}
-
-static void
-ide_transfer_manager_execute_cb (GObject      *object,
-                                 GAsyncResult *result,
-                                 gpointer      user_data)
-{
-  IdeTransfer *transfer = (IdeTransfer *)object;
-  g_autoptr(IdeTransferManager) self = user_data;
-  g_autoptr(GError) error = NULL;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_TRANSFER_MANAGER (self));
-  g_assert (IDE_IS_TRANSFER (transfer));
-
-  transfer_set_completed (transfer, TRUE);
-
-  if (!ide_transfer_execute_finish (transfer, result, &error))
-    {
-      IdeContext *context;
-
-      context = ide_object_get_context (IDE_OBJECT (self));
-      ide_context_warning (context, "%s", error->message);
-
-      g_signal_emit (self, signals [TRANSFER_FAILED], 0, transfer, error);
-    }
-  else
-    g_signal_emit (self, signals [TRANSFER_COMPLETED], 0, transfer);
-
-  ide_transfer_manager_pump (self);
-
-  IDE_EXIT;
-}
-
-static void
-ide_transfer_manager_begin (IdeTransferManager *self,
-                            IdeTransfer        *transfer)
-{
-  GCancellable *cancellable;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_TRANSFER_MANAGER (self));
-  g_assert (IDE_IS_TRANSFER (transfer));
-
-  transfer_set_active (transfer, TRUE);
-
-  cancellable = g_cancellable_new ();
-
-  g_object_set_data_full (G_OBJECT (transfer),
-                          "IDE_TRANSFER_CANCELLABLE",
-                          cancellable,
-                          g_object_unref);
-
-  ide_transfer_execute_async (transfer,
-                              cancellable,
-                              ide_transfer_manager_execute_cb,
-                              g_object_ref (self));
-
-  IDE_EXIT;
-}
-
-static void
-ide_transfer_manager_pump (IdeTransferManager *self)
-{
-  guint active;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_TRANSFER_MANAGER (self));
-
-  active = ide_transfer_manager_count_active (self);
-
-  if (active < self->max_active)
-    {
-      for (guint i = 0; i < self->transfers->len; i++)
-        {
-          IdeTransfer *transfer = g_ptr_array_index (self->transfers, i);
-
-          if (!transfer_get_active (transfer))
-            {
-              active++;
-              ide_transfer_manager_begin (self, transfer);
-              if (active >= self->max_active)
-                break;
-            }
-        }
-    }
-
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_ACTIVE]);
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
-
-  IDE_EXIT;
-}
-
 /**
  * ide_transfer_manager_get_has_active:
  *
@@ -222,35 +70,13 @@ ide_transfer_manager_get_has_active (IdeTransferManager *self)
     {
       IdeTransfer *transfer = g_ptr_array_index (self->transfers, i);
 
-      if (transfer_get_active (transfer) && !ide_transfer_has_completed (transfer))
+      if (ide_transfer_get_active (transfer))
         return TRUE;
     }
 
   return FALSE;
 }
 
-guint
-ide_transfer_manager_get_max_active (IdeTransferManager *self)
-{
-  g_return_val_if_fail (IDE_IS_TRANSFER_MANAGER (self), 0);
-
-  return self->max_active;
-}
-
-void
-ide_transfer_manager_set_max_active (IdeTransferManager *self,
-                                     guint               max_active)
-{
-  g_return_if_fail (IDE_IS_TRANSFER_MANAGER (self));
-
-  if (self->max_active != max_active)
-    {
-      self->max_active = max_active;
-      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_ACTIVE]);
-      ide_transfer_manager_pump (self);
-    }
-}
-
 static void
 ide_transfer_manager_finalize (GObject *object)
 {
@@ -275,10 +101,6 @@ ide_transfer_manager_get_property (GObject    *object,
       g_value_set_boolean (value, ide_transfer_manager_get_has_active (self));
       break;
 
-    case PROP_MAX_ACTIVE:
-      g_value_set_uint (value, ide_transfer_manager_get_max_active (self));
-      break;
-
     case PROP_PROGRESS:
       g_value_set_double (value, ide_transfer_manager_get_progress (self));
       break;
@@ -289,32 +111,12 @@ ide_transfer_manager_get_property (GObject    *object,
 }
 
 static void
-ide_transfer_manager_set_property (GObject      *object,
-                                   guint         prop_id,
-                                   const GValue *value,
-                                   GParamSpec   *pspec)
-{
-  IdeTransferManager *self = IDE_TRANSFER_MANAGER (object);
-
-  switch (prop_id)
-    {
-    case PROP_MAX_ACTIVE:
-      ide_transfer_manager_set_max_active (self, g_value_get_uint (value));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
 ide_transfer_manager_class_init (IdeTransferManagerClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->finalize = ide_transfer_manager_finalize;
   object_class->get_property = ide_transfer_manager_get_property;
-  object_class->set_property = ide_transfer_manager_set_property;
 
   /**
    * IdeTransferManager:has-active:
@@ -329,20 +131,11 @@ ide_transfer_manager_class_init (IdeTransferManagerClass *klass)
                           (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   /**
-   * IdeTransferManager:max-active:
+   * IdeTransferManager:progress:
    *
-   * Sets the max number of transfers to have active at one time.
-   * Set to zero for a sensible default.
+   * A double between and including 0.0 and 1.0 describing the progress of
+   * all tasks.
    */
-  properties [PROP_MAX_ACTIVE] =
-    g_param_spec_uint ("max-active",
-                       "Max Active",
-                       "Max Active",
-                       0,
-                       G_MAXUINT,
-                       0,
-                       (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
   properties [PROP_PROGRESS] =
     g_param_spec_double ("progress",
                          "Progress",
@@ -355,6 +148,17 @@ ide_transfer_manager_class_init (IdeTransferManagerClass *klass)
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
   /**
+   * IdeTransferManager::all-transfers-completed:
+   *
+   * This signal is emitted when all of the transfers have completed or failed.
+   */
+  signals [ALL_TRANSFERS_COMPLETED] =
+    g_signal_new ("all-transfers-completed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  /**
    * IdeTransferManager::transfer-completed:
    * @self: An #IdeTransferManager
    * @transfer: An #IdeTransfer
@@ -390,7 +194,6 @@ ide_transfer_manager_class_init (IdeTransferManagerClass *klass)
 static void
 ide_transfer_manager_init (IdeTransferManager *self)
 {
-  self->max_active = DEFAULT_MAX_ACTIVE;
   self->transfers = g_ptr_array_new_with_free_func (g_object_unref);
 }
 
@@ -405,26 +208,21 @@ ide_transfer_manager_notify_progress (IdeTransferManager *self,
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
 }
 
-void
-ide_transfer_manager_queue (IdeTransferManager *self,
-                            IdeTransfer        *transfer)
+static gboolean
+ide_transfer_manager_append (IdeTransferManager *self,
+                             IdeTransfer        *transfer)
 {
   guint position;
 
   IDE_ENTRY;
 
-  g_return_if_fail (IDE_IS_TRANSFER_MANAGER (self));
-  g_return_if_fail (IDE_IS_TRANSFER (transfer));
+  g_return_val_if_fail (IDE_IS_TRANSFER_MANAGER (self), FALSE);
+  g_return_val_if_fail (IDE_IS_TRANSFER (transfer), FALSE);
 
   for (guint i = 0; i < self->transfers->len; i++)
     {
-      IdeTransfer *ele = g_ptr_array_index (self->transfers, i);
-
-      if (ele == transfer)
-        {
-          transfer_set_active (transfer, FALSE);
-          goto already_inserted;
-        }
+      if (transfer == (IdeTransfer *)g_ptr_array_index (self->transfers, i))
+        IDE_RETURN (FALSE);
     }
 
   g_signal_connect_object (transfer,
@@ -437,10 +235,7 @@ ide_transfer_manager_queue (IdeTransferManager *self,
   g_ptr_array_add (self->transfers, g_object_ref (transfer));
   g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
 
-already_inserted:
-  ide_transfer_manager_pump (self);
-
-  IDE_EXIT;
+  IDE_RETURN (TRUE);
 }
 
 void
@@ -454,26 +249,12 @@ ide_transfer_manager_cancel_all (IdeTransferManager *self)
     {
       IdeTransfer *transfer = g_ptr_array_index (self->transfers, i);
 
-      transfer_cancel (transfer);
+      ide_transfer_cancel (transfer);
     }
 
   IDE_EXIT;
 }
 
-void
-ide_transfer_manager_cancel (IdeTransferManager *self,
-                             IdeTransfer        *transfer)
-{
-  IDE_ENTRY;
-
-  g_return_if_fail (IDE_IS_TRANSFER_MANAGER (self));
-  g_return_if_fail (IDE_IS_TRANSFER (transfer));
-
-  transfer_cancel (transfer);
-
-  IDE_EXIT;
-}
-
 /**
  * ide_transfer_manager_clear:
  *
@@ -490,7 +271,7 @@ ide_transfer_manager_clear (IdeTransferManager *self)
     {
       IdeTransfer *transfer = g_ptr_array_index (self->transfers, i - 1);
 
-      if (ide_transfer_has_completed (transfer))
+      if (!ide_transfer_get_active (transfer))
         {
           g_ptr_array_remove_index (self->transfers, i - 1);
           g_list_model_items_changed (G_LIST_MODEL (self), i - 1, 1, 0);
@@ -545,81 +326,64 @@ ide_transfer_manager_get_progress (IdeTransferManager *self)
 
   g_return_val_if_fail (IDE_IS_TRANSFER_MANAGER (self), 0.0);
 
-  if (self->transfers->len == 0)
-    return 0.0;
-
-  for (guint i = 0; i < self->transfers->len; i++)
+  if (self->transfers->len > 0)
     {
-      IdeTransfer *transfer = g_ptr_array_index (self->transfers, i);
-      gdouble progress;
-
-      progress = ide_transfer_get_progress (transfer);
-      total += MAX (0.0, MIN (1.0, progress));
+      for (guint i = 0; i < self->transfers->len; i++)
+        {
+          IdeTransfer *transfer = g_ptr_array_index (self->transfers, i);
+          gdouble progress = ide_transfer_get_progress (transfer);
+          total += MAX (0.0, MIN (1.0, progress));
+        }
+      total /= (gdouble)self->transfers->len;
     }
 
-  return total / (gdouble)self->transfers->len;
+  return total;
 }
 
 static void
-ide_transfer_manager_execute_transfer_completed (IdeTransferManager *self,
-                                                 IdeTransfer        *transfer,
-                                                 GTask              *task)
+ide_transfer_manager_execute_cb (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
 {
-  IdeTransfer *task_data;
-
-  g_assert (IDE_IS_TRANSFER_MANAGER (self));
-  g_assert (IDE_IS_TRANSFER (transfer));
-  g_assert (G_IS_TASK (task));
-
-  task_data = g_task_get_task_data (task);
-
-  if (task_data == transfer)
-    ide_transfer_manager_execute_complete (self, task, NULL);
-}
+  IdeTransfer *transfer = (IdeTransfer *)object;
+  IdeTransferManager *self;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
 
-static void
-ide_transfer_manager_execute_transfer_failed (IdeTransferManager *self,
-                                              IdeTransfer        *transfer,
-                                              const GError       *reason,
-                                              GTask              *task)
-{
-  IdeTransfer *task_data;
+  IDE_ENTRY;
 
-  g_assert (IDE_IS_TRANSFER_MANAGER (self));
   g_assert (IDE_IS_TRANSFER (transfer));
-  g_assert (reason != NULL);
   g_assert (G_IS_TASK (task));
 
-  task_data = g_task_get_task_data (task);
+  self = g_task_get_source_object (task);
 
-  if (task_data == transfer)
-    ide_transfer_manager_execute_complete (self, task, reason);
-}
-
-static void
-ide_transfer_manager_execute_complete (IdeTransferManager *self,
-                                       GTask              *task,
-                                       const GError       *reason)
-{
-  g_assert (IDE_IS_TRANSFER_MANAGER (self));
-  g_assert (G_IS_TASK (task));
+  if (!ide_transfer_execute_finish (transfer, result, &error))
+    {
+      g_signal_emit (self, signals[TRANSFER_FAILED], 0, transfer, error);
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_GOTO (notify_properties);
+    }
+  else
+    {
+      g_signal_emit (self, signals[TRANSFER_COMPLETED], 0, transfer);
+    }
 
-  g_signal_handlers_disconnect_by_func (self,
-                                        G_CALLBACK (ide_transfer_manager_execute_transfer_completed),
-                                        task);
+  if (!ide_transfer_manager_get_has_active (self))
+    g_signal_emit (self, signals[ALL_TRANSFERS_COMPLETED], 0);
 
-  g_signal_handlers_disconnect_by_func (self,
-                                        G_CALLBACK (ide_transfer_manager_execute_transfer_failed),
-                                        task);
+notify_properties:
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_ACTIVE]);
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
 
-  if (reason != NULL)
-    g_task_return_error (task, g_error_copy (reason));
-  else
-    g_task_return_boolean (task, TRUE);
+  IDE_EXIT;
 }
 
 /**
  * ide_transfer_manager_execute_async:
+ * @self: An #IdeTransferManager
+ * @cancellable: (nullable): A #GCancellable
+ * @callback: (nullable) A callback or %NULL
+ * @user_data: user data for @callback
  *
  * This is a convenience function that will queue @transfer into the transfer
  * manager and execute callback upon completion of the transfer. The success
@@ -635,29 +399,31 @@ ide_transfer_manager_execute_async (IdeTransferManager  *self,
 {
   g_autoptr(GTask) task = NULL;
 
+  IDE_ENTRY;
+
   g_return_if_fail (IDE_IS_TRANSFER_MANAGER (self));
   g_return_if_fail (IDE_IS_TRANSFER (transfer));
   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_transfer_manager_execute_async);
-  g_task_set_task_data (task, g_object_ref (transfer), g_object_unref);
-
-  g_signal_connect_data (self,
-                         "transfer-completed",
-                         G_CALLBACK (ide_transfer_manager_execute_transfer_completed),
-                         g_object_ref (task),
-                         (GClosureNotify)g_object_unref,
-                         0);
-
-  g_signal_connect_data (self,
-                         "transfer-failed",
-                         G_CALLBACK (ide_transfer_manager_execute_transfer_failed),
-                         g_object_ref (task),
-                         (GClosureNotify)g_object_unref,
-                         0);
-
-  ide_transfer_manager_queue (self, transfer);
+
+  if (!ide_transfer_manager_append (self, transfer))
+    {
+      if (ide_transfer_get_active (transfer))
+        {
+          g_warning ("%s is already active, ignoring transfer request",
+                     G_OBJECT_TYPE_NAME (transfer));
+          IDE_EXIT;
+        }
+    }
+
+  ide_transfer_execute_async (transfer,
+                              cancellable,
+                              ide_transfer_manager_execute_cb,
+                              g_steal_pointer (&task));
+
+  IDE_EXIT;
 }
 
 gboolean
diff --git a/libide/transfers/ide-transfer-manager.h b/libide/transfers/ide-transfer-manager.h
index 1463c7d..0892f22 100644
--- a/libide/transfers/ide-transfer-manager.h
+++ b/libide/transfers/ide-transfer-manager.h
@@ -31,15 +31,8 @@ G_DECLARE_FINAL_TYPE (IdeTransferManager, ide_transfer_manager, IDE, TRANSFER_MA
 
 gdouble  ide_transfer_manager_get_progress   (IdeTransferManager   *self);
 gboolean ide_transfer_manager_get_has_active (IdeTransferManager   *self);
-guint    ide_transfer_manager_get_max_active (IdeTransferManager   *self);
-void     ide_transfer_manager_set_max_active (IdeTransferManager   *self,
-                                              guint                 max_active);
-void     ide_transfer_manager_cancel         (IdeTransferManager   *self,
-                                              IdeTransfer          *transfer);
 void     ide_transfer_manager_cancel_all     (IdeTransferManager   *self);
 void     ide_transfer_manager_clear          (IdeTransferManager   *self);
-void     ide_transfer_manager_queue          (IdeTransferManager   *self,
-                                              IdeTransfer          *transfer);
 void     ide_transfer_manager_execute_async  (IdeTransferManager   *self,
                                               IdeTransfer          *transfer,
                                               GCancellable         *cancellable,
diff --git a/libide/transfers/ide-transfer-row.c b/libide/transfers/ide-transfer-row.c
index 7d5b248..2fc6724 100644
--- a/libide/transfers/ide-transfer-row.c
+++ b/libide/transfers/ide-transfer-row.c
@@ -24,16 +24,16 @@
 
 struct _IdeTransferRow
 {
-  GtkListBoxRow parent_instance;
+  GtkListBoxRow    parent_instance;
 
-  IdeTransfer *transfer;
+  IdeTransfer     *transfer;
   EggBindingGroup *bindings;
 
-  GtkLabel *status;
-  GtkLabel *title;
-  GtkImage *image;
-  GtkProgressBar *progress;
-  GtkButton *cancel;
+  GtkLabel        *status;
+  GtkLabel        *title;
+  GtkImage        *image;
+  GtkProgressBar  *progress;
+  GtkButton       *cancel;
 };
 
 enum {
@@ -163,28 +163,28 @@ ide_transfer_row_init (IdeTransferRow *self)
 
   self->bindings = egg_binding_group_new ();
 
-  egg_binding_group_bind (self->bindings,
-                          "title",
-                          self->title,
-                          "label",
+  egg_binding_group_bind (self->bindings, "active",
+                          self->progress, "visible",
                           G_BINDING_SYNC_CREATE);
 
-  egg_binding_group_bind (self->bindings,
-                          "status",
-                          self->status,
-                          "label",
+  egg_binding_group_bind (self->bindings, "active",
+                          self->cancel, "visible",
                           G_BINDING_SYNC_CREATE);
 
-  egg_binding_group_bind (self->bindings,
-                          "progress",
-                          self->progress,
-                          "fraction",
+  egg_binding_group_bind (self->bindings, "title",
+                          self->title, "label",
                           G_BINDING_SYNC_CREATE);
 
-  egg_binding_group_bind (self->bindings,
-                          "icon-name",
-                          self->image,
-                          "icon-name",
+  egg_binding_group_bind (self->bindings, "status",
+                          self->status, "label",
+                          G_BINDING_SYNC_CREATE);
+
+  egg_binding_group_bind (self->bindings, "progress",
+                          self->progress, "fraction",
+                          G_BINDING_SYNC_CREATE);
+
+  egg_binding_group_bind (self->bindings, "icon-name",
+                          self->image, "icon-name",
                           G_BINDING_SYNC_CREATE);
 }
 
@@ -211,20 +211,6 @@ ide_transfer_row_set_transfer (IdeTransferRow *self,
   if (g_set_object (&self->transfer, transfer))
     {
       egg_binding_group_set_source (self->bindings, transfer);
-      ide_transfer_row_pump (self);
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TRANSFER]);
     }
 }
-
-void
-ide_transfer_row_pump (IdeTransferRow *self)
-{
-  gboolean completed;
-
-  g_return_if_fail (IDE_IS_TRANSFER_ROW (self));
-
-  completed = ide_transfer_has_completed (self->transfer);
-
-  gtk_widget_set_visible (GTK_WIDGET (self->cancel), !completed);
-  gtk_widget_set_visible (GTK_WIDGET (self->progress), !completed);
-}
diff --git a/libide/transfers/ide-transfer-row.h b/libide/transfers/ide-transfer-row.h
index 523875d..b369ed1 100644
--- a/libide/transfers/ide-transfer-row.h
+++ b/libide/transfers/ide-transfer-row.h
@@ -29,7 +29,6 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeTransferRow, ide_transfer_row, IDE, TRANSFER_ROW, GtkListBoxRow)
 
-void         ide_transfer_row_pump         (IdeTransferRow *self) G_GNUC_INTERNAL;
 IdeTransfer *ide_transfer_row_get_transfer (IdeTransferRow *self);
 void         ide_transfer_row_set_transfer (IdeTransferRow *self,
                                             IdeTransfer    *transfer);
diff --git a/libide/transfers/ide-transfer.c b/libide/transfers/ide-transfer.c
index d4d1832..82d1a6b 100644
--- a/libide/transfers/ide-transfer.c
+++ b/libide/transfers/ide-transfer.c
@@ -18,9 +18,35 @@
 
 #define G_LOG_DOMAIN "ide-transfer"
 
-#include "ide-transfer.h"
+#include "ide-debug.h"
 
-G_DEFINE_INTERFACE (IdeTransfer, ide_transfer, G_TYPE_OBJECT)
+#include "transfers/ide-transfer.h"
+
+typedef struct
+{
+  gchar *icon_name;
+  gchar *status;
+  gchar *title;
+  GCancellable *cancellable;
+  gdouble progress;
+  guint active : 1;
+  guint completed : 1;
+} IdeTransferPrivate;
+
+enum {
+  PROP_0,
+  PROP_ACTIVE,
+  PROP_COMPLETED,
+  PROP_ICON_NAME,
+  PROP_PROGRESS,
+  PROP_STATUS,
+  PROP_TITLE,
+  N_PROPS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeTransfer, ide_transfer, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
 
 static void
 ide_transfer_real_execute_async (IdeTransfer         *self,
@@ -49,40 +75,185 @@ ide_transfer_real_execute_finish (IdeTransfer   *self,
 }
 
 static void
-ide_transfer_default_init (IdeTransferInterface *iface)
-{
-  iface->execute_async = ide_transfer_real_execute_async;
-  iface->execute_finish = ide_transfer_real_execute_finish;
-
-  g_object_interface_install_property (iface,
-                                       g_param_spec_string ("title",
-                                                            "Title",
-                                                            "Title",
-                                                            NULL,
-                                                            (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-
-  g_object_interface_install_property (iface,
-                                       g_param_spec_string ("icon-name",
-                                                            "Icon Name",
-                                                            "Icon Name",
-                                                            NULL,
-                                                            (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-
-  g_object_interface_install_property (iface,
-                                       g_param_spec_double ("progress",
-                                                            "Progress",
-                                                            "Progress",
-                                                            0.0,
-                                                            1.0,
-                                                            0.0,
-                                                            (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-
-  g_object_interface_install_property (iface,
-                                       g_param_spec_string ("status",
-                                                            "Status",
-                                                            "Status",
-                                                            NULL,
-                                                            (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ide_transfer_finalize (GObject *object)
+{
+  IdeTransfer *self = (IdeTransfer *)object;
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_clear_pointer (&priv->icon_name, g_free);
+  g_clear_pointer (&priv->status, g_free);
+  g_clear_pointer (&priv->title, g_free);
+  g_clear_object (&priv->cancellable);
+
+  G_OBJECT_CLASS (ide_transfer_parent_class)->finalize (object);
+}
+
+static void
+ide_transfer_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  IdeTransfer *self = IDE_TRANSFER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, ide_transfer_get_active (self));
+      break;
+
+    case PROP_COMPLETED:
+      g_value_set_boolean (value, ide_transfer_get_completed (self));
+      break;
+
+    case PROP_ICON_NAME:
+      g_value_set_string (value, ide_transfer_get_icon_name (self));
+      break;
+
+    case PROP_PROGRESS:
+      g_value_set_double (value, ide_transfer_get_progress (self));
+      break;
+
+    case PROP_STATUS:
+      g_value_set_string (value, ide_transfer_get_status (self));
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, ide_transfer_get_title (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_transfer_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  IdeTransfer *self = IDE_TRANSFER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ICON_NAME:
+      ide_transfer_set_icon_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_PROGRESS:
+      ide_transfer_set_progress (self, g_value_get_double (value));
+      break;
+
+    case PROP_STATUS:
+      ide_transfer_set_status (self, g_value_get_string (value));
+      break;
+
+    case PROP_TITLE:
+      ide_transfer_set_title (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_transfer_class_init (IdeTransferClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_transfer_finalize;
+  object_class->get_property = ide_transfer_get_property;
+  object_class->set_property = ide_transfer_set_property;
+
+  klass->execute_async = ide_transfer_real_execute_async;
+  klass->execute_finish = ide_transfer_real_execute_finish;
+
+  properties [PROP_ACTIVE] =
+    g_param_spec_boolean ("active",
+                          "Active",
+                          "If the transfer is active",
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_COMPLETED] =
+    g_param_spec_boolean ("completed",
+                          "Completed",
+                          "If the transfer has completed successfully",
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_ICON_NAME] =
+    g_param_spec_string ("icon-name",
+                         "Icon Name",
+                         "The icon to display next to the transfer",
+                         "folder-download-symbolic",
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_PROGRESS] =
+    g_param_spec_double ("progress",
+                         "Progress",
+                         "The progress for the transfer between 0 adn 1",
+                         0.0,
+                         1.0,
+                         0.0,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_STATUS] =
+    g_param_spec_string ("status",
+                         "Status",
+                         "The status message for the transfer",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "Title",
+                         "The title of the transfer",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_transfer_init (IdeTransfer *self)
+{
+}
+
+static void
+ide_transfer_execute_cb (GObject      *object,
+                         GAsyncResult *result,
+                         gpointer      user_data)
+{
+  IdeTransfer *self = (IdeTransfer *)object;
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_TRANSFER (self));
+  g_assert (G_IS_TASK (task));
+
+  priv->active = FALSE;
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]);
+
+  ide_transfer_set_progress (self, 1.0);
+
+  if (!IDE_TRANSFER_GET_CLASS (self)->execute_finish (self, result, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  priv->completed = TRUE;
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_COMPLETED]);
+
+  g_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
 }
 
 void
@@ -91,10 +262,49 @@ ide_transfer_execute_async (IdeTransfer         *self,
                             GAsyncReadyCallback  callback,
                             gpointer             user_data)
 {
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+  g_autoptr(GTask) task = NULL;
+
+  IDE_ENTRY;
+
   g_assert (IDE_IS_TRANSFER (self));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  IDE_TRANSFER_GET_IFACE (self)->execute_async (self, cancellable, callback, user_data);
+  /*
+   * We already create our own wrapper task so that we can track completion
+   * cleanly from the subclass implementation. It also allows us to ensure
+   * that the subclasses execute_finish() is guaranteed to be called.
+   */
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_transfer_execute_async);
+
+  /*
+   * Wrap our own cancellable so that we can gracefully control
+   * the cancellation of the underlying transfer without affecting
+   * the callers cancellation state.
+   */
+  g_clear_object (&priv->cancellable);
+  priv->cancellable = g_cancellable_new ();
+
+  if (cancellable != NULL)
+    g_signal_connect_object (cancellable,
+                             "cancelled",
+                             G_CALLBACK (g_cancellable_cancel),
+                             priv->cancellable,
+                             G_CONNECT_SWAPPED);
+
+  priv->active = TRUE;
+  priv->completed = FALSE;
+
+  IDE_TRANSFER_GET_CLASS (self)->execute_async (self,
+                                                priv->cancellable,
+                                                ide_transfer_execute_cb,
+                                                g_steal_pointer (&task));
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]);
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_COMPLETED]);
+
+  IDE_EXIT;
 }
 
 gboolean
@@ -102,34 +312,148 @@ ide_transfer_execute_finish (IdeTransfer   *self,
                              GAsyncResult  *result,
                              GError       **error)
 {
+  gboolean ret;
+
+  IDE_ENTRY;
+
   g_return_val_if_fail (IDE_IS_TRANSFER (self), FALSE);
-  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  ret = g_task_propagate_boolean (G_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+const gchar *
+ide_transfer_get_icon_name (IdeTransfer *self)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_TRANSFER (self), NULL);
 
-  return IDE_TRANSFER_GET_IFACE (self)->execute_finish (self, result, error);
+  return priv->icon_name ?: "folder-download-symbolic";
+}
+
+void
+ide_transfer_set_icon_name (IdeTransfer *self,
+                            const gchar *icon_name)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_TRANSFER (self));
+
+  if (g_strcmp0 (priv->icon_name, icon_name) != 0)
+    {
+      g_free (priv->icon_name);
+      priv->icon_name = g_strdup (icon_name);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON_NAME]);
+    }
 }
 
 gdouble
 ide_transfer_get_progress (IdeTransfer *self)
 {
-  gdouble value = 0.0;
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
 
   g_return_val_if_fail (IDE_IS_TRANSFER (self), 0.0);
 
-  if (ide_transfer_has_completed (self))
-    return 1.0;
+  return priv->progress;
+}
+
+void
+ide_transfer_set_progress (IdeTransfer *self,
+                           gdouble      progress)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_TRANSFER (self));
+
+  if (progress != priv->progress)
+    {
+      priv->progress = progress;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
+    }
+}
+
+const gchar *
+ide_transfer_get_status (IdeTransfer *self)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
 
-  g_object_get (self, "progress", &value, NULL);
+  g_return_val_if_fail (IDE_IS_TRANSFER (self), NULL);
 
-  return value;
+  return priv->status;
+}
+
+void
+ide_transfer_set_status (IdeTransfer *self,
+                         const gchar *status)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_TRANSFER (self));
+
+  if (g_strcmp0 (priv->status, status) != 0)
+    {
+      g_free (priv->status);
+      priv->status = g_strdup (status);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_STATUS]);
+    }
+}
+
+const gchar *
+ide_transfer_get_title (IdeTransfer *self)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_TRANSFER (self), NULL);
+
+  return priv->title;
+}
+
+void
+ide_transfer_set_title (IdeTransfer *self,
+                        const gchar *title)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_TRANSFER (self));
+
+  if (g_strcmp0 (priv->title, title) != 0)
+    {
+      g_free (priv->title);
+      priv->title = g_strdup (title);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+    }
+}
+
+void
+ide_transfer_cancel (IdeTransfer *self)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_TRANSFER (self));
+
+  if (!g_cancellable_is_cancelled (priv->cancellable))
+    g_cancellable_cancel (priv->cancellable);
 }
 
 gboolean
-ide_transfer_has_completed (IdeTransfer *self)
+ide_transfer_get_completed (IdeTransfer *self)
 {
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
   g_return_val_if_fail (IDE_IS_TRANSFER (self), FALSE);
 
-  if (IDE_TRANSFER_GET_IFACE (self)->has_completed)
-    return IDE_TRANSFER_GET_IFACE (self)->has_completed (self);
+  return priv->completed;
+}
+
+gboolean
+ide_transfer_get_active (IdeTransfer *self)
+{
+  IdeTransferPrivate *priv = ide_transfer_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_TRANSFER (self), FALSE);
 
-  return !!g_object_get_data (G_OBJECT (self), "IDE_TRANSFER_COMPLETED");
+  return priv->active;
 }
diff --git a/libide/transfers/ide-transfer.h b/libide/transfers/ide-transfer.h
index 7dfe7fe..ec989f6 100644
--- a/libide/transfers/ide-transfer.h
+++ b/libide/transfers/ide-transfer.h
@@ -25,11 +25,11 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_TRANSFER (ide_transfer_get_type())
 
-G_DECLARE_INTERFACE (IdeTransfer, ide_transfer, IDE, TRANSFER, GObject)
+G_DECLARE_DERIVABLE_TYPE (IdeTransfer, ide_transfer, IDE, TRANSFER, GObject)
 
-struct _IdeTransferInterface
+struct _IdeTransferClass
 {
-  GTypeInterface parent;
+  GObjectClass parent_class;
 
   void     (*execute_async)  (IdeTransfer          *self,
                               GCancellable         *cancellable,
@@ -38,18 +38,39 @@ struct _IdeTransferInterface
   gboolean (*execute_finish) (IdeTransfer          *self,
                               GAsyncResult         *result,
                               GError              **error);
-  gboolean (*has_completed)  (IdeTransfer          *self);
+
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+  gpointer _reserved5;
+  gpointer _reserved6;
+  gpointer _reserved7;
+  gpointer _reserved8;
 };
 
-gdouble  ide_transfer_get_progress   (IdeTransfer          *self);
-void     ide_transfer_execute_async  (IdeTransfer          *self,
-                                      GCancellable         *cancellable,
-                                      GAsyncReadyCallback   callback,
-                                      gpointer              user_data);
-gboolean ide_transfer_execute_finish (IdeTransfer          *self,
-                                      GAsyncResult         *result,
-                                      GError              **error);
-gboolean ide_transfer_has_completed  (IdeTransfer          *self);
+void         ide_transfer_cancel         (IdeTransfer          *self);
+gboolean     ide_transfer_get_completed  (IdeTransfer          *self);
+gboolean     ide_transfer_get_active     (IdeTransfer          *self);
+const gchar *ide_transfer_get_icon_name  (IdeTransfer          *self);
+void         ide_transfer_set_icon_name  (IdeTransfer          *self,
+                                          const gchar          *icon_name);
+gdouble      ide_transfer_get_progress   (IdeTransfer          *self);
+void         ide_transfer_set_progress   (IdeTransfer          *self,
+                                          gdouble               progress);
+const gchar *ide_transfer_get_status     (IdeTransfer          *self);
+void         ide_transfer_set_status     (IdeTransfer          *self,
+                                          const gchar          *status);
+const gchar *ide_transfer_get_title      (IdeTransfer          *self);
+void         ide_transfer_set_title      (IdeTransfer          *self,
+                                          const gchar          *title);
+void         ide_transfer_execute_async  (IdeTransfer          *self,
+                                          GCancellable         *cancellable,
+                                          GAsyncReadyCallback   callback,
+                                          gpointer              user_data);
+gboolean     ide_transfer_execute_finish (IdeTransfer          *self,
+                                          GAsyncResult         *result,
+                                          GError              **error);
 
 G_END_DECLS
 
diff --git a/libide/transfers/ide-transfers-button.c b/libide/transfers/ide-transfers-button.c
index e13947a..670af3a 100644
--- a/libide/transfers/ide-transfers-button.c
+++ b/libide/transfers/ide-transfers-button.c
@@ -117,17 +117,13 @@ static void
 ide_transfers_button_cancel_clicked (IdeTransfersButton *self,
                                      IdeTransferRow     *row)
 {
-  IdeTransferManager *transfer_manager;
   IdeTransfer *transfer;
-  IdeContext *context;
 
   g_assert (IDE_IS_TRANSFERS_BUTTON (self));
   g_assert (IDE_IS_TRANSFER_ROW (row));
 
-  if (NULL != (transfer = ide_transfer_row_get_transfer (row)) &&
-      NULL != (context = ide_widget_get_context (GTK_WIDGET (self))) &&
-      NULL != (transfer_manager = ide_context_get_transfer_manager (context)))
-    ide_transfer_manager_cancel (transfer_manager, transfer);
+  if (NULL != (transfer = ide_transfer_row_get_transfer (row)))
+    ide_transfer_cancel (transfer);
 }
 
 static GtkWidget *
@@ -175,47 +171,6 @@ ide_transfers_button_update_visibility (IdeTransfersButton *self)
 
   IDE_EXIT;
 }
-
-static void
-find_transfer_row (GtkWidget *widget,
-                   gpointer   user_data)
-{
-  struct {
-    IdeTransfer    *transfer;
-    IdeTransferRow *row;
-  } *lookup = user_data;
-
-  if (lookup->row != NULL)
-    return;
-
-  if (lookup->transfer == ide_transfer_row_get_transfer (IDE_TRANSFER_ROW (widget)))
-    lookup->row = IDE_TRANSFER_ROW (widget);
-}
-
-static void
-ide_transfers_button_transfer_completed (IdeTransfersButton *self,
-                                         IdeTransfer        *transfer,
-                                         IdeTransferManager *transfer_manager)
-{
-  struct {
-    IdeTransfer    *transfer;
-    IdeTransferRow *row;
-  } lookup = { transfer, NULL };
-
-  g_assert (IDE_IS_TRANSFERS_BUTTON (self));
-  g_assert (IDE_IS_TRANSFER (transfer));
-  g_assert (IDE_IS_TRANSFER_MANAGER (transfer_manager));
-
-  gtk_container_foreach (GTK_CONTAINER (self->list_box),
-                         find_transfer_row,
-                         &lookup);
-
-  if (lookup.row != NULL)
-    ide_transfer_row_pump (lookup.row);
-
-  ide_transfers_button_begin_theatrics (self);
-}
-
 static void
 ide_transfers_button_context_set (GtkWidget  *widget,
                                   IdeContext *context)
@@ -232,8 +187,8 @@ ide_transfers_button_context_set (GtkWidget  *widget,
   transfer_manager = ide_context_get_transfer_manager (context);
 
   g_signal_connect_object (transfer_manager,
-                           "transfer-completed",
-                           G_CALLBACK (ide_transfers_button_transfer_completed),
+                           "all-transfers-completed",
+                           G_CALLBACK (ide_transfers_button_begin_theatrics),
                            self,
                            G_CONNECT_SWAPPED);
 
diff --git a/plugins/flatpak/gbp-flatpak-transfer.c b/plugins/flatpak/gbp-flatpak-transfer.c
index 7dc12f4..5a88f94 100644
--- a/plugins/flatpak/gbp-flatpak-transfer.c
+++ b/plugins/flatpak/gbp-flatpak-transfer.c
@@ -26,18 +26,16 @@
 
 struct _GbpFlatpakTransfer
 {
-  IdeObject    parent_instance;
-
-  gchar       *id;
-  gchar       *arch;
-  gchar       *branch;
-  gchar       *status;
-  gchar       *title;
-  gdouble      progress;
-  guint        has_runtime : 1;
-  guint        force_update : 1;
-  guint        finished : 1;
-  guint        failed : 1;
+  IdeTransfer parent_instance;
+
+  gchar *id;
+  gchar *arch;
+  gchar *branch;
+
+  guint  has_runtime : 1;
+  guint  force_update : 1;
+  guint  finished : 1;
+  guint  failed : 1;
 };
 
 enum {
@@ -46,24 +44,17 @@ enum {
   PROP_ARCH,
   PROP_BRANCH,
   PROP_FORCE_UPDATE,
-  PROP_TITLE,
-  PROP_ICON_NAME,
-  PROP_PROGRESS,
-  PROP_STATUS,
   N_PROPS
 };
 
-static void transfer_iface_init (IdeTransferInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (GbpFlatpakTransfer, gbp_flatpak_transfer, G_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (IDE_TYPE_TRANSFER, transfer_iface_init))
+G_DEFINE_TYPE (GbpFlatpakTransfer, gbp_flatpak_transfer, IDE_TYPE_TRANSFER)
 
 static GParamSpec *properties [N_PROPS];
 
 static void
 gbp_flatpak_transfer_update_title (GbpFlatpakTransfer *self)
 {
-  GString *str;
+  g_autoptr(GString) str = NULL;
 
   g_return_if_fail (GBP_IS_FLATPAK_TRANSFER (self));
 
@@ -89,10 +80,29 @@ gbp_flatpak_transfer_update_title (GbpFlatpakTransfer *self)
 
   g_string_append_printf (str, "%s %s", self->id, self->branch);
 
-  g_free (self->title);
-  self->title = g_string_free (str, FALSE);
+  ide_transfer_set_title (IDE_TRANSFER (self), str->str);
+}
 
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+static void
+task_completed (GbpFlatpakTransfer *self,
+                GParamSpec         *pspec,
+                GTask              *task)
+{
+  g_assert (GBP_IS_FLATPAK_TRANSFER (self));
+  g_assert (G_IS_TASK (task));
+
+  self->finished = TRUE;
+
+  gbp_flatpak_transfer_update_title (self);
+
+  ide_transfer_set_progress (IDE_TRANSFER (self), 1.0);
+
+  if (self->failed)
+    ide_transfer_set_status (IDE_TRANSFER (self), _("Failed to install runtime"));
+  else if (self->finished && self->has_runtime)
+    ide_transfer_set_status (IDE_TRANSFER (self), _("Runtime has been updated"));
+  else
+    ide_transfer_set_status (IDE_TRANSFER (self), _("Runtime has been installed"));
 }
 
 static void
@@ -106,16 +116,12 @@ proxy_notify (GbpFlatpakTransfer *self,
 
   if (g_strcmp0 (pspec->name, "message") == 0)
     {
-      g_free (self->status);
-      self->status = ide_progress_get_message (progress);
-      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
+      g_autofree gchar *message = ide_progress_get_message (progress);
+      ide_transfer_set_status (IDE_TRANSFER (self), message);
     }
 
   if (g_strcmp0 (pspec->name, "fraction") == 0)
-    {
-      self->progress = ide_progress_get_fraction (progress);
-      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PROGRESS]);
-    }
+    ide_transfer_set_progress (IDE_TRANSFER (self), ide_progress_get_fraction (progress));
 }
 
 static void
@@ -141,19 +147,6 @@ gbp_flatpak_transfer_execute_cb (GObject      *object,
 }
 
 static void
-task_completed (GbpFlatpakTransfer *self,
-                GParamSpec         *pspec,
-                GTask              *task)
-{
-  g_assert (GBP_IS_FLATPAK_TRANSFER (self));
-  g_assert (G_IS_TASK (task));
-
-  self->finished = TRUE;
-  gbp_flatpak_transfer_update_title (self);
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_STATUS]);
-}
-
-static void
 gbp_flatpak_transfer_execute_async (IdeTransfer         *transfer,
                                     GCancellable        *cancellable,
                                     GAsyncReadyCallback  callback,
@@ -241,28 +234,15 @@ gbp_flatpak_transfer_execute_finish (IdeTransfer   *transfer,
 }
 
 static void
-transfer_iface_init (IdeTransferInterface *iface)
-{
-  iface->execute_async = gbp_flatpak_transfer_execute_async;
-  iface->execute_finish = gbp_flatpak_transfer_execute_finish;
-}
-
-static void
 gbp_flatpak_transfer_finalize (GObject *object)
 {
   GbpFlatpakTransfer *self = (GbpFlatpakTransfer *)object;
 
-  IDE_ENTRY;
-
   g_clear_pointer (&self->id, g_free);
   g_clear_pointer (&self->arch, g_free);
   g_clear_pointer (&self->branch, g_free);
-  g_clear_pointer (&self->title, g_free);
-  g_clear_pointer (&self->status, g_free);
 
   G_OBJECT_CLASS (gbp_flatpak_transfer_parent_class)->finalize (object);
-
-  IDE_EXIT;
 }
 
 static void
@@ -275,27 +255,20 @@ gbp_flatpak_transfer_get_property (GObject    *object,
 
   switch (prop_id)
     {
-    case PROP_STATUS:
-      if (self->failed)
-        g_value_set_static_string (value, _("Failed to install runtime"));
-      else if (self->finished && self->has_runtime)
-        g_value_set_static_string (value, _("Runtime has been updated"));
-      else if (self->finished)
-        g_value_set_static_string (value, _("Runtime has been installed"));
-      else
-        g_value_set_string (value, self->status);
+    case PROP_ID:
+      g_value_set_string (value, self->id);
       break;
 
-    case PROP_TITLE:
-      g_value_set_string (value, self->title);
+    case PROP_ARCH:
+      g_value_set_string (value, self->arch);
       break;
 
-    case PROP_ICON_NAME:
-      g_value_set_static_string (value, "folder-download-symbolic");
+    case PROP_BRANCH:
+      g_value_set_string (value, self->branch);
       break;
 
-    case PROP_PROGRESS:
-      g_value_set_double (value, self->progress);
+    case PROP_FORCE_UPDATE:
+      g_value_set_boolean (value, self->force_update);
       break;
 
     default:
@@ -321,15 +294,11 @@ gbp_flatpak_transfer_set_property (GObject      *object,
     case PROP_ARCH:
       g_free (self->arch);
       self->arch = g_value_dup_string (value);
-      if (self->arch == NULL)
-        self->arch = g_strdup (flatpak_get_default_arch ());
       break;
 
     case PROP_BRANCH:
       g_free (self->branch);
       self->branch = g_value_dup_string (value);
-      if (self->branch == NULL)
-        self->branch = g_strdup ("stable");
       break;
 
     case PROP_FORCE_UPDATE:
@@ -345,42 +314,42 @@ static void
 gbp_flatpak_transfer_class_init (GbpFlatpakTransferClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeTransferClass *transfer_class = IDE_TRANSFER_CLASS (klass);
 
   object_class->finalize = gbp_flatpak_transfer_finalize;
   object_class->get_property = gbp_flatpak_transfer_get_property;
   object_class->set_property = gbp_flatpak_transfer_set_property;
 
+  transfer_class->execute_async = gbp_flatpak_transfer_execute_async;
+  transfer_class->execute_finish = gbp_flatpak_transfer_execute_finish;
+
   properties [PROP_ID] =
-    g_param_spec_string ("id", NULL, NULL, NULL,
-                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+    g_param_spec_string ("id",
+                         "Id",
+                         "The runtime identifier such as org.gnome.Platform",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_ARCH] =
-    g_param_spec_string ("arch", NULL, NULL, NULL,
-                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+    g_param_spec_string ("arch",
+                         "Arch",
+                         "The arch identifier such as x86_64",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_BRANCH] =
-    g_param_spec_string ("branch", NULL, NULL, NULL,
-                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+    g_param_spec_string ("branch",
+                         "Branch",
+                         "The branch identifier such as 'stable'",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_FORCE_UPDATE] =
-    g_param_spec_boolean ("force-update", NULL, NULL, FALSE,
-                          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
-
-  properties [PROP_STATUS] =
-    g_param_spec_string ("status", NULL, NULL, NULL,
-                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-  properties [PROP_TITLE] =
-    g_param_spec_string ("title", NULL, NULL, NULL,
-                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-  properties [PROP_ICON_NAME] =
-    g_param_spec_string ("icon-name", NULL, NULL, NULL,
-                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
-
-  properties [PROP_PROGRESS] =
-    g_param_spec_double ("progress", NULL, NULL, 0.0, 100.0, 0.0,
-                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+    g_param_spec_boolean ("force-update",
+                          "Force Update",
+                          "If we should always try to at least update",
+                          FALSE,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 }
@@ -388,8 +357,6 @@ gbp_flatpak_transfer_class_init (GbpFlatpakTransferClass *klass)
 static void
 gbp_flatpak_transfer_init (GbpFlatpakTransfer *self)
 {
-  self->arch = g_strdup (flatpak_get_default_arch ());
-  self->branch = g_strdup ("master");
 }
 
 GbpFlatpakTransfer *
diff --git a/plugins/flatpak/gbp-flatpak-transfer.h b/plugins/flatpak/gbp-flatpak-transfer.h
index 7ea48d4..59d7b7e 100644
--- a/plugins/flatpak/gbp-flatpak-transfer.h
+++ b/plugins/flatpak/gbp-flatpak-transfer.h
@@ -25,7 +25,7 @@ G_BEGIN_DECLS
 
 #define GBP_TYPE_FLATPAK_TRANSFER (gbp_flatpak_transfer_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpFlatpakTransfer, gbp_flatpak_transfer, GBP, FLATPAK_TRANSFER, GObject)
+G_DECLARE_FINAL_TYPE (GbpFlatpakTransfer, gbp_flatpak_transfer, GBP, FLATPAK_TRANSFER, IdeTransfer)
 
 GbpFlatpakTransfer *gbp_flatpak_transfer_new (const gchar        *id,
                                               const gchar        *arch,
diff --git a/plugins/rustup/rustup_plugin/__init__.py b/plugins/rustup/rustup_plugin/__init__.py
index 269a939..3a490c8 100644
--- a/plugins/rustup/rustup_plugin/__init__.py
+++ b/plugins/rustup/rustup_plugin/__init__.py
@@ -212,8 +212,9 @@ class RustupApplicationAddin(GObject.Object, Ide.ApplicationAddin):
         workbench.get_context().get_transfer_manager().connect('transfer-failed', self.transfer_failed)
         self.workbenches.add(workbench)
         # add the current transfer to the new workbench
-        if self.active_transfer:
-            workbench.get_context().get_transfer_manager().queue(self.active_transfer)
+        # CJH: This isn't right, so we'll punt on this until we have application level transfers
+        # if self.active_transfer:
+        #    workbench.get_context().get_transfer_manager().execute_async(self.active_transfer, None, None)
 
     def transfer_completed(self, transfer_manager, transfer):
         # reset the active transfer on completion, ensures that new workbenches dont get an old transfer
@@ -231,8 +232,12 @@ class RustupApplicationAddin(GObject.Object, Ide.ApplicationAddin):
         self.active_transfer = transfer
         self.notify('busy')
         # run it in all transfer managers
+        # TODO: This isn't really correct, but we need to move transfer manager to
+        #       IdeApplication.get_transfer_manager()
         for workbench in self.workbenches:
-            workbench.get_context().get_transfer_manager().queue(transfer)
+            context = workbench.get_context()
+            transfers = context.get_transfer_manager()
+            transfers.execute_async(transfer)
 
     def install(self):
         self.run_transfer(RustupInstaller(mode=_MODE_INSTALL))
@@ -252,14 +257,10 @@ _STATE_INSTALL_COMP = 4
 _STATE_CHECK_UPDATE_SELF = 5
 _STATE_DOWN_UPDATE_SELF = 6
 
-class RustupInstaller(Ide.Object, Ide.Transfer):
+class RustupInstaller(Ide.Transfer):
     """
     The RustupInstaller handles the installation of rustup, rustc, rust-std and cargo.
     """
-    title = GObject.Property(type=str)
-    icon_name = GObject.Property(type=str)
-    progress = GObject.Property(type=float)
-    status = GObject.Property(type=str)
 
     def __init__(self, *args, **kwargs):
         if 'mode' in kwargs:


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