summaryrefslogtreecommitdiffstats
path: root/src/gs-upgrade-banner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gs-upgrade-banner.c')
-rw-r--r--src/gs-upgrade-banner.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/src/gs-upgrade-banner.c b/src/gs-upgrade-banner.c
new file mode 100644
index 0000000..b38a353
--- /dev/null
+++ b/src/gs-upgrade-banner.c
@@ -0,0 +1,411 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2016 Kalev Lember <klember@redhat.com>
+ * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <stdlib.h>
+
+#include "gs-upgrade-banner.h"
+#include "gs-common.h"
+
+typedef struct
+{
+ GsApp *app;
+
+ GtkWidget *box_upgrades;
+ GtkWidget *button_upgrades_download;
+ GtkWidget *button_upgrades_install;
+ GtkWidget *button_upgrades_help;
+ GtkWidget *button_upgrades_cancel;
+ GtkWidget *label_upgrades_summary;
+ GtkWidget *label_upgrades_title;
+ GtkWidget *label_upgrades_warning;
+ GtkWidget *progressbar;
+ guint progress_pulse_id;
+ GtkCssProvider *banner_provider; /* (owned) (nullable) */
+} GsUpgradeBannerPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsUpgradeBanner, gs_upgrade_banner, GTK_TYPE_BIN)
+
+enum {
+ SIGNAL_DOWNLOAD_CLICKED,
+ SIGNAL_INSTALL_CLICKED,
+ SIGNAL_HELP_CLICKED,
+ SIGNAL_CANCEL_CLICKED,
+ SIGNAL_LAST
+};
+
+static guint signals [SIGNAL_LAST] = { 0 };
+
+static gboolean
+_pulse_cb (gpointer user_data)
+{
+ GsUpgradeBanner *self = GS_UPGRADE_BANNER (user_data);
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+
+ gtk_progress_bar_pulse (GTK_PROGRESS_BAR (priv->progressbar));
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+stop_progress_pulsing (GsUpgradeBanner *self)
+{
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+
+ if (priv->progress_pulse_id != 0) {
+ g_source_remove (priv->progress_pulse_id);
+ priv->progress_pulse_id = 0;
+ }
+}
+
+static void
+gs_upgrade_banner_refresh (GsUpgradeBanner *self)
+{
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+ const gchar *uri;
+ g_autofree gchar *name_bold = NULL;
+ g_autofree gchar *version_bold = NULL;
+ g_autofree gchar *str = NULL;
+ guint percentage;
+
+ if (priv->app == NULL)
+ return;
+
+ /* embolden */
+ name_bold = g_strdup_printf ("<b>%s</b>", gs_app_get_name (priv->app));
+ version_bold = g_strdup_printf ("<b>%s</b>", gs_app_get_version (priv->app));
+
+ /* Distributions that need to reboot to deploy the upgrade show the "Install" button */
+ if (gs_app_has_quirk (priv->app, GS_APP_QUIRK_NEEDS_REBOOT)) {
+ gtk_button_set_label (GTK_BUTTON (priv->button_upgrades_install),
+ _("_Install"));
+ gtk_label_set_text (GTK_LABEL (priv->label_upgrades_warning),
+ _("It is recommended that you back up your "
+ "data and files before upgrading."));
+ } else {
+ gtk_button_set_label (GTK_BUTTON (priv->button_upgrades_install),
+ _("_Restart Now"));
+ gtk_label_set_text (GTK_LABEL (priv->label_upgrades_warning),
+ _("Updates will be applied when the "
+ "computer is restarted."));
+ }
+
+ /* Refresh the title. Normally a distro upgrade state goes from
+ *
+ * AVAILABLE (available to download) to
+ * INSTALLING (downloading packages for later installation) to
+ * UPDATABLE (packages are downloaded and upgrade is ready to go)
+ */
+ switch (gs_app_get_state (priv->app)) {
+ case AS_APP_STATE_AVAILABLE:
+ /* TRANSLATORS: This is the text displayed when a distro
+ * upgrade is available. First %s is the distro name and the
+ * 2nd %s is the version, e.g. "Fedora 23 Now Available" */
+ str = g_strdup_printf (_("%s %s Now Available"),
+ name_bold, version_bold);
+ gtk_label_set_markup (GTK_LABEL (priv->label_upgrades_title), str);
+ gtk_widget_set_visible (priv->label_upgrades_warning, FALSE);
+ gtk_widget_set_visible (priv->button_upgrades_cancel, FALSE);
+ break;
+ case AS_APP_STATE_QUEUED_FOR_INSTALL:
+ /* TRANSLATORS: This is the text displayed while waiting to
+ * download a distro upgrade. First %s is the distro name and
+ * the 2nd %s is the version, e.g. "Waiting to Download Fedora 23" */
+ str = g_strdup_printf (_("Waiting to Download %s %s"),
+ name_bold, version_bold);
+ gtk_label_set_markup (GTK_LABEL (priv->label_upgrades_title), str);
+ gtk_widget_set_visible (priv->label_upgrades_warning, FALSE);
+ gtk_widget_set_visible (priv->button_upgrades_cancel, TRUE);
+ break;
+ case AS_APP_STATE_INSTALLING:
+ /* TRANSLATORS: This is the text displayed while downloading a
+ * distro upgrade. First %s is the distro name and the 2nd %s
+ * is the version, e.g. "Downloading Fedora 23" */
+ str = g_strdup_printf (_("Downloading %s %s"),
+ name_bold, version_bold);
+ gtk_label_set_markup (GTK_LABEL (priv->label_upgrades_title), str);
+ gtk_widget_set_visible (priv->label_upgrades_warning, FALSE);
+ gtk_widget_set_visible (priv->button_upgrades_cancel, TRUE);
+ break;
+ case AS_APP_STATE_UPDATABLE:
+ /* TRANSLATORS: This is the text displayed when a distro
+ * upgrade has been downloaded and is ready to be installed.
+ * First %s is the distro name and the 2nd %s is the version,
+ * e.g. "Fedora 23 Ready to be Installed" */
+ str = g_strdup_printf (_("%s %s Ready to be Installed"),
+ name_bold, version_bold);
+ gtk_label_set_markup (GTK_LABEL (priv->label_upgrades_title), str);
+ gtk_widget_set_visible (priv->label_upgrades_warning, TRUE);
+ gtk_widget_set_visible (priv->button_upgrades_cancel, FALSE);
+ break;
+ default:
+ g_critical ("Unexpected app state ‘%s’ of app ‘%s’",
+ as_app_state_to_string (gs_app_get_state (priv->app)),
+ gs_app_get_unique_id (priv->app));
+ break;
+ }
+
+ /* Hide the upgrade box until the app state is known. */
+ gtk_widget_set_visible (priv->box_upgrades,
+ (gs_app_get_state (priv->app) != AS_APP_STATE_UNKNOWN));
+
+ /* Refresh the summary if we got anything better than the default blurb */
+ if (gs_app_get_summary (priv->app) != NULL)
+ gtk_label_set_text (GTK_LABEL (priv->label_upgrades_summary),
+ gs_app_get_summary (priv->app));
+
+ /* Show the right buttons for the current state */
+ switch (gs_app_get_state (priv->app)) {
+ case AS_APP_STATE_AVAILABLE:
+ gtk_widget_show (priv->button_upgrades_download);
+ gtk_widget_hide (priv->button_upgrades_install);
+ break;
+ case AS_APP_STATE_QUEUED_FOR_INSTALL:
+ case AS_APP_STATE_INSTALLING:
+ gtk_widget_hide (priv->button_upgrades_download);
+ gtk_widget_hide (priv->button_upgrades_install);
+ break;
+ case AS_APP_STATE_UPDATABLE:
+ gtk_widget_hide (priv->button_upgrades_download);
+ gtk_widget_show (priv->button_upgrades_install);
+ break;
+ default:
+ g_critical ("Unexpected app state ‘%s’ of app ‘%s’",
+ as_app_state_to_string (gs_app_get_state (priv->app)),
+ gs_app_get_unique_id (priv->app));
+ break;
+ }
+
+ /* only show help when we have a URL */
+ uri = gs_app_get_url (priv->app, AS_URL_KIND_HOMEPAGE);
+ gtk_widget_set_visible (priv->button_upgrades_help, uri != NULL);
+
+ /* do a fill bar for the current progress */
+ switch (gs_app_get_state (priv->app)) {
+ case AS_APP_STATE_INSTALLING:
+ percentage = gs_app_get_progress (priv->app);
+ if (percentage == GS_APP_PROGRESS_UNKNOWN) {
+ if (priv->progress_pulse_id == 0)
+ priv->progress_pulse_id = g_timeout_add (50, _pulse_cb, self);
+
+ gtk_widget_set_visible (priv->progressbar, TRUE);
+ break;
+ } else if (percentage <= 100) {
+ stop_progress_pulsing (self);
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progressbar),
+ (gdouble) percentage / 100.f);
+ gtk_widget_set_visible (priv->progressbar, TRUE);
+ break;
+ }
+ break;
+ default:
+ gtk_widget_hide (priv->progressbar);
+ stop_progress_pulsing (self);
+ break;
+ }
+}
+
+static gboolean
+app_refresh_idle (gpointer user_data)
+{
+ GsUpgradeBanner *self = GS_UPGRADE_BANNER (user_data);
+
+ gs_upgrade_banner_refresh (self);
+
+ g_object_unref (self);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+app_state_changed (GsApp *app, GParamSpec *pspec, GsUpgradeBanner *self)
+{
+ g_idle_add (app_refresh_idle, g_object_ref (self));
+}
+
+static void
+app_progress_changed (GsApp *app, GParamSpec *pspec, GsUpgradeBanner *self)
+{
+ g_idle_add (app_refresh_idle, g_object_ref (self));
+}
+
+static void
+download_button_cb (GtkWidget *widget, GsUpgradeBanner *self)
+{
+ g_signal_emit (self, signals[SIGNAL_DOWNLOAD_CLICKED], 0);
+}
+
+static void
+install_button_cb (GtkWidget *widget, GsUpgradeBanner *self)
+{
+ g_signal_emit (self, signals[SIGNAL_INSTALL_CLICKED], 0);
+}
+
+static void
+learn_more_button_cb (GtkWidget *widget, GsUpgradeBanner *self)
+{
+ g_signal_emit (self, signals[SIGNAL_HELP_CLICKED], 0);
+}
+
+static void
+cancel_button_cb (GtkWidget *widget, GsUpgradeBanner *self)
+{
+ g_signal_emit (self, signals[SIGNAL_CANCEL_CLICKED], 0);
+}
+
+void
+gs_upgrade_banner_set_app (GsUpgradeBanner *self, GsApp *app)
+{
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+ const gchar *css;
+
+ g_return_if_fail (GS_IS_UPGRADE_BANNER (self));
+ g_return_if_fail (GS_IS_APP (app) || app == NULL);
+
+ if (priv->app) {
+ g_signal_handlers_disconnect_by_func (priv->app, app_state_changed, self);
+ g_signal_handlers_disconnect_by_func (priv->app, app_progress_changed, self);
+ }
+
+ g_set_object (&priv->app, app);
+ if (!app)
+ return;
+
+ g_signal_connect (priv->app, "notify::state",
+ G_CALLBACK (app_state_changed), self);
+ g_signal_connect (priv->app, "notify::progress",
+ G_CALLBACK (app_progress_changed), self);
+
+ /* perhaps set custom css */
+ css = gs_app_get_metadata_item (app, "GnomeSoftware::UpgradeBanner-css");
+ gs_utils_widget_set_css (priv->box_upgrades, &priv->banner_provider, "upgrade-banner-custom", css);
+
+ gs_upgrade_banner_refresh (self);
+}
+
+GsApp *
+gs_upgrade_banner_get_app (GsUpgradeBanner *self)
+{
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+
+ g_return_val_if_fail (GS_IS_UPGRADE_BANNER (self), NULL);
+
+ return priv->app;
+}
+
+static void
+gs_upgrade_banner_dispose (GObject *object)
+{
+ GsUpgradeBanner *self = GS_UPGRADE_BANNER (object);
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+
+ g_clear_object (&priv->banner_provider);
+
+ G_OBJECT_CLASS (gs_upgrade_banner_parent_class)->dispose (object);
+}
+
+static void
+gs_upgrade_banner_destroy (GtkWidget *widget)
+{
+ GsUpgradeBanner *self = GS_UPGRADE_BANNER (widget);
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+
+ stop_progress_pulsing (self);
+
+ if (priv->app) {
+ g_signal_handlers_disconnect_by_func (priv->app, app_state_changed, self);
+ g_signal_handlers_disconnect_by_func (priv->app, app_progress_changed, self);
+ }
+
+ g_clear_object (&priv->app);
+
+ GTK_WIDGET_CLASS (gs_upgrade_banner_parent_class)->destroy (widget);
+}
+
+static void
+gs_upgrade_banner_init (GsUpgradeBanner *self)
+{
+ GsUpgradeBannerPrivate *priv = gs_upgrade_banner_get_instance_private (self);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect (priv->button_upgrades_download, "clicked",
+ G_CALLBACK (download_button_cb),
+ self);
+ g_signal_connect (priv->button_upgrades_install, "clicked",
+ G_CALLBACK (install_button_cb),
+ self);
+ g_signal_connect (priv->button_upgrades_help, "clicked",
+ G_CALLBACK (learn_more_button_cb),
+ self);
+ g_signal_connect (priv->button_upgrades_cancel, "clicked",
+ G_CALLBACK (cancel_button_cb),
+ self);
+}
+
+static void
+gs_upgrade_banner_class_init (GsUpgradeBannerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gs_upgrade_banner_dispose;
+ widget_class->destroy = gs_upgrade_banner_destroy;
+
+ signals [SIGNAL_DOWNLOAD_CLICKED] =
+ g_signal_new ("download-clicked",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsUpgradeBannerClass, download_clicked),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals [SIGNAL_INSTALL_CLICKED] =
+ g_signal_new ("install-clicked",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsUpgradeBannerClass, install_clicked),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals [SIGNAL_CANCEL_CLICKED] =
+ g_signal_new ("cancel-clicked",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsUpgradeBannerClass, cancel_clicked),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals [SIGNAL_HELP_CLICKED] =
+ g_signal_new ("help-clicked",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsUpgradeBannerClass, help_clicked),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-upgrade-banner.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, box_upgrades);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, button_upgrades_download);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, button_upgrades_install);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, button_upgrades_cancel);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, button_upgrades_help);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, label_upgrades_summary);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, label_upgrades_title);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, progressbar);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, label_upgrades_warning);
+}
+
+GtkWidget *
+gs_upgrade_banner_new (void)
+{
+ GsUpgradeBanner *self;
+ self = g_object_new (GS_TYPE_UPGRADE_BANNER,
+ "vexpand", FALSE,
+ NULL);
+ return GTK_WIDGET (self);
+}