From 6f0f7d1b40a8fa8d46a2d6f4317600001cdbbb18 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:57:27 +0200 Subject: Adding upstream version 43.5. Signed-off-by: Daniel Baumann --- src/gs-updates-section.c | 733 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 733 insertions(+) create mode 100644 src/gs-updates-section.c (limited to 'src/gs-updates-section.c') diff --git a/src/gs-updates-section.c b/src/gs-updates-section.c new file mode 100644 index 0000000..68d818a --- /dev/null +++ b/src/gs-updates-section.c @@ -0,0 +1,733 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2013-2017 Richard Hughes + * Copyright (C) 2014-2018 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "config.h" + +#include +#include + +#include "gs-app-list-private.h" +#include "gs-app-row.h" +#include "gs-page.h" +#include "gs-common.h" +#include "gs-progress-button.h" +#include "gs-update-dialog.h" +#include "gs-updates-section.h" +#include "gs-utils.h" + +struct _GsUpdatesSection +{ + GtkBox parent_instance; + + GtkWidget *button_cancel; + GtkWidget *button_download; + GtkWidget *button_stack; + GtkWidget *button_update; + GtkWidget *description; + GtkWidget *listbox; + GtkWidget *listbox_box; + GtkWidget *section_header; + GtkWidget *title; + + GsAppList *list; + GsUpdatesSectionKind kind; + GCancellable *cancellable; + GsPage *page; /* (transfer none) */ + GsPluginLoader *plugin_loader; + GtkSizeGroup *sizegroup_name; + GtkSizeGroup *sizegroup_button_label; + GtkSizeGroup *sizegroup_button_image; + GtkSizeGroup *sizegroup_header; + gboolean is_narrow; +}; + +G_DEFINE_TYPE (GsUpdatesSection, gs_updates_section, GTK_TYPE_BOX) + +typedef enum { + PROP_IS_NARROW = 1, +} GsUpdatesSectionProperty; + +static GParamSpec *obj_props[PROP_IS_NARROW + 1] = { NULL, }; + +GsAppList * +gs_updates_section_get_list (GsUpdatesSection *self) +{ + return self->list; +} + +static gboolean +_listbox_keynav_failed_cb (GsUpdatesSection *self, GtkDirectionType direction, GtkListBox *listbox) +{ + GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (listbox)); + + if (!root) + return FALSE; + + if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN) + return FALSE; + + return gtk_widget_child_focus (GTK_WIDGET (root), direction == GTK_DIR_UP ? GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD); +} + +static void +_app_row_button_clicked_cb (GsAppRow *app_row, GsUpdatesSection *self) +{ + GsApp *app = gs_app_row_get_app (app_row); + if (gs_app_get_state (app) != GS_APP_STATE_UPDATABLE_LIVE) + return; + gs_page_update_app (GS_PAGE (self->page), app, gs_app_get_cancellable (app)); +} + +static void +_row_unrevealed_cb (GObject *row, GParamSpec *pspec, gpointer data) +{ + GtkWidget *widget; + GsUpdatesSection *self; + + widget = gtk_widget_get_parent (GTK_WIDGET (row)); + if (widget == NULL) + return; + + widget = gtk_widget_get_ancestor (GTK_WIDGET (row), GS_TYPE_UPDATES_SECTION); + g_return_if_fail (GS_IS_UPDATES_SECTION (widget)); + self = GS_UPDATES_SECTION (widget); + + gs_app_list_remove (self->list, gs_app_row_get_app (GS_APP_ROW (row))); + + gtk_list_box_remove (GTK_LIST_BOX (self->listbox), GTK_WIDGET (row)); + + if (!gs_app_list_length (self->list)) + gtk_widget_hide (widget); +} + +static void +_unreveal_row (GsAppRow *app_row) +{ + g_signal_connect (app_row, "unrevealed", + G_CALLBACK (_row_unrevealed_cb), NULL); + gs_app_row_unreveal (app_row); +} + +static void +_app_state_notify_cb (GsApp *app, GParamSpec *pspec, gpointer user_data) +{ + if (gs_app_get_state (app) == GS_APP_STATE_INSTALLED) { + GsAppRow *app_row = GS_APP_ROW (user_data); + _unreveal_row (app_row); + } +} + +void +gs_updates_section_add_app (GsUpdatesSection *self, GsApp *app) +{ + GtkWidget *app_row; + app_row = gs_app_row_new (app); + gs_app_row_set_show_description (GS_APP_ROW (app_row), FALSE); + gs_app_row_set_show_update (GS_APP_ROW (app_row), TRUE); + gs_app_row_set_show_buttons (GS_APP_ROW (app_row), TRUE); + g_signal_connect (app_row, "button-clicked", + G_CALLBACK (_app_row_button_clicked_cb), + self); + gtk_list_box_append (GTK_LIST_BOX (self->listbox), app_row); + gs_app_list_add (self->list, app); + + gs_app_row_set_size_groups (GS_APP_ROW (app_row), + self->sizegroup_name, + self->sizegroup_button_label, + self->sizegroup_button_image); + g_signal_connect_object (app, "notify::state", + G_CALLBACK (_app_state_notify_cb), + app_row, 0); + g_object_bind_property (G_OBJECT (self), "is-narrow", + app_row, "is-narrow", + G_BINDING_SYNC_CREATE); + gtk_widget_show (GTK_WIDGET (self)); +} + +void +gs_updates_section_remove_all (GsUpdatesSection *self) +{ + GtkWidget *child; + while ((child = gtk_widget_get_first_child (self->listbox)) != NULL) + gtk_list_box_remove (GTK_LIST_BOX (self->listbox), child); + gs_app_list_remove_all (self->list); + gtk_widget_hide (GTK_WIDGET (self)); +} + +typedef struct { + GsUpdatesSection *self; + gboolean do_reboot; + gboolean do_reboot_notification; +} GsUpdatesSectionUpdateHelper; + +static gchar * +_get_app_sort_key (GsApp *app) +{ + GString *key; + g_autofree gchar *sort_name = NULL; + + key = g_string_sized_new (64); + + /* sort apps by kind */ + switch (gs_app_get_kind (app)) { + case AS_COMPONENT_KIND_DESKTOP_APP: + g_string_append (key, "2:"); + break; + case AS_COMPONENT_KIND_WEB_APP: + g_string_append (key, "3:"); + break; + case AS_COMPONENT_KIND_RUNTIME: + g_string_append (key, "4:"); + break; + case AS_COMPONENT_KIND_ADDON: + g_string_append (key, "5:"); + break; + case AS_COMPONENT_KIND_CODEC: + g_string_append (key, "6:"); + break; + case AS_COMPONENT_KIND_FONT: + g_string_append (key, "6:"); + break; + case AS_COMPONENT_KIND_INPUT_METHOD: + g_string_append (key, "7:"); + break; + default: + if (gs_app_get_special_kind (app) == GS_APP_SPECIAL_KIND_OS_UPDATE) + g_string_append (key, "1:"); + else + g_string_append (key, "8:"); + break; + } + + /* finally, sort by short name */ + if (gs_app_get_name (app) != NULL) { + sort_name = gs_utils_sort_key (gs_app_get_name (app)); + g_string_append (key, sort_name); + } + + return g_string_free (key, FALSE); +} + +static gint +_list_sort_func (GtkListBoxRow *a, GtkListBoxRow *b, gpointer user_data) +{ + GsApp *a1 = gs_app_row_get_app (GS_APP_ROW (a)); + GsApp *a2 = gs_app_row_get_app (GS_APP_ROW (b)); + g_autofree gchar *key1 = _get_app_sort_key (a1); + g_autofree gchar *key2 = _get_app_sort_key (a2); + + /* compare the keys according to the algorithm above */ + return g_strcmp0 (key1, key2); +} + +static void +_update_helper_free (GsUpdatesSectionUpdateHelper *helper) +{ + g_object_unref (helper->self); + g_free (helper); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsUpdatesSectionUpdateHelper, _update_helper_free); + +static void +_cancel_trigger_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + GsUpdatesSection *self = GS_UPDATES_SECTION (user_data); + g_autoptr(GError) error = NULL; + if (!gs_plugin_loader_job_action_finish (self->plugin_loader, res, &error)) { + g_warning ("failed to cancel trigger: %s", error->message); + return; + } +} + +static void +_reboot_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + GsUpdatesSection *self = GS_UPDATES_SECTION (user_data); + g_autoptr(GError) error = NULL; + GsApp *app = NULL; + g_autoptr(GsPluginJob) plugin_job = NULL; + + /* get result */ + if (gs_utils_invoke_reboot_finish (source, res, &error)) + return; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_debug ("Calling reboot had been cancelled"); + else if (error != NULL) + g_warning ("Calling reboot failed: %s", error->message); + + /* cancel trigger */ + app = gs_app_list_index (self->list, 0); + plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE_CANCEL, + "app", app, + NULL); + gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job, + gs_app_get_cancellable (app), + _cancel_trigger_failed_cb, + self); +} + +static gboolean +_all_offline_updates_downloaded (GsUpdatesSection *self) +{ + /* use the download size to figure out what is downloaded and what not */ + for (guint i = 0; i < gs_app_list_length (self->list); i++) { + GsApp *app = gs_app_list_index (self->list, i); + if (!gs_app_is_downloaded (app)) + return FALSE; + } + + return TRUE; +} + +static void +_update_buttons (GsUpdatesSection *self) +{ + /* operation in progress */ + if (self->cancellable != NULL) { + gtk_widget_set_sensitive (self->button_cancel, + !g_cancellable_is_cancelled (self->cancellable)); + gtk_stack_set_visible_child_name (GTK_STACK (self->button_stack), "cancel"); + gtk_widget_show (GTK_WIDGET (self->button_stack)); + return; + } + + if (self->kind == GS_UPDATES_SECTION_KIND_OFFLINE_FIRMWARE || + self->kind == GS_UPDATES_SECTION_KIND_OFFLINE) { + if (_all_offline_updates_downloaded (self)) + gtk_stack_set_visible_child_name (GTK_STACK (self->button_stack), "update"); + else + gtk_stack_set_visible_child_name (GTK_STACK (self->button_stack), "download"); + + gtk_widget_show (GTK_WIDGET (self->button_stack)); + /* TRANSLATORS: This is the button for installing all + * offline updates */ + gs_progress_button_set_label (GS_PROGRESS_BUTTON (self->button_update), _("Restart & Update")); + } else if (self->kind == GS_UPDATES_SECTION_KIND_ONLINE) { + gtk_stack_set_visible_child_name (GTK_STACK (self->button_stack), "update"); + gtk_widget_show (GTK_WIDGET (self->button_stack)); + /* TRANSLATORS: This is the button for upgrading all + * online-updatable applications */ + gs_progress_button_set_label (GS_PROGRESS_BUTTON (self->button_update), _("Update All")); + } else { + gtk_widget_hide (GTK_WIDGET (self->button_stack)); + } + +} + +static void +_perform_update_cb (GsPluginLoader *plugin_loader, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GsUpdatesSectionUpdateHelper) helper = (GsUpdatesSectionUpdateHelper *) user_data; + GsUpdatesSection *self = helper->self; + + /* get the results */ + if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) { + if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + GsApp *app = NULL; + + if (gs_app_list_length (self->list) == 1) + app = gs_app_list_index (self->list, 0); + + gs_plugin_loader_claim_error (plugin_loader, + NULL, + GS_PLUGIN_ACTION_UPDATE, + app, + TRUE, + error); + } + goto out; + } + + /* trigger reboot if any application was not updatable live */ + if (helper->do_reboot) { + gs_utils_invoke_reboot_async (NULL, _reboot_failed_cb, self); + + /* when we are not doing an offline update, show a notification + * if any application requires a reboot */ + } else if (helper->do_reboot_notification) { + gs_utils_reboot_notify (self->list, TRUE); + } + +out: + g_clear_object (&self->cancellable); + _update_buttons (self); +} + +static void +_button_cancel_clicked_cb (GsUpdatesSection *self) +{ + g_cancellable_cancel (self->cancellable); + _update_buttons (self); +} + +static void +_download_finished_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GsUpdatesSection) self = (GsUpdatesSection *) user_data; + g_autoptr(GError) error = NULL; + g_autoptr(GsAppList) list = NULL; + + /* get result */ + list = gs_plugin_loader_job_process_finish (GS_PLUGIN_LOADER (object), res, &error); + if (list == NULL) { + if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("failed to download updates: %s", error->message); + } + + g_clear_object (&self->cancellable); + _update_buttons (self); +} + +static void +_button_download_clicked_cb (GsUpdatesSection *self) +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new (); + g_autoptr(GsPluginJob) plugin_job = NULL; + + g_set_object (&self->cancellable, cancellable); + plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_DOWNLOAD, + "list", self->list, + "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE, + "interactive", TRUE, + NULL); + gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job, + self->cancellable, + (GAsyncReadyCallback) _download_finished_cb, + g_object_ref (self)); + _update_buttons (self); +} + +static void +_button_update_all_clicked_cb (GsUpdatesSection *self) +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new (); + g_autoptr(GsPluginJob) plugin_job = NULL; + GsUpdatesSectionUpdateHelper *helper = g_new0 (GsUpdatesSectionUpdateHelper, 1); + + helper->self = g_object_ref (self); + + /* look at each app in turn */ + for (guint i = 0; i < gs_app_list_length (self->list); i++) { + GsApp *app = gs_app_list_index (self->list, i); + if (gs_app_get_state (app) == GS_APP_STATE_UPDATABLE) + helper->do_reboot = TRUE; + if (gs_app_has_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT)) + helper->do_reboot_notification = TRUE; + } + + g_set_object (&self->cancellable, cancellable); + plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE, + "list", self->list, + "interactive", TRUE, + "propagate-error", TRUE, + NULL); + gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job, + self->cancellable, + (GAsyncReadyCallback) _perform_update_cb, + helper); + _update_buttons (self); +} + +static void +_setup_section_header (GsUpdatesSection *self) +{ + /* get labels and buttons for everything */ + switch (self->kind) { + case GS_UPDATES_SECTION_KIND_OFFLINE_FIRMWARE: + /* TRANSLATORS: This is the header for system firmware that + * requires a reboot to apply */ + gtk_label_set_label (GTK_LABEL (self->title), _("Integrated Firmware")); + break; + case GS_UPDATES_SECTION_KIND_OFFLINE: + /* TRANSLATORS: This is the header for offline OS and offline + * app updates that require a reboot to apply */ + gtk_label_set_label (GTK_LABEL (self->title), _("Requires Restart")); + break; + case GS_UPDATES_SECTION_KIND_ONLINE: + /* TRANSLATORS: This is the header for online runtime and + * app updates, typically flatpaks or snaps */ + gtk_label_set_label (GTK_LABEL (self->title), _("Application Updates")); + break; + case GS_UPDATES_SECTION_KIND_ONLINE_FIRMWARE: + /* TRANSLATORS: This is the header for device firmware that can + * be installed online */ + gtk_label_set_label (GTK_LABEL (self->title), _("Device Firmware")); + break; + default: + g_assert_not_reached (); + } +} + +static void +_app_row_activated_cb (GsUpdatesSection *self, GtkListBoxRow *row) +{ + GsApp *app = gs_app_row_get_app (GS_APP_ROW (row)); + GtkWidget *dialog; + g_autofree gchar *str = NULL; + + /* debug */ + str = gs_app_to_string (app); + g_debug ("%s", str); + + dialog = gs_update_dialog_new_for_app (self->plugin_loader, app); + gs_shell_modal_dialog_present (gs_page_get_shell (self->page), GTK_WINDOW (dialog)); +} + +static void +gs_updates_section_show (GtkWidget *widget) +{ + _update_buttons (GS_UPDATES_SECTION (widget)); + + GTK_WIDGET_CLASS (gs_updates_section_parent_class)->show (widget); +} + +static void +gs_updates_section_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsUpdatesSection *self = GS_UPDATES_SECTION (object); + + switch ((GsUpdatesSectionProperty) prop_id) { + case PROP_IS_NARROW: + g_value_set_boolean (value, gs_updates_section_get_is_narrow (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_updates_section_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsUpdatesSection *self = GS_UPDATES_SECTION (object); + + switch ((GsUpdatesSectionProperty) prop_id) { + case PROP_IS_NARROW: + gs_updates_section_set_is_narrow (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_updates_section_dispose (GObject *object) +{ + GsUpdatesSection *self = GS_UPDATES_SECTION (object); + + g_clear_object (&self->cancellable); + g_clear_object (&self->list); + g_clear_object (&self->plugin_loader); + g_clear_object (&self->sizegroup_name); + g_clear_object (&self->sizegroup_button_label); + g_clear_object (&self->sizegroup_button_image); + g_clear_object (&self->sizegroup_header); + self->button_download = NULL; + self->button_update = NULL; + self->button_cancel = NULL; + self->button_stack = NULL; + self->page = NULL; + + G_OBJECT_CLASS (gs_updates_section_parent_class)->dispose (object); +} + +static void +gs_updates_section_class_init (GsUpdatesSectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = gs_updates_section_get_property; + object_class->set_property = gs_updates_section_set_property; + object_class->dispose = gs_updates_section_dispose; + widget_class->show = gs_updates_section_show; + + /** + * GsUpdatesSection:is-narrow: + * + * Whether the section is in narrow mode. + * + * In narrow mode, the section will take up less horizontal space, doing + * so by e.g. using icons rather than labels in buttons. This is needed + * to keep the UI useable on small form-factors like smartphones. + * + * Since: 41 + */ + obj_props[PROP_IS_NARROW] = + g_param_spec_boolean ("is-narrow", NULL, NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_props), obj_props); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-updates-section.ui"); + + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, button_cancel); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, button_download); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, button_stack); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, button_update); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, description); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, listbox); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, listbox_box); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, section_header); + gtk_widget_class_bind_template_child (widget_class, GsUpdatesSection, title); + gtk_widget_class_bind_template_callback (widget_class, _app_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, _button_cancel_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, _button_download_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, _button_update_all_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, _listbox_keynav_failed_cb); +} + +void +gs_updates_section_set_size_groups (GsUpdatesSection *self, + GtkSizeGroup *name, + GtkSizeGroup *button_label, + GtkSizeGroup *button_image, + GtkSizeGroup *header) +{ + g_return_if_fail (GS_IS_UPDATES_SECTION (self)); + + g_set_object (&self->sizegroup_name, name); + g_set_object (&self->sizegroup_button_label, button_label); + g_set_object (&self->sizegroup_button_image, button_image); + g_set_object (&self->sizegroup_header, header); + + gs_progress_button_set_size_groups (GS_PROGRESS_BUTTON (self->button_cancel), button_label, button_image); + gs_progress_button_set_size_groups (GS_PROGRESS_BUTTON (self->button_download), button_label, button_image); + gs_progress_button_set_size_groups (GS_PROGRESS_BUTTON (self->button_update), button_label, button_image); + gtk_size_group_add_widget (self->sizegroup_header, self->section_header); +} + +static void +gs_updates_section_progress_notify_cb (GsAppList *list, + GParamSpec *pspec, + GsUpdatesSection *self) +{ + if (self->button_cancel == NULL) + return; + + gs_progress_button_set_progress (GS_PROGRESS_BUTTON (self->button_cancel), + gs_app_list_get_progress (list)); +} + +static void +gs_updates_section_app_state_changed_cb (GsAppList *list, + GsApp *in_app, + GsUpdatesSection *self) +{ + guint ii, len, busy = 0; + + len = gs_app_list_length (list); + + for (ii = 0; ii < len; ii++) { + GsApp *app = gs_app_list_index (list, ii); + GsAppState state = gs_app_get_state (app); + + if (state == GS_APP_STATE_INSTALLING || + state == GS_APP_STATE_REMOVING) { + busy++; + } + } + + gtk_widget_set_sensitive (self->button_update, busy < len); +} + +static void +gs_updates_section_init (GsUpdatesSection *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->list = gs_app_list_new (); + gs_app_list_add_flag (self->list, + GS_APP_LIST_FLAG_WATCH_APPS | + GS_APP_LIST_FLAG_WATCH_APPS_ADDONS | + GS_APP_LIST_FLAG_WATCH_APPS_RELATED); + g_signal_connect_object (self->list, "notify::progress", + G_CALLBACK (gs_updates_section_progress_notify_cb), + self, 0); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (self->listbox), + GTK_SELECTION_NONE); + gtk_list_box_set_sort_func (GTK_LIST_BOX (self->listbox), + _list_sort_func, + self, NULL); +} + +/** + * gs_updates_section_get_is_narrow: + * @self: a #GsUpdatesSection + * + * Get the value of #GsUpdatesSection:is-narrow. + * + * Returns: %TRUE if the section is in narrow mode, %FALSE otherwise + * + * Since: 41 + */ +gboolean +gs_updates_section_get_is_narrow (GsUpdatesSection *self) +{ + g_return_val_if_fail (GS_IS_UPDATES_SECTION (self), FALSE); + + return self->is_narrow; +} + +/** + * gs_updates_section_set_is_narrow: + * @self: a #GsUpdatesSection + * @is_narrow: %TRUE to set the section in narrow mode, %FALSE otherwise + * + * Set the value of #GsUpdatesSection:is-narrow. + * + * Since: 41 + */ +void +gs_updates_section_set_is_narrow (GsUpdatesSection *self, gboolean is_narrow) +{ + g_return_if_fail (GS_IS_UPDATES_SECTION (self)); + + is_narrow = !!is_narrow; + + if (self->is_narrow == is_narrow) + return; + + self->is_narrow = is_narrow; + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_IS_NARROW]); +} + +GsUpdatesSection * +gs_updates_section_new (GsUpdatesSectionKind kind, + GsPluginLoader *plugin_loader, + GsPage *page) +{ + GsUpdatesSection *self; + self = g_object_new (GS_TYPE_UPDATES_SECTION, NULL); + self->kind = kind; + self->plugin_loader = g_object_ref (plugin_loader); + self->page = page; + _setup_section_header (self); + + if (self->kind == GS_UPDATES_SECTION_KIND_ONLINE) { + g_signal_connect_object (self->list, "app-state-changed", + G_CALLBACK (gs_updates_section_app_state_changed_cb), + self, 0); + } + + return self; +} -- cgit v1.2.3