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.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/src/gs-upgrade-banner.c b/src/gs-upgrade-banner.c
new file mode 100644
index 0000000..8a5e6a2
--- /dev/null
+++ b/src/gs-upgrade-banner.c
@@ -0,0 +1,374 @@
+/* -*- 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_info;
+ GtkWidget *box_upgrades_download;
+ GtkWidget *box_upgrades_downloading;
+ GtkWidget *box_upgrades_install;
+ GtkWidget *button_upgrades_download;
+ GtkWidget *button_upgrades_install;
+ GtkWidget *button_upgrades_cancel;
+ GtkWidget *label_upgrades_summary;
+ GtkWidget *label_upgrades_title;
+ GtkWidget *label_download_info;
+ GtkWidget *label_upgrades_downloading;
+ GtkWidget *progressbar;
+ guint progress_pulse_id;
+ GtkCssProvider *banner_provider; /* (owned) (nullable) */
+} GsUpgradeBannerPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsUpgradeBanner, gs_upgrade_banner, ADW_TYPE_BIN)
+
+enum {
+ SIGNAL_DOWNLOAD_CLICKED,
+ SIGNAL_INSTALL_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, *summary, *version;
+ g_autofree gchar *str = NULL;
+ guint percentage;
+ GsSizeType size_download_type;
+ guint64 size_download_bytes;
+
+ if (priv->app == NULL)
+ return;
+
+ version = gs_app_get_version (priv->app);
+
+ if (version != NULL && *version != '\0') {
+ /* TRANSLATORS: This is the text displayed when a distro
+ * upgrade is available. The first %s is the distro name
+ * and the 2nd %s is the version, e.g. "Fedora 35 Available" */
+ str = g_strdup_printf (_("%s %s Available"), gs_app_get_name (priv->app), version);
+ } else {
+ /* TRANSLATORS: This is the text displayed when a distro
+ * upgrade is available. The %s is the distro name,
+ * e.g. "GNOME OS Available" */
+ str = g_strdup_printf (_("%s Available"), gs_app_get_name (priv->app));
+ }
+ gtk_label_set_text (GTK_LABEL (priv->label_upgrades_title), str);
+
+ /* 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 GS_APP_STATE_AVAILABLE:
+ case GS_APP_STATE_QUEUED_FOR_INSTALL:
+ gtk_widget_show (priv->box_upgrades_download);
+ gtk_widget_hide (priv->box_upgrades_downloading);
+ gtk_widget_hide (priv->box_upgrades_install);
+ break;
+ case GS_APP_STATE_INSTALLING:
+ gtk_widget_hide (priv->box_upgrades_download);
+ gtk_widget_show (priv->box_upgrades_downloading);
+ gtk_widget_hide (priv->box_upgrades_install);
+ break;
+ case GS_APP_STATE_UPDATABLE:
+ gtk_widget_hide (priv->box_upgrades_download);
+ gtk_widget_hide (priv->box_upgrades_downloading);
+ gtk_widget_show (priv->box_upgrades_install);
+ break;
+ default:
+ g_critical ("Unexpected app state ‘%s’ of app ‘%s’",
+ gs_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 (GTK_WIDGET (self),
+ (gs_app_get_state (priv->app) != GS_APP_STATE_UNKNOWN));
+
+ /* Refresh the summary if we got anything better than the default blurb */
+ summary = gs_app_get_summary (priv->app);
+ if (summary != NULL)
+ gtk_label_set_text (GTK_LABEL (priv->label_upgrades_summary), summary);
+
+ uri = gs_app_get_url (priv->app, AS_URL_KIND_HOMEPAGE);
+ size_download_type = gs_app_get_size_download (priv->app, &size_download_bytes);
+
+ if (uri != NULL) {
+ g_autofree gchar *link = NULL;
+ link = g_markup_printf_escaped ("<a href=\"%s\">%s</a>", uri, _("Learn about the new version"));
+ gtk_label_set_markup (GTK_LABEL (priv->label_download_info), link);
+ gtk_widget_show (priv->label_download_info);
+ } else if (size_download_type == GS_SIZE_TYPE_VALID &&
+ size_download_bytes > 0) {
+ g_autofree gchar *tmp = NULL;
+ g_clear_pointer (&str, g_free);
+ tmp = g_format_size (size_download_bytes);
+ /* Translators: the '%s' is replaced with the download size, forming text like "2 GB download" */
+ str = g_strdup_printf ("%s download", tmp);
+ gtk_label_set_text (GTK_LABEL (priv->label_download_info), str);
+ gtk_widget_show (priv->label_download_info);
+ } else {
+ gtk_widget_hide (priv->label_download_info);
+ }
+
+ /* do a fill bar for the current progress */
+ switch (gs_app_get_state (priv->app)) {
+ case GS_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_label_set_text (GTK_LABEL (priv->label_upgrades_downloading), _("Downloading…"));
+ break;
+ } else if (percentage <= 100) {
+ stop_progress_pulsing (self);
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progressbar),
+ (gdouble) percentage / 100.f);
+ g_clear_pointer (&str, g_free);
+
+ if (size_download_type == GS_SIZE_TYPE_VALID) {
+ g_autofree gchar *tmp = NULL;
+ g_autofree gchar *downloaded_tmp = NULL;
+ guint64 downloaded;
+
+ downloaded = size_download_bytes * percentage / 100.0;
+ downloaded_tmp = g_format_size (downloaded);
+ tmp = g_format_size (size_download_bytes);
+ /* Translators: the first '%s' is replaced with the downloaded size, the second '%s'
+ with the total download size, forming text like "135 MB of 2 GB downloaded" */
+ str = g_strdup_printf (_("%s of %s downloaded"), downloaded_tmp, tmp);
+ } else {
+ /* Translators: the '%u' is replaced with the actual percentage being already
+ downloaded, forming text like "13% downloaded" */
+ str = g_strdup_printf (_("%u%% downloaded"), percentage);
+ }
+ gtk_label_set_text (GTK_LABEL (priv->label_upgrades_downloading), str);
+ break;
+ }
+ break;
+ default:
+ 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
+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_autofree gchar *modified_css = NULL;
+
+ 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");
+ modified_css = gs_utils_set_key_colors_in_css (css, app);
+ gs_utils_widget_set_css (priv->box_upgrades_info, &priv->banner_provider, "upgrade-banner-custom", modified_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);
+
+ 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);
+ g_clear_object (&priv->banner_provider);
+
+ G_OBJECT_CLASS (gs_upgrade_banner_parent_class)->dispose (object);
+}
+
+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_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;
+
+ 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);
+
+ 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_info);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, box_upgrades_download);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, box_upgrades_downloading);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, box_upgrades_install);
+ 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, 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, label_download_info);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, label_upgrades_downloading);
+ gtk_widget_class_bind_template_child_private (widget_class, GsUpgradeBanner, progressbar);
+}
+
+GtkWidget *
+gs_upgrade_banner_new (void)
+{
+ GsUpgradeBanner *self;
+ self = g_object_new (GS_TYPE_UPGRADE_BANNER,
+ "vexpand", FALSE,
+ NULL);
+ return GTK_WIDGET (self);
+}