/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 - 2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . * * Author: David Zeuthen */ #include "config.h" #include #include #include #define GOA_API_IS_SUBJECT_TO_CHANGE #include #include "cc-online-accounts-panel.h" #include "cc-online-accounts-resources.h" #ifdef GDK_WINDOWING_X11 #include #endif #ifdef GDK_WINDOWING_WAYLAND #include #endif struct _CcGoaPanel { CcPanel parent_instance; GtkFrame *accounts_frame; GtkListBox *accounts_listbox; GtkWidget *close_notification_button; GtkDialog *edit_account_dialog; GtkHeaderBar *edit_account_headerbar; GtkBox *editor_box; GtkLabel *notification_label; GtkRevealer *notification_revealer; GtkLabel *offline_label; GtkListBox *providers_listbox; GtkButton *remove_account_button; GtkBox *accounts_vbox; GoaClient *client; GoaObject *active_object; GoaObject *removed_object; guint remove_account_timeout_id; gchar *window_export_handle; }; static gboolean remove_account_timeout_cb (gpointer user_data); CC_PANEL_REGISTER (CcGoaPanel, cc_goa_panel); enum { PROP_0, PROP_PARAMETERS }; /* Rows methods */ typedef void (*RowForAccountCallback) (CcGoaPanel *self, GtkWidget *row, GList *other_rows); static void hide_row_for_account_cb (CcGoaPanel *self, GtkWidget *row, GList *other_rows) { gtk_widget_hide (row); gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL); } static void remove_row_for_account_cb (CcGoaPanel *self, GtkWidget *row, GList *other_rows) { gtk_list_box_remove (self->accounts_listbox, row); gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL); } static void show_row_for_account_cb (CcGoaPanel *self, GtkWidget *row, GList *other_rows) { gtk_widget_show (row); gtk_widget_show (GTK_WIDGET (self->accounts_frame)); } static void modify_row_for_account (CcGoaPanel *self, GoaObject *object, RowForAccountCallback callback) { GtkWidget *child; GList *children = NULL; GList *l; for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox)); child; child = gtk_widget_get_next_sibling (child)) { children = g_list_prepend (children, child); } children = g_list_reverse (children); for (l = children; l != NULL; l = l->next) { GoaObject *row_object; row_object = g_object_get_data (G_OBJECT (l->data), "goa-object"); if (row_object == object) { GtkWidget *row = GTK_WIDGET (l->data); children = g_list_remove_link (children, l); callback (self, row, children); g_list_free (l); break; } } g_list_free (children); } /* Auxiliary methods */ 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-control-center-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_wait_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_wait_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-control-center-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 cancel_notification_timeout (CcGoaPanel *self) { g_clear_handle_id (&self->remove_account_timeout_id, g_source_remove); self->removed_object = NULL; } static void start_remove_account_timeout (CcGoaPanel *self) { GoaAccount *account; g_autofree gchar *id = NULL; g_autofree gchar *label = NULL; if (self->active_object == NULL) return; if (self->removed_object != NULL) gtk_widget_activate (self->close_notification_button); self->removed_object = g_steal_pointer (&self->active_object); account = goa_object_peek_account (self->removed_object); id = g_strdup_printf ("%s", goa_account_get_presentation_identity (account)); /* Translators: The %s is the username (eg., debarshi.ray@gmail.com * or rishi). */ label = g_strdup_printf (_("%s removed"), id); gtk_label_set_markup (self->notification_label, label); gtk_revealer_set_reveal_child (self->notification_revealer, TRUE); modify_row_for_account (self, self->removed_object, hide_row_for_account_cb); self->remove_account_timeout_id = g_timeout_add_seconds (10, remove_account_timeout_cb, self); } static void on_show_account_finish_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { CcGoaPanel *self = CC_GOA_PANEL (user_data); g_autofree char *output = NULL; g_autoptr(GError) error = NULL; output = g_task_propagate_pointer (G_TASK (result), &error); if (error) { g_warning ("Error showing account: %s", error->message); return; } if (g_strcmp0 (output, "remove") == 0) start_remove_account_timeout (self); self->active_object = NULL; } static void show_account (CcGoaPanel *self, GoaObject *object) { GoaAccount *account; if (!self->window_export_handle) return; self->active_object = g_object_ref (object); account = goa_object_peek_account (object); run_goa_helper_async ("show-account", goa_account_get_id (account), self->window_export_handle, cc_panel_get_cancellable (CC_PANEL (self)), on_show_account_finish_cb, self); } static void on_create_account_finish_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { CcGoaPanel *self = CC_GOA_PANEL (user_data); g_autofree char *new_account_id = NULL; g_autoptr(GoaObject) object = NULL; g_autoptr(GError) error = NULL; new_account_id = g_task_propagate_pointer (G_TASK (result), &error); if (error) { g_warning ("Error showing account: %s", error->message); return; } if (new_account_id) object = goa_client_lookup_by_id (self->client, new_account_id); if (object) show_account (self, object); } static void create_account (CcGoaPanel *self, GVariant *provider) { g_autofree char *provider_type = NULL; if (!self->window_export_handle) return; g_variant_get (provider, "(ssviu)", &provider_type, NULL, NULL, NULL, NULL); run_goa_helper_async ("create-account", provider_type, self->window_export_handle, cc_panel_get_cancellable (CC_PANEL (self)), on_create_account_finish_cb, self); } 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_row (CcGoaPanel *self, GVariant *provider) { g_autofree char *name = NULL; g_autoptr(GIcon) icon = NULL; GtkWidget *image; GtkWidget *row; row = adw_action_row_new (); gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE); if (provider == NULL) { g_object_set_data (G_OBJECT (row), "goa-provider", NULL); icon = g_themed_icon_new_with_default_fallbacks ("goa-account"); name = g_strdup (C_("Online Account", "Other")); } else { g_autoptr(GVariant) icon_variant = NULL; g_object_set_data_full (G_OBJECT (row), "goa-provider", g_variant_ref (provider), (GDestroyNotify) g_variant_unref); g_variant_get (provider, "(ssviu)", NULL, &name, &icon_variant, NULL, NULL); icon = g_icon_deserialize (icon_variant); } image = gtk_image_new_from_gicon (icon); gtk_widget_set_halign (image, GTK_ALIGN_CENTER); gtk_widget_set_valign (image, GTK_ALIGN_CENTER); if (is_gicon_symbolic (image, 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"); } adw_action_row_add_prefix (ADW_ACTION_ROW (row), image); adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), name); gtk_list_box_append (self->providers_listbox, row); } static void list_providers (CcGoaPanel *self) { g_autoptr(GVariant) providers_variant = NULL; g_autoptr(GError) error = NULL; g_autofree char *providers = NULL; GVariantIter iter; GVariant *provider; providers = run_goa_helper_sync ("list-providers", NULL); providers_variant = g_variant_parse (G_VARIANT_TYPE ("a(ssviu)"), providers, NULL, NULL, &error); if (error) { g_warning ("Error listing providers: %s", error->message); return; } g_variant_iter_init (&iter, providers_variant); while ((provider = g_variant_iter_next_value (&iter))) add_provider_row (self, provider); } static void add_account (CcGoaPanel *self, GoaObject *object) { g_autoptr(GError) error = NULL; g_autoptr(GIcon) gicon = NULL; GoaAccount *account; GtkWidget *row, *icon; account = goa_object_peek_account (object); row = adw_action_row_new (); g_object_set_data (G_OBJECT (row), "goa-object", object); gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE); adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), goa_account_get_provider_name (account)); adw_action_row_set_subtitle (ADW_ACTION_ROW (row), goa_account_get_presentation_identity (account)); /* The provider icon */ icon = gtk_image_new (); gtk_widget_set_halign (icon, GTK_ALIGN_CENTER); gtk_widget_set_valign (icon, GTK_ALIGN_CENTER); gicon = g_icon_new_for_string (goa_account_get_provider_icon (account), &error); if (error != NULL) { g_warning ("Error creating GIcon for account: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); } else { gtk_image_set_from_gicon (GTK_IMAGE (icon), gicon); if (is_gicon_symbolic (icon, gicon)) { gtk_image_set_icon_size (GTK_IMAGE (icon), GTK_ICON_SIZE_NORMAL); gtk_widget_add_css_class (icon, "symbolic-circular"); } else { gtk_image_set_icon_size (GTK_IMAGE (icon), GTK_ICON_SIZE_LARGE); gtk_widget_add_css_class (icon, "lowres-icon"); } } adw_action_row_add_prefix (ADW_ACTION_ROW (row), icon); /* "Needs attention" icon */ icon = gtk_image_new_from_icon_name ("dialog-warning-symbolic"); g_object_bind_property (account, "attention-needed", icon, "visible", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); adw_action_row_add_suffix (ADW_ACTION_ROW (row), icon); /* Add to the listbox */ gtk_list_box_append (self->accounts_listbox, row); gtk_widget_show (GTK_WIDGET (self->accounts_frame)); } static void fill_accounts_listbox (CcGoaPanel *self) { g_autolist(GoaAccount) accounts = NULL; GList *l; accounts = goa_client_get_accounts (self->client); for (l = accounts; l != NULL; l = l->next) add_account (self, l->data); } #ifdef GDK_WINDOWING_WAYLAND static void wayland_window_exported_cb (GdkToplevel *toplevel, const char *handle, gpointer data) { CcGoaPanel *self = data; self->window_export_handle = g_strdup_printf ("wayland:%s", handle); } #endif static void export_window_handle (CcGoaPanel *self) { GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self)); #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); self->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, self, NULL); } #endif } static void unexport_window_handle (CcGoaPanel *self) { if (!self->window_export_handle) return; #ifdef GDK_WINDOWING_WAYLAND GtkNative *native = gtk_widget_get_native (GTK_WIDGET (self)); 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 select_account_by_id (CcGoaPanel *self, const gchar *account_id) { GtkWidget *child; for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox)); child; child = gtk_widget_get_next_sibling (child)) { GoaAccount *account; GoaObject *row_object; row_object = g_object_get_data (G_OBJECT (child), "goa-object"); account = goa_object_peek_account (row_object); if (g_strcmp0 (goa_account_get_id (account), account_id) == 0) { show_account (self, row_object); break; } } } static void command_add (CcGoaPanel *self, GVariant *parameters) { const gchar *provider_name = NULL; GVariant *v = NULL; g_assert (self != NULL); g_assert (parameters != NULL); switch (g_variant_n_children (parameters)) { case 2: g_variant_get_child (parameters, 1, "v", &v); if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) provider_name = g_variant_get_string (v, NULL); else g_warning ("Wrong type for the second argument (provider name) GVariant, expected 's' but got '%s'", (gchar *)g_variant_get_type (v)); g_variant_unref (v); break; default: g_warning ("Unexpected parameters found, ignore request"); return; } if (provider_name != NULL) { GtkWidget *child; GVariant *provider; for (child = gtk_widget_get_first_child (GTK_WIDGET (self->providers_listbox)); child; child = gtk_widget_get_next_sibling (child)) { g_autofree gchar *provider_type = NULL; provider = g_object_get_data (G_OBJECT (child), "goa-provider"); g_variant_get (provider, "(ssviu)", &provider_type, NULL, NULL, NULL, NULL); if (g_strcmp0 (provider_type, provider_name) == 0) break; } if (child == NULL) { g_warning ("Unable to get a provider for type '%s'", provider_name); return; } create_account (self, provider); } } static void load_custom_css (void) { g_autoptr(GtkCssProvider) provider = NULL; provider = gtk_css_provider_new (); gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/online-accounts/online-accounts.css"); gtk_style_context_add_provider_for_display (gdk_display_get_default (), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } /* Callbacks */ static gint sort_accounts_func (GtkListBoxRow *a, GtkListBoxRow *b, gpointer user_data) { GoaAccount *a_account, *b_account; GoaObject *a_object, *b_object; a_object = g_object_get_data (G_OBJECT (a), "goa-object"); a_account = goa_object_peek_account (a_object); b_object = g_object_get_data (G_OBJECT (b), "goa-object"); b_account = goa_object_peek_account (b_object); return g_strcmp0 (goa_account_get_id (a_account), goa_account_get_id (b_account)); } static gint sort_providers_func (GtkListBoxRow *a, GtkListBoxRow *b, gpointer user_data) { GVariant *a_provider, *b_provider; gboolean a_branded, b_branded; gint a_features, b_features; a_provider = g_object_get_data (G_OBJECT (a), "goa-provider"); b_provider = g_object_get_data (G_OBJECT (b), "goa-provider"); g_variant_get (a_provider, "(ssviu)", NULL, NULL, NULL, &a_features, NULL); g_variant_get (b_provider, "(ssviu)", NULL, NULL, NULL, &b_features, NULL); /* FIXME: this needs to go away once libgoa-backend is ported to GTK4 */ #define FEATURE_BRANDED (1 << 1) a_branded = (a_features & FEATURE_BRANDED) != 0; b_branded = (a_features & FEATURE_BRANDED) != 0; #undef FEATURE_BRANDED if (a_branded != b_branded) { if (a_branded) return -1; else return 1; } return gtk_list_box_row_get_index (b) - gtk_list_box_row_get_index (a); } static void on_account_added_cb (GoaClient *client, GoaObject *object, CcGoaPanel *self) { add_account (self, object); } static void on_account_changed_cb (GoaClient *client, GoaObject *object, CcGoaPanel *self) { if (self->active_object == object) show_account (self, self->active_object); } static void on_account_removed_cb (GoaClient *client, GoaObject *object, CcGoaPanel *self) { modify_row_for_account (self, object, remove_row_for_account_cb); } static void on_accounts_listbox_row_activated (CcGoaPanel *self, GtkListBoxRow *activated_row) { GoaObject *object = g_object_get_data (G_OBJECT (activated_row), "goa-object"); show_account (self, object); } static void on_client_remove_account_finish_cb (GoaAccount *account, GAsyncResult *res, CcGoaPanel *self) { g_autoptr(GError) error = NULL; goa_account_call_remove_finish (account, res, &error); if (error) { GtkWidget *dialog; dialog = gtk_message_dialog_new (GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)))), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Error removing account")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message); gtk_window_present (GTK_WINDOW (dialog)); } g_object_unref (self); } static void on_notification_closed_cb (GtkButton *button, CcGoaPanel *self) { goa_account_call_remove (goa_object_peek_account (self->removed_object), cc_panel_get_cancellable (CC_PANEL (self)), (GAsyncReadyCallback) on_client_remove_account_finish_cb, g_object_ref (self)); gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); cancel_notification_timeout (self); self->removed_object = NULL; } static void on_undo_button_clicked_cb (GtkButton *button, CcGoaPanel *self) { /* Simply show the account row and hide the notification */ modify_row_for_account (self, self->removed_object, show_row_for_account_cb); gtk_revealer_set_reveal_child (self->notification_revealer, FALSE); cancel_notification_timeout (self); self->removed_object = NULL; } static void on_provider_row_activated_cb (CcGoaPanel *self, GtkListBoxRow *activated_row) { GVariant *provider = g_object_get_data (G_OBJECT (activated_row), "goa-provider"); create_account (self, provider); } static gboolean remove_account_timeout_cb (gpointer user_data) { CcGoaPanel *self = CC_GOA_PANEL (user_data); gtk_widget_activate (self->close_notification_button); return G_SOURCE_REMOVE; } /* CcPanel overrides */ static const char * cc_goa_panel_get_help_uri (CcPanel *panel) { return "help:gnome-help/accounts"; } /* GtkWidget overrides */ static void cc_goa_panel_realize (GtkWidget *widget) { GTK_WIDGET_CLASS (cc_goa_panel_parent_class)->realize (widget); export_window_handle (CC_GOA_PANEL (widget)); } static void cc_goa_panel_unrealize (GtkWidget *widget) { unexport_window_handle (CC_GOA_PANEL (widget)); GTK_WIDGET_CLASS (cc_goa_panel_parent_class)->unrealize (widget); } /* GObject overrides */ static void cc_goa_panel_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_PARAMETERS: { GVariant *parameters, *v; const gchar *first_arg = NULL; parameters = g_value_get_variant (value); if (parameters == NULL) return; if (g_variant_n_children (parameters) > 0) { g_variant_get_child (parameters, 0, "v", &v); if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) first_arg = g_variant_get_string (v, NULL); else g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'", (gchar *)g_variant_get_type (v)); g_variant_unref (v); } if (g_strcmp0 (first_arg, "add") == 0) command_add (CC_GOA_PANEL (object), parameters); else if (first_arg != NULL) select_account_by_id (CC_GOA_PANEL (object), first_arg); return; } } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void cc_goa_panel_constructed (GObject *object) { CcGoaPanel *self = CC_GOA_PANEL (object); G_OBJECT_CLASS (cc_goa_panel_parent_class)->constructed (object); list_providers (self); } static void cc_goa_panel_finalize (GObject *object) { CcGoaPanel *panel = CC_GOA_PANEL (object); if (panel->removed_object != NULL) { g_autoptr(GError) error = NULL; goa_account_call_remove_sync (goa_object_peek_account (panel->removed_object), NULL, /* GCancellable */ &error); if (error != NULL) { g_warning ("Error removing account: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); } } g_clear_object (&panel->client); G_OBJECT_CLASS (cc_goa_panel_parent_class)->finalize (object); } static void cc_goa_panel_class_init (CcGoaPanelClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); CcPanelClass *panel_class = CC_PANEL_CLASS (klass); panel_class->get_help_uri = cc_goa_panel_get_help_uri; object_class->set_property = cc_goa_panel_set_property; object_class->finalize = cc_goa_panel_finalize; object_class->constructed = cc_goa_panel_constructed; widget_class->realize = cc_goa_panel_realize; widget_class->unrealize = cc_goa_panel_unrealize; g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters"); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/online-accounts/cc-online-accounts-panel.ui"); gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, accounts_frame); gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, accounts_listbox); gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, close_notification_button); gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, notification_label); gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, notification_revealer); gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, offline_label); gtk_widget_class_bind_template_child (widget_class, CcGoaPanel, providers_listbox); gtk_widget_class_bind_template_callback (widget_class, on_accounts_listbox_row_activated); gtk_widget_class_bind_template_callback (widget_class, on_notification_closed_cb); gtk_widget_class_bind_template_callback (widget_class, on_provider_row_activated_cb); gtk_widget_class_bind_template_callback (widget_class, on_undo_button_clicked_cb); } static void cc_goa_panel_init (CcGoaPanel *self) { g_autoptr(GError) error = NULL; GNetworkMonitor *monitor; g_resources_register (cc_online_accounts_get_resource ()); gtk_widget_init_template (GTK_WIDGET (self)); gtk_list_box_set_sort_func (self->accounts_listbox, sort_accounts_func, self, NULL); gtk_list_box_set_sort_func (self->providers_listbox, sort_providers_func, self, NULL); monitor = g_network_monitor_get_default(); g_object_bind_property (monitor, "network-available", self->offline_label, "visible", G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); g_object_bind_property (monitor, "network-available", self->providers_listbox, "sensitive", G_BINDING_SYNC_CREATE); /* TODO: probably want to avoid _sync() ... */ self->client = goa_client_new_sync (cc_panel_get_cancellable (CC_PANEL (self)), &error); if (self->client == NULL) { g_warning ("Error getting a GoaClient: %s (%s, %d)", error->message, g_quark_to_string (error->domain), error->code); gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); return; } g_signal_connect (self->client, "account-added", G_CALLBACK (on_account_added_cb), self); g_signal_connect (self->client, "account-changed", G_CALLBACK (on_account_changed_cb), self); g_signal_connect (self->client, "account-removed", G_CALLBACK (on_account_removed_cb), self); fill_accounts_listbox (self); load_custom_css (); }