diff options
-rw-r--r-- | panels/region/cc-input-list-box.c | 802 | ||||
-rw-r--r-- | panels/region/cc-input-list-box.h | 43 | ||||
-rw-r--r-- | panels/region/cc-input-list-box.ui | 50 | ||||
-rw-r--r-- | panels/region/cc-region-panel.c | 707 | ||||
-rw-r--r-- | panels/region/cc-region-panel.ui | 47 | ||||
-rw-r--r-- | panels/region/meson.build | 1 | ||||
-rw-r--r-- | panels/region/region.gresource.xml | 1 |
7 files changed, 915 insertions, 736 deletions
diff --git a/panels/region/cc-input-list-box.c b/panels/region/cc-input-list-box.c new file mode 100644 index 000000000..d42ad48e1 --- /dev/null +++ b/panels/region/cc-input-list-box.c @@ -0,0 +1,802 @@ +/* cc-input-list-box.c + * + * Copyright (C) 2010 Intel, Inc + * Copyright (C) 2020 System76, 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> + * Ian Douglas Scott <idscott@system76.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include <libgnome-desktop/gnome-xkb-info.h> + +#include "list-box-helper.h" +#include "cc-input-list-box.h" +#include "cc-input-chooser.h" +#include "cc-input-row.h" +#include "cc-input-source-ibus.h" +#include "cc-input-source-xkb.h" + +#ifdef HAVE_IBUS +#include <ibus.h> +#endif + +#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" +#define KEY_INPUT_SOURCES "sources" + +struct _CcInputListBox { + GtkListBox parent_instance; + + GtkListBoxRow *add_input_row; + GtkSizeGroup *input_size_group; + GtkListBoxRow *no_inputs_row; + + GCancellable *cancellable; + + gboolean login; + gboolean login_auto_apply; + GPermission *permission; + GDBusProxy *localed; + + GSettings *input_settings; + GnomeXkbInfo *xkb_info; +#ifdef HAVE_IBUS + IBusBus *ibus; + GHashTable *ibus_engines; +#endif +}; + +G_DEFINE_TYPE (CcInputListBox, cc_input_list_box, GTK_TYPE_LIST_BOX) + +typedef struct +{ + CcInputListBox *panel; + CcInputRow *source; + CcInputRow *dest; +} RowData; + +static RowData * +row_data_new (CcInputListBox *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 show_input_chooser (CcInputListBox *self); + +#ifdef HAVE_IBUS +static void +update_ibus_active_sources (CcInputListBox *self) +{ + g_autoptr(GList) rows = NULL; + GList *l; + + rows = gtk_container_get_children (GTK_CONTAINER (self)); + 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, + CcInputListBox *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 (CcInputListBox *self) +{ + ibus_bus_list_engines_async (self->ibus, + -1, + self->cancellable, + (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 (CcInputListBox *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 (CcInputListBox *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 (CcInputListBox *self, CcInputRow *source, CcInputRow *dest); + +static void +row_moved_cb (CcInputListBox *self, + CcInputRow *dest_row, + CcInputRow *row) +{ + move_input (self, row, dest_row); +} + +static void remove_input (CcInputListBox *self, CcInputRow *row); + +static void +row_removed_cb (CcInputListBox *self, + CcInputRow *row) +{ + remove_input (self, row); +} + +static void +update_input_rows (CcInputListBox *self) +{ + g_autoptr(GList) rows = NULL; + GList *l; + guint n_input_rows = 0; + + rows = gtk_container_get_children (GTK_CONTAINER (self)); + 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 (CcInputListBox *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), GTK_WIDGET (row), gtk_list_box_row_get_index (self->add_input_row)); + update_input_rows (self); +} + +static void +add_input_sources (CcInputListBox *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 (CcInputListBox *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 (CcInputListBox *self) +{ + g_autoptr(GList) list = NULL; + GList *l; + + list = gtk_container_get_children (GTK_CONTAINER (self)); + for (l = list; l; l = l->next) { + if (CC_IS_INPUT_ROW (l->data)) + gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (l->data)); + } + + cc_list_box_adjust_scrolling (GTK_LIST_BOX (self)); +} + +static CcInputRow * +get_row_by_source (CcInputListBox *self, CcInputSource *source) +{ + g_autoptr(GList) list = NULL; + GList *l; + + list = gtk_container_get_children (GTK_CONTAINER (self)); + 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 (CcInputListBox *self, + const gchar *key) +{ + CcInputRow *selected; + g_autoptr(CcInputSource) source = NULL; + + selected = CC_INPUT_ROW (gtk_list_box_get_selected_row (GTK_LIST_BOX (self))); + 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), GTK_LIST_BOX_ROW (row)); + } +} + +static void +set_input_settings (CcInputListBox *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)); + 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 (CcInputListBox *self); + +static void +update_input (CcInputListBox *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 (CcInputListBox *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)); +} + +// Duplicated from cc-region-panel.c +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 +add_input_permission_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + CcInputListBox *self = user_data; + if (permission_acquired (G_PERMISSION (source), res, "add input")) + show_input_chooser (self); +} + +static void +add_input (CcInputListBox *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, + self->cancellable, + 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 (CcInputListBox *self, CcInputRow *row) +{ + GtkWidget *sibling; + + sibling = find_sibling (GTK_CONTAINER (self), GTK_WIDGET (row)); + gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (row)); + gtk_list_box_select_row (GTK_LIST_BOX (self), GTK_LIST_BOX_ROW (sibling)); + + cc_list_box_adjust_scrolling (GTK_LIST_BOX(self)); + + 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 (CcInputListBox *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, + self->cancellable, + remove_input_permission_cb, + row_data_new (self, row, NULL)); + } +} + +static void +do_move_input (CcInputListBox *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), GTK_WIDGET (source)); + gtk_list_box_insert (GTK_LIST_BOX (self), GTK_WIDGET (source), dest_index); + g_object_unref (source); + + cc_list_box_adjust_scrolling (GTK_LIST_BOX (self)); + + 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 (CcInputListBox *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, + self->cancellable, + move_input_permission_cb, + row_data_new (self, source, dest)); + } +} + +static void +input_row_activated_cb (CcInputListBox *self, GtkListBoxRow *row) +{ + if (row == self->add_input_row) { + add_input (self); + } +} + +static void +add_input_sources_from_localed (CcInputListBox *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_input (CcInputListBox *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)); + 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 +cc_input_list_box_finalize (GObject *object) +{ + CcInputListBox *self = CC_INPUT_LIST_BOX (object); + + g_cancellable_cancel (self->cancellable); + + 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_OBJECT_CLASS (cc_input_list_box_parent_class)->finalize (object); +} + +static void +cc_input_list_box_class_init (CcInputListBoxClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = cc_input_list_box_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/region/cc-input-list-box.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcInputListBox, add_input_row); + gtk_widget_class_bind_template_child (widget_class, CcInputListBox, input_size_group); + gtk_widget_class_bind_template_child (widget_class, CcInputListBox, no_inputs_row); + + gtk_widget_class_bind_template_callback (widget_class, input_row_activated_cb); +} + +static void +cc_input_list_box_init (CcInputListBox *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->login = FALSE; + self->login_auto_apply = FALSE; + self->localed = NULL; + self->permission = NULL; + + self->cancellable = g_cancellable_new(); + + 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 (GTK_LIST_BOX (self), + 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); +} + +void +cc_input_list_box_set_login (CcInputListBox *self, gboolean login) +{ + self->login = login; + clear_input_sources (self); + if (login) + add_input_sources_from_localed (self); + else + add_input_sources_from_settings (self); +} + +void +cc_input_list_box_set_login_auto_apply (CcInputListBox *self, gboolean login_auto_apply) +{ + self->login_auto_apply = login_auto_apply; +} + +void +cc_input_list_box_set_localed (CcInputListBox *self, GDBusProxy *localed) +{ + self->localed = localed; +} + +void +cc_input_list_box_set_permission (CcInputListBox *self, GPermission *permission) +{ + self->permission = permission; +} diff --git a/panels/region/cc-input-list-box.h b/panels/region/cc-input-list-box.h new file mode 100644 index 000000000..223112e6e --- /dev/null +++ b/panels/region/cc-input-list-box.h @@ -0,0 +1,43 @@ +/* cc-input-list-box.c + * + * Copyright (C) 2010 Intel, Inc + * Copyright (C) 2020 System76, 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> + * Ian Douglas Scott <idscott@system76.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +G_BEGIN_DECLS + +#include <gtk/gtk.h> + +#define CC_TYPE_INPUT_LIST_BOX (cc_input_list_box_get_type ()) +G_DECLARE_FINAL_TYPE (CcInputListBox, cc_input_list_box, CC, INPUT_LIST_BOX, GtkListBox) + +void cc_input_list_box_set_login (CcInputListBox *box, + gboolean login); +void cc_input_list_box_set_login_auto_apply (CcInputListBox *box, + gboolean auto_apply); +void cc_input_list_box_set_localed (CcInputListBox *box, + GDBusProxy *localed); +void cc_input_list_box_set_permission (CcInputListBox *box, + GPermission *permission); + +G_END_DECLS diff --git a/panels/region/cc-input-list-box.ui b/panels/region/cc-input-list-box.ui new file mode 100644 index 000000000..3df59dee9 --- /dev/null +++ b/panels/region/cc-input-list-box.ui @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CcInputListBox" parent="GtkListBox"> + <property name="can_focus">True</property> + <property name="selection-mode">none</property> + <signal name="row_activated" handler="input_row_activated_cb" object="CcInputListBox" swapped="yes"/> + <child> + <object class="GtkListBoxRow" id="no_inputs_row"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="selectable">False</property> + <property name="activatable">False</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="margin_bottom">8</property> + <property name="margin_top">8</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">No input source selected</property> + </object> + </child> + </object> + </child> + <child> + <object class="GtkListBoxRow" id="add_input_row"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="selectable">False</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="height_request">50</property> + <property name="margin_start">12</property> + <property name="margin_end">12</property> + <property name="icon-name">list-add-symbolic</property> + </object> + </child> + </object> + </child> + </template> + <object class="GtkSizeGroup" id="input_size_group"> + <property name="mode">vertical</property> + <widgets> + <widget name="no_inputs_row"/> + </widgets> + </object> +</interface>
\ No newline at end of file diff --git a/panels/region/cc-region-panel.c b/panels/region/cc-region-panel.c index 9b59b23c4..1049228a6 100644 --- a/panels/region/cc-region-panel.c +++ b/panels/region/cc-region-panel.c @@ -32,10 +32,7 @@ #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-input-list-box.h" #include "cc-common-language.h" @@ -43,15 +40,9 @@ #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" @@ -60,11 +51,10 @@ struct _CcRegionPanel { CcPanel parent_instance; - GtkListBoxRow *add_input_row; GtkLabel *alt_next_source; GtkLabel *formats_label; GtkListBoxRow *formats_row; - GtkListBox *input_list; + CcInputListBox *input_list; GtkBox *input_section_box; GtkSizeGroup *input_size_group; GtkToggleButton *login_button; @@ -75,7 +65,6 @@ struct _CcRegionPanel { 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; @@ -98,45 +87,10 @@ struct _CcRegionPanel { 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) { @@ -157,12 +111,6 @@ cc_region_panel_finalize (GObject *object) 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); @@ -490,8 +438,6 @@ show_region_chooser (CcRegionPanel *self) gtk_window_present (GTK_WINDOW (chooser)); } -static void show_input_chooser (CcRegionPanel *self); - static gboolean permission_acquired (GPermission *permission, GAsyncResult *res, const gchar *action) { @@ -629,511 +575,6 @@ setup_language_section (CcRegionPanel *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) @@ -1204,34 +645,9 @@ update_modifiers_shortcut (CcRegionPanel *self) } static void -setup_input_section (CcRegionPanel *self) +setup_input_shortcuts (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); + GSettings *input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); g_object_bind_property (self->previous_source, "visible", self->previous_source_label, "visible", @@ -1240,15 +656,13 @@ setup_input_section (CcRegionPanel *self) self->next_source_label, "visible", G_BINDING_DEFAULT); - g_settings_bind (self->input_settings, "per-window", + g_settings_bind (input_settings, "per-window", self->per_window_source, "active", G_SETTINGS_BIND_DEFAULT); - g_settings_bind (self->input_settings, "per-window", + g_settings_bind (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 @@ -1293,47 +707,6 @@ on_localed_properties_changed (CcRegionPanel *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; @@ -1368,50 +741,6 @@ set_localed_locale (CcRegionPanel *self) } 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) @@ -1429,6 +758,7 @@ localed_proxy_ready (GObject *source, } self->localed = proxy; + cc_input_list_box_set_localed (self->input_list, self->localed); gtk_widget_set_sensitive (GTK_WIDGET (self->login_button), TRUE); @@ -1452,12 +782,6 @@ login_changed (CcRegionPanel *self) 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); } @@ -1474,6 +798,7 @@ set_login_button_visibility (CcRegionPanel *self) 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); + cc_input_list_box_set_login_auto_apply (self->input_list, self->login_auto_apply); 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); @@ -1492,6 +817,7 @@ setup_login_button (CcRegionPanel *self) error->message); return; } + cc_input_list_box_set_permission (self->input_list, self->permission); bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); g_dbus_proxy_new (bus, @@ -1553,15 +879,16 @@ cc_region_panel_class_init (CcRegionPanelClass * klass) object_class->constructed = cc_region_panel_constructed; object_class->finalize = cc_region_panel_finalize; + // TODO better way? + CC_TYPE_INPUT_LIST_BOX; + 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); @@ -1569,7 +896,6 @@ cc_region_panel_class_init (CcRegionPanelClass * klass) 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); @@ -1578,7 +904,6 @@ cc_region_panel_class_init (CcRegionPanelClass * klass) 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); } @@ -1605,9 +930,11 @@ cc_region_panel_init (CcRegionPanel *self) setup_login_button (self); setup_language_section (self); - setup_input_section (self); + setup_input_shortcuts (self); + update_shortcuts (self); + update_modifiers_shortcut (self); needs_restart_file = get_needs_restart_file (); if (g_file_query_exists (needs_restart_file, NULL)) set_restart_notification_visible (self, NULL, TRUE); -} +}
\ No newline at end of file diff --git a/panels/region/cc-region-panel.ui b/panels/region/cc-region-panel.ui index b77914267..d7c70aa7c 100644 --- a/panels/region/cc-region-panel.ui +++ b/panels/region/cc-region-panel.ui @@ -243,47 +243,8 @@ <property name="label_xalign">0</property> <property name="shadow_type">in</property> <child> - <object class="GtkListBox" id="input_list"> + <object class="CcInputListBox" id="input_list"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="selection-mode">none</property> - <signal name="row_activated" handler="input_row_activated_cb" object="CcRegionPanel" swapped="yes"/> - <accessibility> - <relation target="input_heading_label" type="labelled-by"/> - </accessibility> - <child> - <object class="GtkListBoxRow" id="no_inputs_row"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="selectable">False</property> - <property name="activatable">False</property> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">No input source selected</property> - </object> - </child> - </object> - </child> - <child> - <object class="GtkListBoxRow" id="add_input_row"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="selectable">False</property> - <child> - <object class="GtkImage"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="hexpand">True</property> - <property name="height_request">50</property> - <property name="margin_start">12</property> - <property name="margin_end">12</property> - <property name="icon-name">list-add-symbolic</property> - </object> - </child> - </object> - </child> </object> </child> </object> @@ -506,10 +467,4 @@ </object> </child> </object> - <object class="GtkSizeGroup" id="input_size_group"> - <property name="mode">vertical</property> - <widgets> - <widget name="no_inputs_row"/> - </widgets> - </object> </interface> diff --git a/panels/region/meson.build b/panels/region/meson.build index e709eb620..da3d14087 100644 --- a/panels/region/meson.build +++ b/panels/region/meson.build @@ -25,6 +25,7 @@ sources = files( 'cc-input-row.c', 'cc-input-source.c', 'cc-input-source-ibus.c', + 'cc-input-list-box.c', 'cc-input-source-xkb.c', ) diff --git a/panels/region/region.gresource.xml b/panels/region/region.gresource.xml index 5b57ddeb5..411c3dcf6 100644 --- a/panels/region/region.gresource.xml +++ b/panels/region/region.gresource.xml @@ -4,6 +4,7 @@ <file preprocess="xml-stripblanks">cc-format-chooser.ui</file> <file preprocess="xml-stripblanks">cc-input-chooser.ui</file> <file preprocess="xml-stripblanks">cc-input-row.ui</file> + <file preprocess="xml-stripblanks">cc-input-list-box.ui</file> <file preprocess="xml-stripblanks">cc-region-panel.ui</file> </gresource> <gresource prefix="/org/gnome/ControlCenter/icons/scalable/actions"> |