From e05fb7b3e36c052baf0dd607ddeb22c0a2b5cbde Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:47:04 +0200 Subject: Adding upstream version 43.2. Signed-off-by: Daniel Baumann --- gnome-initial-setup/pages/goa/gis-goa-page.c | 606 +++++++++++++++++++++++++++ 1 file changed, 606 insertions(+) create mode 100644 gnome-initial-setup/pages/goa/gis-goa-page.c (limited to 'gnome-initial-setup/pages/goa/gis-goa-page.c') diff --git a/gnome-initial-setup/pages/goa/gis-goa-page.c b/gnome-initial-setup/pages/goa/gis-goa-page.c new file mode 100644 index 0000000..b934825 --- /dev/null +++ b/gnome-initial-setup/pages/goa/gis-goa-page.c @@ -0,0 +1,606 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2012 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Written by: + * Jasper St. Pierre + */ + +/* Online accounts page {{{1 */ + +#define PAGE_ID "goa" + +#include "config.h" +#include "gis-goa-page.h" +#include "goa-resources.h" + +#define GOA_API_IS_SUBJECT_TO_CHANGE +#include + +#include +#include + +#include "gis-page-header.h" + +#ifdef GDK_WINDOWING_X11 +#include +#endif +#ifdef GDK_WINDOWING_WAYLAND +#include +#endif + +#define VENDOR_GOA_GROUP "goa" +#define VENDOR_PROVIDERS_KEY "providers" + +struct _GisGoaPagePrivate { + GtkWidget *accounts_list; + + GoaClient *goa_client; + GHashTable *providers; + gboolean accounts_exist; + char *window_export_handle; +}; +typedef struct _GisGoaPagePrivate GisGoaPagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GisGoaPage, gis_goa_page, GIS_TYPE_PAGE); + +struct _ProviderWidget { + GisGoaPage *page; + GVariant *provider; + GoaAccount *displayed_account; + + GtkWidget *row; + GtkWidget *arrow_icon; +}; +typedef struct _ProviderWidget ProviderWidget; + +G_GNUC_NULL_TERMINATED +static char * +run_goa_helper_sync (const char *command, + ...) +{ + g_autoptr(GPtrArray) argv = NULL; + g_autofree char *output = NULL; + g_autoptr(GError) error = NULL; + const char *param; + va_list args; + int status; + + argv = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/gnome-initial-setup-goa-helper")); + g_ptr_array_add (argv, g_strdup (command)); + + va_start (args, command); + while ((param = va_arg (args, const char*)) != NULL) + g_ptr_array_add (argv, g_strdup (param)); + va_end (args); + + g_ptr_array_add (argv, NULL); + + if (!g_spawn_sync (NULL, + (char **) argv->pdata, + NULL, + 0, + NULL, + NULL, + &output, + NULL, + &status, + &error)) + { + g_warning ("Failed to run online accounts helper: %s", error->message); + return NULL; + } + + if (!g_spawn_check_exit_status (status, NULL)) + return NULL; + + if (output == NULL || *output == '\0') + return NULL; + + return g_steal_pointer (&output); +} + +static void +run_goa_helper_in_thread_func (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + g_autofree char *output = NULL; + g_autoptr(GError) error = NULL; + GPtrArray *argv = task_data; + int status; + + g_spawn_sync (NULL, + (char **) argv->pdata, + NULL, 0, NULL, NULL, + &output, + NULL, + &status, + &error); + + if (error) + { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + if (!g_spawn_check_exit_status (status, &error)) + { + g_task_return_error (task, g_steal_pointer (&error)); + return; + } + + g_task_return_pointer (task, g_steal_pointer (&output), g_free); +} + +static void +run_goa_helper_async (const gchar *command, + const gchar *param, + const gchar *window_handle, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GPtrArray) argv = NULL; + g_autoptr(GTask) task = NULL; + + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + argv = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/gnome-initial-setup-goa-helper")); + g_ptr_array_add (argv, g_strdup (command)); + g_ptr_array_add (argv, g_strdup (param)); + g_ptr_array_add (argv, g_strdup (window_handle)); + g_ptr_array_add (argv, NULL); + + task = g_task_new (NULL, cancellable, callback, user_data); + g_task_set_source_tag (task, run_goa_helper_async); + g_task_set_task_data (task, g_steal_pointer (&argv), (GDestroyNotify) g_ptr_array_unref); + g_task_run_in_thread (task, run_goa_helper_in_thread_func); +} + +static void +sync_provider_widget (ProviderWidget *provider_widget) +{ + gboolean has_account = (provider_widget->displayed_account != NULL); + + gtk_widget_set_visible (provider_widget->arrow_icon, !has_account); + gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (provider_widget->row), !has_account); + + adw_action_row_set_subtitle (ADW_ACTION_ROW (provider_widget->row), + has_account ? goa_account_get_presentation_identity (provider_widget->displayed_account) : NULL); + +} + +static void +on_create_account_finish_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + g_autofree char *new_account_id = NULL; + g_autoptr(GError) error = NULL; + + new_account_id = g_task_propagate_pointer (G_TASK (result), &error); + + if (error) + g_warning ("Error creating account: %s", error->message); +} + +static void +add_account_to_provider (ProviderWidget *provider_widget) +{ + GisGoaPage *page = provider_widget->page; + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + g_autofree char *provider_type = NULL; + + if (!priv->window_export_handle) + return; + + g_variant_get (provider_widget->provider, "(ssviu)", &provider_type, NULL, NULL, NULL, NULL); + + run_goa_helper_async ("create-account", + provider_type, + priv->window_export_handle, + NULL, + on_create_account_finish_cb, + page); +} + +static gboolean +is_gicon_symbolic (GtkWidget *widget, + GIcon *icon) +{ + g_autoptr(GtkIconPaintable) icon_paintable = NULL; + GtkIconTheme *icon_theme; + + icon_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ()); + icon_paintable = gtk_icon_theme_lookup_by_gicon (icon_theme, + icon, + 32, + gtk_widget_get_scale_factor (widget), + gtk_widget_get_direction (widget), + 0); + + return icon_paintable && gtk_icon_paintable_is_symbolic (icon_paintable); +} + +static void +add_provider_to_list (GisGoaPage *page, GVariant *provider) +{ + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + g_autoptr(GIcon) provider_icon = NULL; + g_autofree char *provider_name = NULL; + g_autofree char *provider_type = NULL; + g_autoptr(GVariant) icon_variant = NULL; + ProviderWidget *provider_widget; + GtkWidget *row; + GtkWidget *image; + GtkWidget *arrow_icon; + + g_variant_get (provider, "(ssviu)", + &provider_type, + &provider_name, + &icon_variant, + NULL, + NULL); + + row = adw_action_row_new (); + gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE); + adw_preferences_row_set_use_markup (ADW_PREFERENCES_ROW (row), TRUE); + adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), provider_name); + + provider_icon = g_icon_deserialize (icon_variant); + image = gtk_image_new_from_gicon (provider_icon); + adw_action_row_add_prefix (ADW_ACTION_ROW (row), image); + + if (is_gicon_symbolic (GTK_WIDGET (page), provider_icon)) + { + gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_NORMAL); + gtk_widget_add_css_class (image, "symbolic-circular"); + } + else + { + gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE); + gtk_widget_add_css_class (image, "lowres-icon"); + } + + arrow_icon = gtk_image_new_from_icon_name ("go-next-symbolic"); + adw_action_row_add_suffix (ADW_ACTION_ROW (row), arrow_icon); + + provider_widget = g_new0 (ProviderWidget, 1); + provider_widget->page = page; + provider_widget->provider = g_variant_ref (provider); + provider_widget->row = row; + provider_widget->arrow_icon = arrow_icon; + + g_object_set_data_full (G_OBJECT (row), "widget", provider_widget, g_free); + + g_hash_table_insert (priv->providers, g_steal_pointer (&provider_type), provider_widget); + + gtk_list_box_append (GTK_LIST_BOX (priv->accounts_list), row); +} + +static void +populate_provider_list (GisGoaPage *page) +{ + g_autoptr(GVariant) providers_variant = NULL; + g_autoptr(GError) error = NULL; + g_autofree char *listed_providers = NULL; + GVariantIter iter; + GVariant *provider; + g_auto(GStrv) conf_providers = + gis_driver_conf_get_string_list (GIS_PAGE (page)->driver, VENDOR_GOA_GROUP, VENDOR_PROVIDERS_KEY, NULL); + GStrv providers = conf_providers ? conf_providers : + (gchar *[]) { "google", "owncloud", "windows_live", "facebook", NULL }; + + listed_providers = run_goa_helper_sync ("list-providers", NULL); + providers_variant = g_variant_parse (G_VARIANT_TYPE ("a(ssviu)"), + listed_providers, + NULL, + NULL, + &error); + + if (error) + { + g_warning ("Error listing providers: %s", error->message); + gtk_widget_hide (GTK_WIDGET (page)); + return; + } + + /* This code will read the keyfile containing vendor customization options and + * look for options under the "goa" group, and supports the following keys: + * - providers (optional): list of online account providers to offer + * + * This is how this file might look on a vendor image: + * + * [goa] + * providers=owncloud;imap_smtp + */ + + g_variant_iter_init (&iter, providers_variant); + + while ((provider = g_variant_iter_next_value (&iter))) + { + g_autofree gchar *id = NULL; + + g_variant_get (provider, "(ssviu)", &id, NULL, NULL, NULL, NULL); + + if (g_strv_contains ((const char * const *)providers, id)) + add_provider_to_list (page, provider); + } +} + +static void +sync_visibility (GisGoaPage *page) +{ + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + GisAssistant *assistant = gis_driver_get_assistant (GIS_PAGE (page)->driver); + GNetworkMonitor *network_monitor = g_network_monitor_get_default (); + gboolean visible; + + if (gis_assistant_get_current_page (assistant) == GIS_PAGE (page)) + return; + + visible = (priv->accounts_exist || g_network_monitor_get_network_available (network_monitor)); + gtk_widget_set_visible (GTK_WIDGET (page), visible); +} + +static void +sync_accounts (GisGoaPage *page) +{ + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + GList *accounts, *l; + + accounts = goa_client_get_accounts (priv->goa_client); + + for (l = accounts; l != NULL; l = l->next) { + GoaObject *object = GOA_OBJECT (l->data); + GoaAccount *account = goa_object_get_account (object); + const char *account_type = goa_account_get_provider_type (account); + ProviderWidget *provider_widget; + + provider_widget = g_hash_table_lookup (priv->providers, account_type); + if (!provider_widget) + continue; + + priv->accounts_exist = TRUE; + + if (provider_widget->displayed_account) + continue; + + provider_widget->displayed_account = account; + sync_provider_widget (provider_widget); + } + + g_list_free_full (accounts, (GDestroyNotify) g_object_unref); + + sync_visibility (page); + gis_page_set_skippable (GIS_PAGE (page), !priv->accounts_exist); + gis_page_set_complete (GIS_PAGE (page), priv->accounts_exist); +} + +static void +accounts_changed (GoaClient *client, GoaObject *object, gpointer user_data) +{ + GisGoaPage *page = GIS_GOA_PAGE (user_data); + sync_accounts (page); +} + +static void +network_status_changed (GNetworkMonitor *monitor, + gboolean available, + gpointer user_data) +{ + GisGoaPage *page = GIS_GOA_PAGE (user_data); + sync_visibility (page); +} + +static void +row_activated (GtkListBox *box, + GtkListBoxRow *row, + GisGoaPage *page) +{ + ProviderWidget *provider_widget; + + if (row == NULL) + return; + + provider_widget = g_object_get_data (G_OBJECT (row), "widget"); + g_assert (provider_widget != NULL); + g_assert (provider_widget->displayed_account == NULL); + add_account_to_provider (provider_widget); +} + +#ifdef GDK_WINDOWING_WAYLAND +static void +wayland_window_exported_cb (GdkToplevel *toplevel, + const char *handle, + gpointer data) + +{ + GisGoaPage *page = data; + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + + priv->window_export_handle = g_strdup_printf ("wayland:%s", handle); +} +#endif + +static void +export_window_handle (GisGoaPage *page) +{ + GtkNative *native = gtk_widget_get_native (GTK_WIDGET (page)); + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native)))) + { + GdkSurface *surface = gtk_native_get_surface (native); + guint32 xid = (guint32) gdk_x11_surface_get_xid (surface); + + priv->window_export_handle = g_strdup_printf ("x11:%x", xid); + } +#endif +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native)))) + { + GdkSurface *surface = gtk_native_get_surface (native); + + gdk_wayland_toplevel_export_handle (GDK_TOPLEVEL (surface), + wayland_window_exported_cb, + page, + NULL); + } +#endif +} + +static void +unexport_window_handle (GisGoaPage *page) +{ + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + + if (!priv->window_export_handle) + return; + +#ifdef GDK_WINDOWING_WAYLAND + GtkNative *native = gtk_widget_get_native (GTK_WIDGET (page)); + + if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (native)))) + { + GdkSurface *surface = gtk_native_get_surface (native); + gdk_wayland_toplevel_unexport_handle (GDK_TOPLEVEL (surface)); + } +#endif +} + + +static void +gis_goa_page_realize (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (gis_goa_page_parent_class)->realize (widget); + + export_window_handle (GIS_GOA_PAGE (widget)); +} + +static void +gis_goa_page_unrealize (GtkWidget *widget) +{ + unexport_window_handle (GIS_GOA_PAGE (widget)); + + GTK_WIDGET_CLASS (gis_goa_page_parent_class)->unrealize (widget); +} + + +static void +gis_goa_page_constructed (GObject *object) +{ + GisGoaPage *page = GIS_GOA_PAGE (object); + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + GError *error = NULL; + GNetworkMonitor *network_monitor = g_network_monitor_get_default (); + + G_OBJECT_CLASS (gis_goa_page_parent_class)->constructed (object); + + gis_page_set_skippable (GIS_PAGE (page), TRUE); + + priv->providers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + priv->goa_client = goa_client_new_sync (NULL, &error); + + if (priv->goa_client == NULL) { + g_warning ("Failed to get a GoaClient: %s", error->message); + g_error_free (error); + return; + } + + g_signal_connect (priv->goa_client, "account-added", + G_CALLBACK (accounts_changed), page); + g_signal_connect (priv->goa_client, "account-removed", + G_CALLBACK (accounts_changed), page); + g_signal_connect (network_monitor, "network-changed", + G_CALLBACK (network_status_changed), page); + + g_signal_connect (priv->accounts_list, "row-activated", + G_CALLBACK (row_activated), page); + + populate_provider_list (page); + sync_accounts (page); +} + +static void +gis_goa_page_dispose (GObject *object) +{ + GisGoaPage *page = GIS_GOA_PAGE (object); + GisGoaPagePrivate *priv = gis_goa_page_get_instance_private (page); + GNetworkMonitor *network_monitor = g_network_monitor_get_default (); + + g_clear_object (&priv->goa_client); + + g_signal_handlers_disconnect_by_func (network_monitor, G_CALLBACK (network_status_changed), page); + + G_OBJECT_CLASS (gis_goa_page_parent_class)->dispose (object); +} + +static void +gis_goa_page_locale_changed (GisPage *page) +{ + gis_page_set_title (GIS_PAGE (page), _("Online Accounts")); +} + +static void +gis_goa_page_class_init (GisGoaPageClass *klass) +{ + GisPageClass *page_class = GIS_PAGE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/initial-setup/gis-goa-page.ui"); + + gtk_widget_class_bind_template_child_private (widget_class, GisGoaPage, accounts_list); + + page_class->page_id = PAGE_ID; + page_class->locale_changed = gis_goa_page_locale_changed; + object_class->constructed = gis_goa_page_constructed; + object_class->dispose = gis_goa_page_dispose; + widget_class->realize = gis_goa_page_realize; + widget_class->unrealize = gis_goa_page_unrealize; +} + +static void +gis_goa_page_init (GisGoaPage *page) +{ + g_autoptr(GtkCssProvider) provider = NULL; + + g_resources_register (goa_get_resource ()); + g_type_ensure (GIS_TYPE_PAGE_HEADER); + + gtk_widget_init_template (GTK_WIDGET (page)); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, "/org/gnome/initial-setup/gis-goa-page.css"); + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +} + +GisPage * +gis_prepare_goa_page (GisDriver *driver) +{ + return g_object_new (GIS_TYPE_GOA_PAGE, + "driver", driver, + NULL); +} -- cgit v1.2.3