[gnome-software] Allow plugins to say that installation can/cannot be cancelled



commit 82bd6c445727af4e1fd83dad16dadafbd71bed9d
Author: Iain Lane <iain orangesquash org uk>
Date:   Tue Aug 8 14:58:56 2017 +0100

    Allow plugins to say that installation can/cannot be cancelled
    
    For example - it's not safe to cancel a distro package installation
    after it has gone past a certain point, or else you'll end up with a
    broken system.
    
    Also implement this for the PackageKit plugin by propagating the
    allow-cancel property up.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=785515

 lib/gs-app.c                              |   57 +++++++++++++++++++++++++++++
 lib/gs-app.h                              |    3 ++
 plugins/packagekit/gs-plugin-packagekit.c |   10 +++++
 src/gs-app-row.c                          |    6 +++-
 src/gs-details-page.c                     |   37 ++++++++++++++++++-
 5 files changed, 111 insertions(+), 2 deletions(-)
---
diff --git a/lib/gs-app.c b/lib/gs-app.c
index d63654a..5ae25fa 100644
--- a/lib/gs-app.c
+++ b/lib/gs-app.c
@@ -107,6 +107,7 @@ typedef struct
        AsAppScope               scope;
        AsBundleKind             bundle_kind;
        guint                    progress;
+       gboolean                 allow_cancel;
        GHashTable              *metadata;
        GPtrArray               *addons; /* of GsApp */
        GHashTable              *addons_hash; /* of "id" */
@@ -136,6 +137,7 @@ enum {
        PROP_KIND,
        PROP_STATE,
        PROP_PROGRESS,
+       PROP_CAN_CANCEL_INSTALLATION,
        PROP_INSTALL_DATE,
        PROP_QUIRK,
        PROP_LAST
@@ -792,6 +794,24 @@ gs_app_get_progress (GsApp *app)
 }
 
 /**
+ * gs_app_get_allow_cancel:
+ * @app: a #GsApp
+ *
+ * Gets whether the app's installation or upgrade can be cancelled.
+ *
+ * Returns: TRUE if cancellation is possible, FALSE otherwise.
+ *
+ * Since: 3.26
+ **/
+gboolean
+gs_app_get_allow_cancel (GsApp *app)
+{
+       GsAppPrivate *priv = gs_app_get_instance_private (app);
+       g_return_val_if_fail (GS_IS_APP (app), FALSE);
+       return priv->allow_cancel;
+}
+
+/**
  * gs_app_set_state_recover:
  * @app: a #GsApp
  *
@@ -995,6 +1015,29 @@ gs_app_set_progress (GsApp *app, guint percentage)
 }
 
 /**
+ * gs_app_set_allow_cancel:
+ * @app: a #GsApp
+ * @boolean: if the installation or upgrade can be cancelled or not
+ *
+ * This sets a flag indicating whether the operation can be cancelled or not.
+ * This is used by the UI to set the "Cancel" button insensitive as
+ * appropriate.
+ *
+ * Since: 3.26
+ **/
+void
+gs_app_set_allow_cancel (GsApp *app, gboolean allow_cancel)
+{
+       GsAppPrivate *priv = gs_app_get_instance_private (app);
+       g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->mutex);
+       g_return_if_fail (GS_IS_APP (app));
+       if (priv->allow_cancel == allow_cancel)
+               return;
+       priv->allow_cancel = allow_cancel;
+       gs_app_queue_notify (app, "allow-cancel");
+}
+
+/**
  * gs_app_set_state:
  * @app: a #GsApp
  * @state: a #AsAppState, e.g. AS_APP_STATE_UPDATABLE_LIVE
@@ -3727,6 +3770,9 @@ gs_app_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *
        case PROP_PROGRESS:
                g_value_set_uint (value, priv->progress);
                break;
+       case PROP_CAN_CANCEL_INSTALLATION:
+               g_value_set_boolean (value, priv->allow_cancel);
+               break;
        case PROP_INSTALL_DATE:
                g_value_set_uint64 (value, priv->install_date);
                break;
@@ -3779,6 +3825,9 @@ gs_app_set_property (GObject *object, guint prop_id, const GValue *value, GParam
        case PROP_PROGRESS:
                priv->progress = g_value_get_uint (value);
                break;
+       case PROP_CAN_CANCEL_INSTALLATION:
+               priv->allow_cancel = g_value_get_boolean (value);
+               break;
        case PROP_INSTALL_DATE:
                gs_app_set_install_date (app, g_value_get_uint64 (value));
                break;
@@ -3945,6 +3994,13 @@ gs_app_class_init (GsAppClass *klass)
        g_object_class_install_property (object_class, PROP_PROGRESS, pspec);
 
        /**
+        * GsApp:allow-cancel:
+        */
+       pspec = g_param_spec_boolean ("allow-cancel", NULL, NULL, TRUE,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+       g_object_class_install_property (object_class, PROP_CAN_CANCEL_INSTALLATION, pspec);
+
+       /**
         * GsApp:install-date:
         */
        pspec = g_param_spec_uint64 ("install-date", NULL, NULL,
@@ -3993,6 +4049,7 @@ gs_app_init (GsApp *app)
                                            g_str_equal,
                                            g_free,
                                            g_free);
+       priv->allow_cancel = TRUE;
        g_mutex_init (&priv->mutex);
 }
 
diff --git a/lib/gs-app.h b/lib/gs-app.h
index a217be0..508d6a2 100644
--- a/lib/gs-app.h
+++ b/lib/gs-app.h
@@ -135,6 +135,9 @@ void                 gs_app_set_state_recover       (GsApp          *app);
 guint           gs_app_get_progress            (GsApp          *app);
 void            gs_app_set_progress            (GsApp          *app,
                                                 guint           percentage);
+gboolean       gs_app_get_allow_cancel         (GsApp  *app);
+void           gs_app_set_allow_cancel          (GsApp *app,
+                                                 gboolean      can_cancel);
 const gchar    *gs_app_get_unique_id           (GsApp          *app);
 const gchar    *gs_app_get_branch              (GsApp          *app);
 void            gs_app_set_branch              (GsApp          *app,
diff --git a/plugins/packagekit/gs-plugin-packagekit.c b/plugins/packagekit/gs-plugin-packagekit.c
index f91112a..d1e655d 100644
--- a/plugins/packagekit/gs-plugin-packagekit.c
+++ b/plugins/packagekit/gs-plugin-packagekit.c
@@ -94,6 +94,12 @@ gs_plugin_packagekit_progress_cb (PkProgress *progress,
                if (data->app != NULL && percentage >= 0 && percentage <= 100)
                        gs_app_set_progress (data->app, (guint) percentage);
        }
+
+       /* Only go from TRUE to FALSE - it doesn't make sense for a package
+        * install to become uncancellable later on */
+       if (gs_app_get_allow_cancel (data->app))
+               gs_app_set_allow_cancel (data->app,
+                                        pk_progress_get_allow_cancel (progress));
 }
 
 static gboolean
@@ -312,6 +318,10 @@ gs_plugin_app_install (GsPlugin *plugin,
                /* state is known */
                gs_app_set_state (app, AS_APP_STATE_INSTALLED);
 
+               /* if we remove the app again later, we should be able to
+                * cancel the installation if we'd never installed it */
+               gs_app_set_allow_cancel (app, TRUE);
+
                /* no longer valid */
                gs_app_clear_source_ids (app);
                return TRUE;
diff --git a/src/gs-app-row.c b/src/gs-app-row.c
index 8fa5808..85e8dc0 100644
--- a/src/gs-app-row.c
+++ b/src/gs-app-row.c
@@ -276,7 +276,8 @@ gs_app_row_refresh_button (GsAppRow *app_row, gboolean missing_search_result)
                        gtk_widget_set_sensitive (priv->button, FALSE);
                        break;
                default:
-                       gtk_widget_set_sensitive (priv->button, TRUE);
+                       gtk_widget_set_sensitive (priv->button,
+                                                 gs_app_get_allow_cancel (priv->app));
                        break;
                }
        }
@@ -570,6 +571,9 @@ gs_app_row_set_app (GsAppRow *app_row, GsApp *app)
        g_signal_connect_object (priv->app, "notify::progress",
                                 G_CALLBACK (gs_app_row_notify_props_changed_cb),
                                 app_row, 0);
+       g_signal_connect_object (priv->app, "notify::allow-cancel",
+                                G_CALLBACK (gs_app_row_notify_props_changed_cb),
+                                app_row, 0);
        gs_app_row_refresh (app_row);
 }
 
diff --git a/src/gs-details-page.c b/src/gs-details-page.c
index 76a04cb..25a26e5 100644
--- a/src/gs-details-page.c
+++ b/src/gs-details-page.c
@@ -412,8 +412,14 @@ gs_details_page_refresh_progress (GsDetailsPage *self)
        switch (state) {
        case AS_APP_STATE_INSTALLING:
                gtk_widget_set_visible (self->button_cancel, TRUE);
+               /* If the app is installing, the user can only cancel it if
+                * 1) They haven't already, and
+                * 2) the plugin hasn't said that they can't, for example if a
+                *    package manager has already gone 'too far'
+                */
                gtk_widget_set_sensitive (self->button_cancel,
-                                         !g_cancellable_is_cancelled (self->cancellable));
+                                         !g_cancellable_is_cancelled (self->cancellable) &&
+                                          gs_app_get_allow_cancel (self->app));
                break;
        default:
                gtk_widget_set_visible (self->button_cancel, FALSE);
@@ -499,6 +505,25 @@ gs_details_page_progress_changed_cb (GsApp *app,
 }
 
 static gboolean
+gs_details_page_allow_cancel_changed_idle (gpointer user_data)
+{
+       GsDetailsPage *self = GS_DETAILS_PAGE (user_data);
+       gtk_widget_set_sensitive (self->button_cancel,
+                                 gs_app_get_allow_cancel (self->app));
+       g_object_unref (self);
+       return G_SOURCE_REMOVE;
+}
+
+static void
+gs_details_page_allow_cancel_changed_cb (GsApp *app,
+                                                    GParamSpec *pspec,
+                                                    GsDetailsPage *self)
+{
+       g_idle_add (gs_details_page_allow_cancel_changed_idle,
+                   g_object_ref (self));
+}
+
+static gboolean
 gs_details_page_switch_to_idle (gpointer user_data)
 {
        GsDetailsPage *self = GS_DETAILS_PAGE (user_data);
@@ -1492,6 +1517,8 @@ set_app (GsDetailsPage *self, GsApp *app)
        if (self->app != NULL) {
                g_signal_handlers_disconnect_by_func (self->app, gs_details_page_notify_state_changed_cb, 
self);
                g_signal_handlers_disconnect_by_func (self->app, gs_details_page_progress_changed_cb, self);
+               g_signal_handlers_disconnect_by_func (self->app, gs_details_page_allow_cancel_changed_cb,
+                                                     self);
        }
 
        /* save app */
@@ -1514,6 +1541,9 @@ set_app (GsDetailsPage *self, GsApp *app)
        g_signal_connect_object (self->app, "notify::progress",
                                 G_CALLBACK (gs_details_page_progress_changed_cb),
                                 self, 0);
+       g_signal_connect_object (self->app, "notify::allow-cancel",
+                                G_CALLBACK (gs_details_page_allow_cancel_changed_cb),
+                                self, 0);
 
        /* print what we've got */
        tmp = gs_app_to_string (self->app);
@@ -1699,6 +1729,8 @@ gs_details_page_set_app (GsDetailsPage *self, GsApp *app)
                g_signal_handlers_disconnect_by_func (self->settings,
                                                      settings_changed_cb,
                                                      self);
+               g_signal_handlers_disconnect_by_func (self->app, gs_details_page_allow_cancel_changed_cb,
+                                                     self);
        }
        /* save app */
        g_set_object (&self->app, app);
@@ -1718,6 +1750,9 @@ gs_details_page_set_app (GsDetailsPage *self, GsApp *app)
        g_signal_connect_object (self->app, "notify::progress",
                                 G_CALLBACK (gs_details_page_progress_changed_cb),
                                 self, 0);
+       g_signal_connect_object (self->app, "notify::allow-cancel",
+                                G_CALLBACK (gs_details_page_allow_cancel_changed_cb),
+                                self, 0);
        gs_details_page_load (self);
 
        /* change widgets */


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