diff options
Diffstat (limited to '')
-rw-r--r-- | panels/region/cc-region-panel.c | 1613 |
1 files changed, 1613 insertions, 0 deletions
diff --git a/panels/region/cc-region-panel.c b/panels/region/cc-region-panel.c new file mode 100644 index 0000000..9b59b23 --- /dev/null +++ b/panels/region/cc-region-panel.c @@ -0,0 +1,1613 @@ +/* + * Copyright (C) 2010 Intel, Inc + * + * 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 <http://www.gnu.org/licenses/>. + * + * Author: Sergey Udaltsov <svu@gnome.org> + * + */ + +#include <config.h> +#include <errno.h> +#include <locale.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gio/gdesktopappinfo.h> +#include <gtk/gtk.h> +#include <polkit/polkit.h> + +#include "list-box-helper.h" +#include "cc-region-panel.h" +#include "cc-region-resources.h" +#include "cc-language-chooser.h" +#include "cc-format-chooser.h" +#include "cc-input-chooser.h" +#include "cc-input-row.h" +#include "cc-input-source-ibus.h" +#include "cc-input-source-xkb.h" + +#include "cc-common-language.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include <libgnome-desktop/gnome-languages.h> +#include <libgnome-desktop/gnome-xkb-info.h> + +#ifdef HAVE_IBUS +#include <ibus.h> +#endif + +#include <act/act.h> + +#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" +#define KEY_INPUT_SOURCES "sources" + +#define GNOME_SYSTEM_LOCALE_DIR "org.gnome.system.locale" +#define KEY_REGION "region" + +#define DEFAULT_LOCALE "en_US.utf-8" + +struct _CcRegionPanel { + CcPanel parent_instance; + + GtkListBoxRow *add_input_row; + GtkLabel *alt_next_source; + GtkLabel *formats_label; + GtkListBoxRow *formats_row; + GtkListBox *input_list; + GtkBox *input_section_box; + GtkSizeGroup *input_size_group; + GtkToggleButton *login_button; + GtkLabel *login_label; + GtkLabel *language_label; + GtkListBox *language_list; + GtkListBoxRow *language_row; + GtkFrame *language_section_frame; + GtkLabel *next_source; + GtkLabel *next_source_label; + GtkListBoxRow *no_inputs_row; + GtkButton *options_button; + GtkRadioButton *per_window_source; + GtkLabel *previous_source; + GtkLabel *previous_source_label; + GtkButton *restart_button; + GtkRevealer *restart_revealer; + GtkRadioButton *same_source; + + gboolean login; + gboolean login_auto_apply; + GPermission *permission; + GDBusProxy *localed; + GDBusProxy *session; + + ActUserManager *user_manager; + ActUser *user; + GSettings *locale_settings; + + gchar *language; + gchar *region; + gchar *system_language; + gchar *system_region; + + GSettings *input_settings; + GnomeXkbInfo *xkb_info; +#ifdef HAVE_IBUS + IBusBus *ibus; + GHashTable *ibus_engines; +#endif +}; + +CC_PANEL_REGISTER (CcRegionPanel, cc_region_panel) + +typedef struct +{ + CcRegionPanel *panel; + CcInputRow *source; + CcInputRow *dest; +} RowData; + +static RowData * +row_data_new (CcRegionPanel *panel, CcInputRow *source, CcInputRow *dest) +{ + RowData *data = g_malloc0 (sizeof (RowData)); + data->panel = panel; + data->source = g_object_ref (source); + if (dest != NULL) + data->dest = g_object_ref (dest); + return data; +} + +static void +row_data_free (RowData *data) +{ + g_clear_object (&data->source); + g_clear_object (&data->dest); + g_free (data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (RowData, row_data_free) + +static void +cc_region_panel_finalize (GObject *object) +{ + CcRegionPanel *self = CC_REGION_PANEL (object); + GtkWidget *chooser; + + if (self->user_manager) { + g_signal_handlers_disconnect_by_data (self->user_manager, self); + self->user_manager = NULL; + } + + if (self->user) { + g_signal_handlers_disconnect_by_data (self->user, self); + self->user = NULL; + } + + g_clear_object (&self->permission); + g_clear_object (&self->localed); + g_clear_object (&self->session); + g_clear_object (&self->locale_settings); + g_clear_object (&self->input_settings); + g_clear_object (&self->xkb_info); +#ifdef HAVE_IBUS + g_clear_object (&self->ibus); + g_clear_pointer (&self->ibus_engines, g_hash_table_destroy); +#endif + g_free (self->language); + g_free (self->region); + g_free (self->system_language); + g_free (self->system_region); + + chooser = g_object_get_data (G_OBJECT (self), "input-chooser"); + if (chooser) + gtk_widget_destroy (chooser); + + G_OBJECT_CLASS (cc_region_panel_parent_class)->finalize (object); +} + +static void +cc_region_panel_constructed (GObject *object) +{ + CcRegionPanel *self = CC_REGION_PANEL (object); + + G_OBJECT_CLASS (cc_region_panel_parent_class)->constructed (object); + + if (self->permission) + cc_shell_embed_widget_in_header (cc_panel_get_shell (CC_PANEL (object)), + GTK_WIDGET (self->login_button), + GTK_POS_RIGHT); +} + +static const char * +cc_region_panel_get_help_uri (CcPanel *panel) +{ + return "help:gnome-help/prefs-language"; +} + +static GFile * +get_needs_restart_file (void) +{ + g_autofree gchar *path = NULL; + + path = g_build_filename (g_get_user_runtime_dir (), + "gnome-control-center-region-needs-restart", + NULL); + return g_file_new_for_path (path); +} + +static void +restart_now (CcRegionPanel *self) +{ + g_autoptr(GFile) file = NULL; + + file = get_needs_restart_file (); + g_file_delete (file, NULL, NULL); + + g_dbus_proxy_call (self->session, + "Logout", + g_variant_new ("(u)", 0), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); +} + +static void +set_restart_notification_visible (CcRegionPanel *self, + const gchar *locale, + gboolean visible) +{ + locale_t new_locale; + locale_t current_locale; + g_autoptr(GFile) file = NULL; + g_autoptr(GFileOutputStream) output_stream = NULL; + g_autoptr(GError) error = NULL; + + if (locale) { + new_locale = newlocale (LC_MESSAGES_MASK, locale, (locale_t) 0); + if (new_locale == (locale_t) 0) + g_warning ("Failed to create locale %s: %s", locale, g_strerror (errno)); + else + current_locale = uselocale (new_locale); + } + + gtk_revealer_set_reveal_child (self->restart_revealer, visible); + + if (locale && new_locale != (locale_t) 0) { + uselocale (current_locale); + freelocale (new_locale); + } + + file = get_needs_restart_file (); + + if (!visible) { + g_file_delete (file, NULL, NULL); + return; + } + + output_stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error); + if (output_stream == NULL) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + g_warning ("Unable to create %s: %s", g_file_get_path (file), error->message); + } +} + +typedef struct { + CcRegionPanel *self; + int category; + gchar *target_locale; +} MaybeNotifyData; + +static void +maybe_notify_data_free (MaybeNotifyData *data) +{ + g_free (data->target_locale); + g_free (data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MaybeNotifyData, maybe_notify_data_free) + +static void +maybe_notify_finish (GObject *source, + GAsyncResult *res, + gpointer data) +{ + g_autoptr(MaybeNotifyData) mnd = data; + CcRegionPanel *self = mnd->self; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) retval = NULL; + g_autofree gchar *current_lang_code = NULL; + g_autofree gchar *current_country_code = NULL; + g_autofree gchar *target_lang_code = NULL; + g_autofree gchar *target_country_code = NULL; + const gchar *current_locale = NULL; + + retval = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!retval) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to get locale: %s\n", error->message); + return; + } + + g_variant_get (retval, "(&s)", ¤t_locale); + + if (!gnome_parse_locale (current_locale, + ¤t_lang_code, + ¤t_country_code, + NULL, + NULL)) + return; + + if (!gnome_parse_locale (mnd->target_locale, + &target_lang_code, + &target_country_code, + NULL, + NULL)) + return; + + if (g_str_equal (current_lang_code, target_lang_code) == FALSE || + g_str_equal (current_country_code, target_country_code) == FALSE) + set_restart_notification_visible (self, + mnd->category == LC_MESSAGES ? mnd->target_locale : NULL, + TRUE); + else + set_restart_notification_visible (self, + mnd->category == LC_MESSAGES ? mnd->target_locale : NULL, + FALSE); +} + +static void +maybe_notify (CcRegionPanel *self, + int category, + const gchar *target_locale) +{ + MaybeNotifyData *mnd; + + mnd = g_new0 (MaybeNotifyData, 1); + mnd->self = self; + mnd->category = category; + mnd->target_locale = g_strdup (target_locale); + + g_dbus_proxy_call (self->session, + "GetLocale", + g_variant_new ("(i)", category), + G_DBUS_CALL_FLAGS_NONE, + -1, + cc_panel_get_cancellable (CC_PANEL (self)), + maybe_notify_finish, + mnd); +} + +static void set_localed_locale (CcRegionPanel *self); + +static void +set_system_language (CcRegionPanel *self, + const gchar *language) +{ + if (g_strcmp0 (language, self->system_language) == 0) + return; + + g_free (self->system_language); + self->system_language = g_strdup (language); + + set_localed_locale (self); +} + +static void +update_language (CcRegionPanel *self, + const gchar *language) +{ + if (self->login) { + set_system_language (self, language); + } else { + if (g_strcmp0 (language, self->language) == 0) + return; + act_user_set_language (self->user, language); + if (self->login_auto_apply) + set_system_language (self, language); + maybe_notify (self, LC_MESSAGES, language); + } +} + +static void +language_response (CcRegionPanel *self, + gint response_id, + CcLanguageChooser *chooser) +{ + const gchar *language; + + if (response_id == GTK_RESPONSE_OK) { + language = cc_language_chooser_get_language (chooser); + update_language (self, language); + } + + gtk_widget_destroy (GTK_WIDGET (chooser)); +} + +static void +set_system_region (CcRegionPanel *self, + const gchar *region) +{ + if (g_strcmp0 (region, self->system_region) == 0) + return; + + g_free (self->system_region); + self->system_region = g_strdup (region); + + set_localed_locale (self); +} + +static void +update_region (CcRegionPanel *self, + const gchar *region) +{ + if (self->login) { + set_system_region (self, region); + } else { + if (g_strcmp0 (region, self->region) == 0) + return; + g_settings_set_string (self->locale_settings, KEY_REGION, region); + if (self->login_auto_apply) + set_system_region (self, region); + maybe_notify (self, LC_TIME, region); + } +} + +static void +format_response (CcRegionPanel *self, + gint response_id, + CcFormatChooser *chooser) +{ + const gchar *region; + + if (response_id == GTK_RESPONSE_OK) { + region = cc_format_chooser_get_region (chooser); + update_region (self, region); + } + + gtk_widget_destroy (GTK_WIDGET (chooser)); +} + +static const gchar * +get_effective_language (CcRegionPanel *self) +{ + if (self->login) + return self->system_language; + else + return self->language; +} + +static void +show_language_chooser (CcRegionPanel *self) +{ + CcLanguageChooser *chooser; + + chooser = cc_language_chooser_new (); + gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)))); + cc_language_chooser_set_language (chooser, get_effective_language (self)); + g_signal_connect_object (chooser, "response", + G_CALLBACK (language_response), self, G_CONNECT_SWAPPED); + gtk_window_present (GTK_WINDOW (chooser)); +} + +static const gchar * +get_effective_region (CcRegionPanel *self) +{ + const gchar *region; + + if (self->login) + region = self->system_region; + else + region = self->region; + + /* Region setting might be empty - show the language because + * that's what LC_TIME and others will effectively be when the + * user logs in again. */ + if (region == NULL || region[0] == '\0') + region = get_effective_language (self); + + return region; +} + +static void +show_region_chooser (CcRegionPanel *self) +{ + CcFormatChooser *chooser; + + chooser = cc_format_chooser_new (); + gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)))); + cc_format_chooser_set_region (chooser, get_effective_region (self)); + g_signal_connect_object (chooser, "response", + G_CALLBACK (format_response), self, G_CONNECT_SWAPPED); + gtk_window_present (GTK_WINDOW (chooser)); +} + +static void show_input_chooser (CcRegionPanel *self); + +static gboolean +permission_acquired (GPermission *permission, GAsyncResult *res, const gchar *action) +{ + g_autoptr(GError) error = NULL; + + if (!g_permission_acquire_finish (permission, res, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to acquire permission to %s: %s\n", error->message, action); + return FALSE; + } + + return FALSE; +} + +static void +choose_language_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + CcRegionPanel *self = user_data; + if (permission_acquired (G_PERMISSION (source), res, "choose language")) + show_language_chooser (self); +} + +static void +choose_region_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + CcRegionPanel *self = user_data; + if (permission_acquired (G_PERMISSION (source), res, "choose region")) + show_region_chooser (self); +} + +static void +activate_language_row (CcRegionPanel *self, + GtkListBoxRow *row) +{ + if (row == self->language_row) { + if (!self->login || g_permission_get_allowed (self->permission)) { + show_language_chooser (self); + } else if (g_permission_get_can_acquire (self->permission)) { + g_permission_acquire_async (self->permission, + cc_panel_get_cancellable (CC_PANEL (self)), + choose_language_permission_cb, + self); + } + } else if (row == self->formats_row) { + if (!self->login || g_permission_get_allowed (self->permission)) { + show_region_chooser (self); + } else if (g_permission_get_can_acquire (self->permission)) { + g_permission_acquire_async (self->permission, + cc_panel_get_cancellable (CC_PANEL (self)), + choose_region_permission_cb, + self); + } + } +} + +static void +update_region_label (CcRegionPanel *self) +{ + const gchar *region = get_effective_region (self); + g_autofree gchar *name = NULL; + + if (region) + name = gnome_get_country_from_locale (region, region); + + if (!name) + name = gnome_get_country_from_locale (DEFAULT_LOCALE, DEFAULT_LOCALE); + + gtk_label_set_label (self->formats_label, name); +} + +static void +update_region_from_setting (CcRegionPanel *self) +{ + g_free (self->region); + self->region = g_settings_get_string (self->locale_settings, KEY_REGION); + update_region_label (self); +} + +static void +update_language_label (CcRegionPanel *self) +{ + const gchar *language = get_effective_language (self); + g_autofree gchar *name = NULL; + + if (language) + name = gnome_get_language_from_locale (language, language); + + if (!name) + name = gnome_get_language_from_locale (DEFAULT_LOCALE, DEFAULT_LOCALE); + + gtk_label_set_label (self->language_label, name); + + /* Formats will change too if not explicitly set. */ + update_region_label (self); +} + +static void +update_language_from_user (CcRegionPanel *self) +{ + const gchar *language = NULL; + + if (act_user_is_loaded (self->user)) + language = act_user_get_language (self->user); + + if (language == NULL || *language == '\0') + language = setlocale (LC_MESSAGES, NULL); + + g_free (self->language); + self->language = g_strdup (language); + update_language_label (self); +} + +static void +setup_language_section (CcRegionPanel *self) +{ + self->user = act_user_manager_get_user_by_id (self->user_manager, getuid ()); + g_signal_connect_object (self->user, "notify::language", + G_CALLBACK (update_language_from_user), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->user, "notify::is-loaded", + G_CALLBACK (update_language_from_user), self, G_CONNECT_SWAPPED); + + self->locale_settings = g_settings_new (GNOME_SYSTEM_LOCALE_DIR); + g_signal_connect_object (self->locale_settings, "changed::" KEY_REGION, + G_CALLBACK (update_region_from_setting), self, G_CONNECT_SWAPPED); + + gtk_list_box_set_selection_mode (self->language_list, + GTK_SELECTION_NONE); + gtk_list_box_set_header_func (self->language_list, + cc_list_box_update_header_func, + NULL, NULL); + g_signal_connect_object (self->language_list, "row-activated", + G_CALLBACK (activate_language_row), self, G_CONNECT_SWAPPED); + + update_language_from_user (self); + update_region_from_setting (self); +} + +#ifdef HAVE_IBUS +static void +update_ibus_active_sources (CcRegionPanel *self) +{ + g_autoptr(GList) rows = NULL; + GList *l; + + rows = gtk_container_get_children (GTK_CONTAINER (self->input_list)); + for (l = rows; l; l = l->next) { + CcInputRow *row; + CcInputSourceIBus *source; + IBusEngineDesc *engine_desc; + + if (!CC_IS_INPUT_ROW (l->data)) + continue; + row = CC_INPUT_ROW (l->data); + + if (!CC_IS_INPUT_SOURCE_IBUS (cc_input_row_get_source (row))) + continue; + source = CC_INPUT_SOURCE_IBUS (cc_input_row_get_source (row)); + + engine_desc = g_hash_table_lookup (self->ibus_engines, cc_input_source_ibus_get_engine_name (source)); + if (engine_desc != NULL) + cc_input_source_ibus_set_engine_desc (source, engine_desc); + } +} + +static void +fetch_ibus_engines_result (GObject *object, + GAsyncResult *result, + CcRegionPanel *self) +{ + g_autoptr(GList) list = NULL; + GList *l; + g_autoptr(GError) error = NULL; + + list = ibus_bus_list_engines_async_finish (IBUS_BUS (object), result, &error); + if (!list && error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Couldn't finish IBus request: %s", error->message); + return; + } + + /* Maps engine ids to engine description objects */ + self->ibus_engines = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + + for (l = list; l; l = l->next) { + IBusEngineDesc *engine = l->data; + const gchar *engine_id = ibus_engine_desc_get_name (engine); + + if (g_str_has_prefix (engine_id, "xkb:")) + g_object_unref (engine); + else + g_hash_table_replace (self->ibus_engines, (gpointer)engine_id, engine); + } + + update_ibus_active_sources (self); +} + +static void +fetch_ibus_engines (CcRegionPanel *self) +{ + ibus_bus_list_engines_async (self->ibus, + -1, + cc_panel_get_cancellable (CC_PANEL (self)), + (GAsyncReadyCallback)fetch_ibus_engines_result, + self); + + /* We've got everything we needed, don't want to be called again. */ + g_signal_handlers_disconnect_by_func (self->ibus, fetch_ibus_engines, self); +} + +static void +maybe_start_ibus (void) +{ + /* IBus doesn't export API in the session bus. The only thing + * we have there is a well known name which we can use as a + * sure-fire way to activate it. + */ + g_bus_unwatch_name (g_bus_watch_name (G_BUS_TYPE_SESSION, + IBUS_SERVICE_IBUS, + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + NULL, + NULL, + NULL, + NULL)); +} + +#endif + +static void +row_settings_cb (CcRegionPanel *self, + CcInputRow *row) +{ + CcInputSourceIBus *source; + g_autoptr(GdkAppLaunchContext) ctx = NULL; + GDesktopAppInfo *app_info; + g_autoptr(GError) error = NULL; + + g_return_if_fail (CC_IS_INPUT_SOURCE_IBUS (cc_input_row_get_source (row))); + source = CC_INPUT_SOURCE_IBUS (cc_input_row_get_source (row)); + + app_info = cc_input_source_ibus_get_app_info (source); + if (app_info == NULL) + return; + + ctx = gdk_display_get_app_launch_context (gdk_display_get_default ()); + gdk_app_launch_context_set_timestamp (ctx, gtk_get_current_event_time ()); + + g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (ctx), + "IBUS_ENGINE_NAME", cc_input_source_ibus_get_engine_name (source)); + + if (!g_app_info_launch (G_APP_INFO (app_info), NULL, G_APP_LAUNCH_CONTEXT (ctx), &error)) + g_warning ("Failed to launch input source setup: %s", error->message); +} + +static void +row_layout_cb (CcRegionPanel *self, + CcInputRow *row) +{ + CcInputSource *source; + const gchar *layout, *layout_variant; + g_autofree gchar *commandline = NULL; + + source = cc_input_row_get_source (row); + + layout = cc_input_source_get_layout (source); + layout_variant = cc_input_source_get_layout_variant (source); + + if (layout_variant && layout_variant[0]) + commandline = g_strdup_printf ("gkbd-keyboard-display -l \"%s\t%s\"", + layout, layout_variant); + else + commandline = g_strdup_printf ("gkbd-keyboard-display -l %s", + layout); + + g_spawn_command_line_async (commandline, NULL); +} + +static void move_input (CcRegionPanel *self, CcInputRow *source, CcInputRow *dest); + +static void +row_moved_cb (CcRegionPanel *self, + CcInputRow *dest_row, + CcInputRow *row) +{ + move_input (self, row, dest_row); +} + +static void remove_input (CcRegionPanel *self, CcInputRow *row); + +static void +row_removed_cb (CcRegionPanel *self, + CcInputRow *row) +{ + remove_input (self, row); +} + +static void +update_input_rows (CcRegionPanel *self) +{ + g_autoptr(GList) rows = NULL; + GList *l; + guint n_input_rows = 0; + + rows = gtk_container_get_children (GTK_CONTAINER (self->input_list)); + for (l = rows; l; l = l->next) + if (CC_IS_INPUT_ROW (l->data)) + n_input_rows++; + for (l = rows; l; l = l->next) { + CcInputRow *row; + + if (!CC_IS_INPUT_ROW (l->data)) + continue; + row = CC_INPUT_ROW (l->data); + + cc_input_row_set_removable (row, n_input_rows > 1); + cc_input_row_set_draggable (row, n_input_rows > 1); + } +} + +static void +add_input_row (CcRegionPanel *self, CcInputSource *source) +{ + CcInputRow *row; + + gtk_widget_set_visible (GTK_WIDGET (self->no_inputs_row), FALSE); + + row = cc_input_row_new (source); + gtk_widget_show (GTK_WIDGET (row)); + gtk_size_group_add_widget (self->input_size_group, GTK_WIDGET (row)); + g_signal_connect_object (row, "show-settings", G_CALLBACK (row_settings_cb), self, G_CONNECT_SWAPPED); + g_signal_connect_object (row, "show-layout", G_CALLBACK (row_layout_cb), self, G_CONNECT_SWAPPED); + g_signal_connect_object (row, "move-row", G_CALLBACK (row_moved_cb), self, G_CONNECT_SWAPPED); + g_signal_connect_object (row, "remove-row", G_CALLBACK (row_removed_cb), self, G_CONNECT_SWAPPED); + gtk_list_box_insert (GTK_LIST_BOX (self->input_list), GTK_WIDGET (row), gtk_list_box_row_get_index (self->add_input_row)); + update_input_rows (self); +} + +static void +add_input_sources (CcRegionPanel *self, + GVariant *sources) +{ + GVariantIter iter; + const gchar *type, *id; + + if (g_variant_n_children (sources) < 1) { + gtk_widget_set_visible (GTK_WIDGET (self->no_inputs_row), TRUE); + return; + } + + g_variant_iter_init (&iter, sources); + while (g_variant_iter_next (&iter, "(&s&s)", &type, &id)) { + g_autoptr(CcInputSource) source = NULL; + + if (g_str_equal (type, "xkb")) { + source = CC_INPUT_SOURCE (cc_input_source_xkb_new_from_id (self->xkb_info, id)); + } else if (g_str_equal (type, "ibus")) { + source = CC_INPUT_SOURCE (cc_input_source_ibus_new (id)); +#ifdef HAVE_IBUS + if (self->ibus_engines) { + IBusEngineDesc *engine_desc = g_hash_table_lookup (self->ibus_engines, id); + if (engine_desc != NULL) + cc_input_source_ibus_set_engine_desc (CC_INPUT_SOURCE_IBUS (source), engine_desc); + } +#endif + } else { + g_warning ("Unhandled input source type '%s'", type); + continue; + } + + add_input_row (self, source); + } +} + +static void +add_input_sources_from_settings (CcRegionPanel *self) +{ + g_autoptr(GVariant) sources = NULL; + sources = g_settings_get_value (self->input_settings, "sources"); + add_input_sources (self, sources); +} + +static void +clear_input_sources (CcRegionPanel *self) +{ + g_autoptr(GList) list = NULL; + GList *l; + + list = gtk_container_get_children (GTK_CONTAINER (self->input_list)); + for (l = list; l; l = l->next) { + if (CC_IS_INPUT_ROW (l->data)) + gtk_container_remove (GTK_CONTAINER (self->input_list), GTK_WIDGET (l->data)); + } + + cc_list_box_adjust_scrolling (self->input_list); +} + +static CcInputRow * +get_row_by_source (CcRegionPanel *self, CcInputSource *source) +{ + g_autoptr(GList) list = NULL; + GList *l; + + list = gtk_container_get_children (GTK_CONTAINER (self->input_list)); + for (l = list; l; l = l->next) { + CcInputRow *row; + + if (!CC_IS_INPUT_ROW (l->data)) + continue; + row = CC_INPUT_ROW (l->data); + + if (cc_input_source_matches (source, cc_input_row_get_source (row))) + return row; + } + + return NULL; +} + +static void +input_sources_changed (CcRegionPanel *self, + const gchar *key) +{ + CcInputRow *selected; + g_autoptr(CcInputSource) source = NULL; + + selected = CC_INPUT_ROW (gtk_list_box_get_selected_row (self->input_list)); + if (selected) + source = g_object_ref (cc_input_row_get_source (selected)); + clear_input_sources (self); + add_input_sources_from_settings (self); + if (source != NULL) { + CcInputRow *row = get_row_by_source (self, source); + if (row != NULL) + gtk_list_box_select_row (GTK_LIST_BOX (self->input_list), GTK_LIST_BOX_ROW (row)); + } +} + +static void +set_input_settings (CcRegionPanel *self) +{ + GVariantBuilder builder; + g_autoptr(GList) list = NULL; + GList *l; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + list = gtk_container_get_children (GTK_CONTAINER (self->input_list)); + for (l = list; l; l = l->next) { + CcInputRow *row; + CcInputSource *source; + + if (!CC_IS_INPUT_ROW (l->data)) + continue; + row = CC_INPUT_ROW (l->data); + source = cc_input_row_get_source (row); + + if (CC_IS_INPUT_SOURCE_XKB (source)) { + g_autofree gchar *id = cc_input_source_xkb_get_id (CC_INPUT_SOURCE_XKB (source)); + g_variant_builder_add (&builder, "(ss)", "xkb", id); + } else if (CC_IS_INPUT_SOURCE_IBUS (source)) { + g_variant_builder_add (&builder, "(ss)", "ibus", + cc_input_source_ibus_get_engine_name (CC_INPUT_SOURCE_IBUS (source))); + } + } + + g_settings_set_value (self->input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); +} + +static void set_localed_input (CcRegionPanel *self); + +static void +update_input (CcRegionPanel *self) +{ + if (self->login) { + set_localed_input (self); + } else { + set_input_settings (self); + if (self->login_auto_apply) + set_localed_input (self); + } +} + +static void +show_input_chooser (CcRegionPanel *self) +{ + CcInputChooser *chooser; + + chooser = cc_input_chooser_new (self->login, + self->xkb_info, +#ifdef HAVE_IBUS + self->ibus_engines +#else + NULL +#endif + ); + gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)))); + + if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_OK) { + CcInputSource *source; + + source = cc_input_chooser_get_source (chooser); + if (source != NULL && get_row_by_source (self, source) == NULL) { + add_input_row (self, source); + update_input (self); + } + } + gtk_widget_destroy (GTK_WIDGET (chooser)); +} + +static void +add_input_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + CcRegionPanel *self = user_data; + if (permission_acquired (G_PERMISSION (source), res, "add input")) + show_input_chooser (self); +} + +static void +add_input (CcRegionPanel *self) +{ + if (!self->login) { + show_input_chooser (self); + } else if (g_permission_get_allowed (self->permission)) { + show_input_chooser (self); + } else if (g_permission_get_can_acquire (self->permission)) { + g_permission_acquire_async (self->permission, + cc_panel_get_cancellable (CC_PANEL (self)), + add_input_permission_cb, + self); + } +} + +static GtkWidget * +find_sibling (GtkContainer *container, GtkWidget *child) +{ + g_autoptr(GList) list = NULL; + GList *c, *l; + GtkWidget *sibling; + + list = gtk_container_get_children (container); + c = g_list_find (list, child); + + for (l = c->next; l; l = l->next) { + sibling = l->data; + if (gtk_widget_get_visible (sibling) && gtk_widget_get_child_visible (sibling)) + return sibling; + } + + for (l = c->prev; l; l = l->prev) { + sibling = l->data; + if (gtk_widget_get_visible (sibling) && gtk_widget_get_child_visible (sibling)) + return sibling; + } + + return NULL; +} + +static void +do_remove_input (CcRegionPanel *self, CcInputRow *row) +{ + GtkWidget *sibling; + + sibling = find_sibling (GTK_CONTAINER (self->input_list), GTK_WIDGET (row)); + gtk_container_remove (GTK_CONTAINER (self->input_list), GTK_WIDGET (row)); + gtk_list_box_select_row (self->input_list, GTK_LIST_BOX_ROW (sibling)); + + cc_list_box_adjust_scrolling (self->input_list); + + update_input (self); + update_input_rows (self); +} + +static void +remove_input_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + RowData *data = user_data; + if (permission_acquired (G_PERMISSION (source), res, "remove input")) + do_remove_input (data->panel, data->source); +} + +static void +remove_input (CcRegionPanel *self, CcInputRow *row) +{ + if (!self->login) { + do_remove_input (self, row); + } else if (g_permission_get_allowed (self->permission)) { + do_remove_input (self, row); + } else if (g_permission_get_can_acquire (self->permission)) { + g_permission_acquire_async (self->permission, + cc_panel_get_cancellable (CC_PANEL (self)), + remove_input_permission_cb, + row_data_new (self, row, NULL)); + } +} + +static void +do_move_input (CcRegionPanel *self, CcInputRow *source, CcInputRow *dest) +{ + gint dest_index; + + dest_index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (dest)); + + g_object_ref (source); + gtk_container_remove (GTK_CONTAINER (self->input_list), GTK_WIDGET (source)); + gtk_list_box_insert (self->input_list, GTK_WIDGET (source), dest_index); + g_object_unref (source); + + cc_list_box_adjust_scrolling (self->input_list); + + update_input (self); +} + +static void +move_input_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + RowData *data = user_data; + if (permission_acquired (G_PERMISSION (source), res, "move input")) + do_move_input (data->panel, data->source, data->dest); +} + +static void +move_input (CcRegionPanel *self, + CcInputRow *source, + CcInputRow *dest) +{ + if (!self->login) { + do_move_input (self, source, dest); + } else if (g_permission_get_allowed (self->permission)) { + do_move_input (self, source, dest); + } else if (g_permission_get_can_acquire (self->permission)) { + g_permission_acquire_async (self->permission, + cc_panel_get_cancellable (CC_PANEL (self)), + move_input_permission_cb, + row_data_new (self, source, dest)); + } +} + +static void +input_row_activated_cb (CcRegionPanel *self, GtkListBoxRow *row) +{ + if (row == self->add_input_row) { + add_input (self); + } +} + +static void +update_shortcut_label (GtkLabel *label, + const gchar *value) +{ + g_autofree gchar *text = NULL; + guint accel_key; + g_autofree guint *keycode = NULL; + GdkModifierType mods; + + if (value == NULL || *value == '\0') { + gtk_widget_hide (GTK_WIDGET (label)); + return; + } + + gtk_accelerator_parse_with_keycode (value, &accel_key, &keycode, &mods); + if (accel_key == 0 && keycode == NULL && mods == 0) { + g_warning ("Failed to parse keyboard shortcut: '%s'", value); + gtk_widget_hide (GTK_WIDGET (label)); + return; + } + + text = gtk_accelerator_get_label_with_keycode (gtk_widget_get_display (GTK_WIDGET (label)), accel_key, *keycode, mods); + gtk_label_set_text (label, text); +} + +static void +update_shortcuts (CcRegionPanel *self) +{ + g_auto(GStrv) previous = NULL; + g_auto(GStrv) next = NULL; + g_autofree gchar *previous_shortcut = NULL; + g_autoptr(GSettings) settings = NULL; + + settings = g_settings_new ("org.gnome.desktop.wm.keybindings"); + + previous = g_settings_get_strv (settings, "switch-input-source-backward"); + next = g_settings_get_strv (settings, "switch-input-source"); + + previous_shortcut = g_strdup (previous[0]); + + update_shortcut_label (self->previous_source, previous_shortcut); + update_shortcut_label (self->next_source, next[0]); +} + +static void +update_modifiers_shortcut (CcRegionPanel *self) +{ + g_auto(GStrv) options = NULL; + gchar **p; + g_autoptr(GSettings) settings = NULL; + g_autoptr(GnomeXkbInfo) xkb_info = NULL; + const gchar *text; + + xkb_info = gnome_xkb_info_new (); + settings = g_settings_new ("org.gnome.desktop.input-sources"); + options = g_settings_get_strv (settings, "xkb-options"); + + for (p = options; p && *p; ++p) + if (g_str_has_prefix (*p, "grp:")) + break; + + if (p && *p) { + text = gnome_xkb_info_description_for_option (xkb_info, "grp", *p); + gtk_label_set_text (self->alt_next_source, text); + } else { + gtk_widget_hide (GTK_WIDGET (self->alt_next_source)); + } +} + +static void +setup_input_section (CcRegionPanel *self) +{ + self->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + + self->xkb_info = gnome_xkb_info_new (); + +#ifdef HAVE_IBUS + ibus_init (); + if (!self->ibus) { + self->ibus = ibus_bus_new_async (); + if (ibus_bus_is_connected (self->ibus)) + fetch_ibus_engines (self); + else + g_signal_connect_object (self->ibus, "connected", + G_CALLBACK (fetch_ibus_engines), self, + G_CONNECT_SWAPPED); + } + maybe_start_ibus (); +#endif + + gtk_list_box_set_header_func (self->input_list, + cc_list_box_update_header_func, + NULL, NULL); + + g_signal_connect_object (self->input_settings, "changed::" KEY_INPUT_SOURCES, + G_CALLBACK (input_sources_changed), self, G_CONNECT_SWAPPED); + + add_input_sources_from_settings (self); + + g_object_bind_property (self->previous_source, "visible", + self->previous_source_label, "visible", + G_BINDING_DEFAULT); + g_object_bind_property (self->next_source, "visible", + self->next_source_label, "visible", + G_BINDING_DEFAULT); + + g_settings_bind (self->input_settings, "per-window", + self->per_window_source, "active", + G_SETTINGS_BIND_DEFAULT); + g_settings_bind (self->input_settings, "per-window", + self->same_source, "active", + G_SETTINGS_BIND_DEFAULT | G_SETTINGS_BIND_INVERT_BOOLEAN); + + update_shortcuts (self); + update_modifiers_shortcut (self); +} + +static void +on_localed_properties_changed (CcRegionPanel *self, + GVariant *changed_properties, + const gchar **invalidated_properties) +{ + g_autoptr(GVariant) v = NULL; + + v = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (self->localed), "Locale"); + if (v) { + g_autofree const gchar **strv = NULL; + gsize len; + gint i; + const gchar *lang, *messages, *time; + + strv = g_variant_get_strv (v, &len); + + lang = messages = time = NULL; + for (i = 0; strv[i]; i++) { + if (g_str_has_prefix (strv[i], "LANG=")) { + lang = strv[i] + strlen ("LANG="); + } else if (g_str_has_prefix (strv[i], "LC_MESSAGES=")) { + messages = strv[i] + strlen ("LC_MESSAGES="); + } else if (g_str_has_prefix (strv[i], "LC_TIME=")) { + time = strv[i] + strlen ("LC_TIME="); + } + } + if (!lang) { + lang = setlocale (LC_MESSAGES, NULL); + } + if (!messages) { + messages = lang; + } + g_free (self->system_language); + self->system_language = g_strdup (messages); + g_free (self->system_region); + self->system_region = g_strdup (time); + + update_language_label (self); + } +} + +static void +add_input_sources_from_localed (CcRegionPanel *self) +{ + g_autoptr(GVariant) layout_property = NULL; + g_autoptr(GVariant) variant_property = NULL; + const gchar *s; + g_auto(GStrv) layouts = NULL; + g_auto(GStrv) variants = NULL; + gint i, n; + + if (!self->localed) + return; + + layout_property = g_dbus_proxy_get_cached_property (self->localed, "X11Layout"); + if (layout_property) { + s = g_variant_get_string (layout_property, NULL); + layouts = g_strsplit (s, ",", -1); + } + + variant_property = g_dbus_proxy_get_cached_property (self->localed, "X11Variant"); + if (variant_property) { + s = g_variant_get_string (variant_property, NULL); + if (s && *s) + variants = g_strsplit (s, ",", -1); + } + + if (variants && variants[0]) + n = MIN (g_strv_length (layouts), g_strv_length (variants)); + else if (layouts && layouts[0]) + n = g_strv_length (layouts); + else + n = 0; + + for (i = 0; i < n && layouts[i][0]; i++) { + const char *variant = variants ? variants[i] : NULL; + g_autoptr(CcInputSourceXkb) source = cc_input_source_xkb_new (self->xkb_info, layouts[i], variant); + add_input_row (self, CC_INPUT_SOURCE (source)); + } + gtk_widget_set_visible (GTK_WIDGET (self->no_inputs_row), n == 0); +} + +static void +set_localed_locale (CcRegionPanel *self) +{ + g_autoptr(GVariantBuilder) b = NULL; + g_autofree gchar *lang_value = NULL; + + b = g_variant_builder_new (G_VARIANT_TYPE ("as")); + lang_value = g_strconcat ("LANG=", self->system_language, NULL); + g_variant_builder_add (b, "s", lang_value); + + if (self->system_region != NULL) { + g_autofree gchar *time_value = NULL; + g_autofree gchar *numeric_value = NULL; + g_autofree gchar *monetary_value = NULL; + g_autofree gchar *measurement_value = NULL; + g_autofree gchar *paper_value = NULL; + time_value = g_strconcat ("LC_TIME=", self->system_region, NULL); + g_variant_builder_add (b, "s", time_value); + numeric_value = g_strconcat ("LC_NUMERIC=", self->system_region, NULL); + g_variant_builder_add (b, "s", numeric_value); + monetary_value = g_strconcat ("LC_MONETARY=", self->system_region, NULL); + g_variant_builder_add (b, "s", monetary_value); + measurement_value = g_strconcat ("LC_MEASUREMENT=", self->system_region, NULL); + g_variant_builder_add (b, "s", measurement_value); + paper_value = g_strconcat ("LC_PAPER=", self->system_region, NULL); + g_variant_builder_add (b, "s", paper_value); + } + g_dbus_proxy_call (self->localed, + "SetLocale", + g_variant_new ("(asb)", b, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); +} + +static void +set_localed_input (CcRegionPanel *self) +{ + g_autoptr(GString) layouts = NULL; + g_autoptr(GString) variants = NULL; + g_autoptr(GList) list = NULL; + GList *li; + + layouts = g_string_new (""); + variants = g_string_new (""); + + list = gtk_container_get_children (GTK_CONTAINER (self->input_list)); + for (li = list; li; li = li->next) { + CcInputRow *row; + CcInputSourceXkb *source; + g_autofree gchar *id = NULL; + const gchar *l, *v; + + if (!CC_IS_INPUT_ROW (li->data)) + continue; + row = CC_INPUT_ROW (li->data); + + if (!CC_IS_INPUT_SOURCE_XKB (cc_input_row_get_source (row))) + continue; + source = CC_INPUT_SOURCE_XKB (cc_input_row_get_source (row)); + + id = cc_input_source_xkb_get_id (source); + if (gnome_xkb_info_get_layout_info (self->xkb_info, id, NULL, NULL, &l, &v)) { + if (layouts->str[0]) { + g_string_append_c (layouts, ','); + g_string_append_c (variants, ','); + } + g_string_append (layouts, l); + g_string_append (variants, v); + } + } + + g_dbus_proxy_call (self->localed, + "SetX11Keyboard", + g_variant_new ("(ssssbb)", layouts->str, "", variants->str, "", TRUE, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); +} + +static void +localed_proxy_ready (GObject *source, + GAsyncResult *res, + gpointer data) +{ + CcRegionPanel *self = data; + GDBusProxy *proxy; + g_autoptr(GError) error = NULL; + + proxy = g_dbus_proxy_new_finish (res, &error); + + if (!proxy) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to contact localed: %s\n", error->message); + return; + } + + self->localed = proxy; + + gtk_widget_set_sensitive (GTK_WIDGET (self->login_button), TRUE); + + g_signal_connect_object (self->localed, "g-properties-changed", + G_CALLBACK (on_localed_properties_changed), self, G_CONNECT_SWAPPED); + on_localed_properties_changed (self, NULL, NULL); +} + +static void +login_changed (CcRegionPanel *self) +{ + gboolean can_acquire; + + self->login = gtk_toggle_button_get_active (self->login_button); + gtk_widget_set_visible (GTK_WIDGET (self->login_label), self->login); + + can_acquire = self->permission && + (g_permission_get_allowed (self->permission) || + g_permission_get_can_acquire (self->permission)); + /* FIXME: insensitive doesn't look quite right for this */ + gtk_widget_set_sensitive (GTK_WIDGET (self->language_section_frame), !self->login || can_acquire); + gtk_widget_set_sensitive (GTK_WIDGET (self->input_section_box), !self->login || can_acquire); + + clear_input_sources (self); + if (self->login) + add_input_sources_from_localed (self); + else + add_input_sources_from_settings (self); + + update_language_label (self); +} + +static void +set_login_button_visibility (CcRegionPanel *self) +{ + gboolean has_multiple_users; + gboolean loaded; + + g_object_get (self->user_manager, "is-loaded", &loaded, NULL); + if (!loaded) + return; + + g_object_get (self->user_manager, "has-multiple-users", &has_multiple_users, NULL); + + self->login_auto_apply = !has_multiple_users && g_permission_get_allowed (self->permission); + gtk_widget_set_visible (GTK_WIDGET (self->login_button), !self->login_auto_apply); + + g_signal_handlers_disconnect_by_func (self->user_manager, set_login_button_visibility, self); +} + +static void +setup_login_button (CcRegionPanel *self) +{ + g_autoptr(GDBusConnection) bus = NULL; + gboolean loaded; + g_autoptr(GError) error = NULL; + + self->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-locale", NULL, NULL, &error); + if (self->permission == NULL) { + g_warning ("Could not get 'org.freedesktop.locale1.set-locale' permission: %s", + error->message); + return; + } + + bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); + g_dbus_proxy_new (bus, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + cc_panel_get_cancellable (CC_PANEL (self)), + (GAsyncReadyCallback) localed_proxy_ready, + self); + + self->login_button = GTK_TOGGLE_BUTTON (gtk_toggle_button_new_with_mnemonic (_("Login _Screen"))); + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self->login_button)), + "text-button"); + gtk_widget_set_valign (GTK_WIDGET (self->login_button), GTK_ALIGN_CENTER); + gtk_widget_set_visible (GTK_WIDGET (self->login_button), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET (self->login_button), FALSE); + g_signal_connect_object (self->login_button, "notify::active", + G_CALLBACK (login_changed), self, G_CONNECT_SWAPPED); + + g_object_get (self->user_manager, "is-loaded", &loaded, NULL); + if (loaded) + set_login_button_visibility (self); + else + g_signal_connect_object (self->user_manager, "notify::is-loaded", + G_CALLBACK (set_login_button_visibility), self, G_CONNECT_SWAPPED); +} + +static void +session_proxy_ready (GObject *source, + GAsyncResult *res, + gpointer data) +{ + CcRegionPanel *self = data; + GDBusProxy *proxy; + g_autoptr(GError) error = NULL; + + proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (!proxy) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to contact gnome-session: %s\n", error->message); + return; + } + + self->session = proxy; +} + +static void +cc_region_panel_class_init (CcRegionPanelClass * 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_region_panel_get_help_uri; + + object_class->constructed = cc_region_panel_constructed; + object_class->finalize = cc_region_panel_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/region/cc-region-panel.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, add_input_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, alt_next_source); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, formats_label); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, formats_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, input_list); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, input_section_box); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, input_size_group); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, login_label); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, language_label); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, language_list); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, language_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, language_section_frame); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, next_source); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, next_source_label); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, no_inputs_row); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, options_button); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, per_window_source); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, previous_source); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, previous_source_label); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, restart_button); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, restart_revealer); + gtk_widget_class_bind_template_child (widget_class, CcRegionPanel, same_source); + + gtk_widget_class_bind_template_callback (widget_class, input_row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, restart_now); +} + +static void +cc_region_panel_init (CcRegionPanel *self) +{ + g_autoptr(GFile) needs_restart_file = NULL; + + g_resources_register (cc_region_get_resource ()); + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->user_manager = act_user_manager_get_default (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + cc_panel_get_cancellable (CC_PANEL (self)), + session_proxy_ready, + self); + + setup_login_button (self); + setup_language_section (self); + setup_input_section (self); + + needs_restart_file = get_needs_restart_file (); + if (g_file_query_exists (needs_restart_file, NULL)) + set_restart_notification_visible (self, NULL, TRUE); +} |