From 0a78c3504e34927c9864d489fafd3e8025e21d5c Mon Sep 17 00:00:00 2001 From: Rui Matos Date: Tue, 22 May 2012 19:14:19 +0200 Subject: [PATCH] region: Add XKB input sources We allow the user to choose from all XKB layouts installed and keep a user picked list in gsettings. https://bugzilla.gnome.org/show_bug.cgi?id=662489 --- configure.ac | 3 +- .../gnome-region-panel-input-chooser.ui | 5 + panels/region/gnome-region-panel-input.c | 250 ++++++++++++++++-- 3 files changed, 238 insertions(+), 20 deletions(-) diff --git a/configure.ac b/configure.ac index 0318ae232..3ec724291 100644 --- a/configure.ac +++ b/configure.ac @@ -115,7 +115,8 @@ PKG_CHECK_MODULES(COLOR_PANEL, $COMMON_MODULES colord >= 0.1.8) PKG_CHECK_MODULES(PRINTERS_PANEL, $COMMON_MODULES polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION) PKG_CHECK_MODULES(REGION_PANEL, $COMMON_MODULES - polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION) + polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION + gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION) PKG_CHECK_MODULES(SCREEN_PANEL, $COMMON_MODULES) PKG_CHECK_MODULES(SOUND_PANEL, $COMMON_MODULES libxml-2.0 libcanberra-gtk3 >= $CANBERRA_REQUIRED_VERSION diff --git a/panels/region/gnome-region-panel-input-chooser.ui b/panels/region/gnome-region-panel-input-chooser.ui index a7d30aa06..c81c856ec 100644 --- a/panels/region/gnome-region-panel-input-chooser.ui +++ b/panels/region/gnome-region-panel-input-chooser.ui @@ -3,6 +3,11 @@ + + + + + diff --git a/panels/region/gnome-region-panel-input.c b/panels/region/gnome-region-panel-input.c index d8a68151b..9e9329338 100644 --- a/panels/region/gnome-region-panel-input.c +++ b/panels/region/gnome-region-panel-input.c @@ -26,15 +26,167 @@ #include #include +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include + #include "gnome-region-panel-input.h" #define WID(s) GTK_WIDGET(gtk_builder_get_object (builder, s)) -static GtkWidget *input_chooser_new (GtkWindow *main_window); +#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" + +#define KEY_INPUT_SOURCES "sources" + +#define INPUT_SOURCE_TYPE_XKB "xkb" + +enum { + NAME_COLUMN, + TYPE_COLUMN, + ID_COLUMN, + N_COLUMNS +}; + +static GSettings *input_sources_settings = NULL; +static gulong input_sources_settings_changed_id = 0; +static GnomeXkbInfo *xkb_info = NULL; + +static GtkWidget *input_chooser_new (GtkWindow *main_window, + GtkListStore *active_sources); static gboolean input_chooser_get_selected (GtkWidget *chooser, GtkTreeModel **model, GtkTreeIter *iter); +static gboolean +add_source_to_table (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GHashTable *hash = data; + gchar *type; + gchar *id; + + gtk_tree_model_get (model, iter, + TYPE_COLUMN, &type, + ID_COLUMN, &id, + -1); + + g_hash_table_add (hash, g_strconcat (type, id, NULL)); + + g_free (type); + g_free (id); + + return FALSE; +} + +static void +populate_model (GtkListStore *store, + GtkListStore *active_sources_store) +{ + GHashTable *active_sources_table; + GtkTreeIter iter; + const gchar *name; + GList *sources, *tmp; + gchar *source_id = NULL; + + active_sources_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + gtk_tree_model_foreach (GTK_TREE_MODEL (active_sources_store), + add_source_to_table, + active_sources_table); + + sources = gnome_xkb_info_get_all_layouts (xkb_info); + + for (tmp = sources; tmp; tmp = tmp->next) + { + g_free (source_id); + source_id = g_strconcat (INPUT_SOURCE_TYPE_XKB, tmp->data, NULL); + + if (g_hash_table_contains (active_sources_table, source_id)) + continue; + + gnome_xkb_info_get_layout_info (xkb_info, (const gchar *)tmp->data, + &name, NULL, NULL, NULL); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + NAME_COLUMN, name, + TYPE_COLUMN, INPUT_SOURCE_TYPE_XKB, + ID_COLUMN, tmp->data, + -1); + } + g_free (source_id); + + g_list_free (sources); + g_hash_table_destroy (active_sources_table); +} + +static void +populate_with_active_sources (GtkListStore *store) +{ + GVariant *sources; + GVariantIter iter; + const gchar *name; + const gchar *type; + const gchar *id; + GtkTreeIter tree_iter; + + sources = g_settings_get_value (input_sources_settings, KEY_INPUT_SOURCES); + + g_variant_iter_init (&iter, sources); + while (g_variant_iter_next (&iter, "(&s&s)", &type, &id)) + { + if (!g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) + { + g_warning ("Unknown input source type '%s'", type); + continue; + } + + gnome_xkb_info_get_layout_info (xkb_info, id, &name, NULL, NULL, NULL); + + if (!name) + { + g_warning ("Couldn't find XKB input source '%s'", id); + continue; + } + + gtk_list_store_append (store, &tree_iter); + gtk_list_store_set (store, &tree_iter, + NAME_COLUMN, name, + TYPE_COLUMN, type, + ID_COLUMN, id, + -1); + } + + g_variant_unref (sources); +} + +static void +update_configuration (GtkTreeModel *model) +{ + GtkTreeIter iter; + gchar *type; + gchar *id; + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + + gtk_tree_model_get_iter_first (model, &iter); + do { + gtk_tree_model_get (model, &iter, + TYPE_COLUMN, &type, + ID_COLUMN, &id, + -1); + g_variant_builder_add (&builder, "(ss)", type, id); + g_free (type); + g_free (id); + } while (gtk_tree_model_iter_next (model, &iter)); + + g_signal_handler_block (input_sources_settings, input_sources_settings_changed_id); + g_settings_set_value (input_sources_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); + g_signal_handler_unblock (input_sources_settings, input_sources_settings_changed_id); +} + static gboolean get_selected_iter (GtkBuilder *builder, GtkTreeModel **model, @@ -122,9 +274,13 @@ chooser_response (GtkWidget *chooser, gint response_id, gpointer data) GtkListStore *my_model; GtkTreeIter my_iter; gchar *name; + gchar *type; + gchar *id; gtk_tree_model_get (model, &iter, - 0, &name, + NAME_COLUMN, &name, + TYPE_COLUMN, &type, + ID_COLUMN, &id, -1); my_tv = GTK_TREE_VIEW (WID ("active_input_sources")); @@ -133,14 +289,18 @@ chooser_response (GtkWidget *chooser, gint response_id, gpointer data) gtk_list_store_append (my_model, &my_iter); gtk_list_store_set (my_model, &my_iter, - 0, name, + NAME_COLUMN, name, + TYPE_COLUMN, type, + ID_COLUMN, id, -1); - g_free (name); + g_free (type); + g_free (id); gtk_tree_selection_select_iter (gtk_tree_view_get_selection (my_tv), &my_iter); update_button_sensitivity (builder); + update_configuration (GTK_TREE_MODEL (my_model)); } else { @@ -157,11 +317,16 @@ add_input (GtkButton *button, gpointer data) GtkBuilder *builder = data; GtkWidget *chooser; GtkWidget *toplevel; + GtkWidget *treeview; + GtkListStore *active_sources; g_debug ("add an input source"); toplevel = gtk_widget_get_toplevel (WID ("region_notebook")); - chooser = input_chooser_new (GTK_WINDOW (toplevel)); + treeview = WID ("active_input_sources"); + active_sources = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview))); + + chooser = input_chooser_new (GTK_WINDOW (toplevel), active_sources); g_signal_connect (chooser, "response", G_CALLBACK (chooser_response), builder); } @@ -190,6 +355,7 @@ remove_selected_input (GtkButton *button, gpointer data) gtk_tree_path_free (path); update_button_sensitivity (builder); + update_configuration (model); } static void @@ -216,6 +382,7 @@ move_selected_input_up (GtkButton *button, gpointer data) gtk_tree_path_free (path); update_button_sensitivity (builder); + update_configuration (model); } static void @@ -241,6 +408,7 @@ move_selected_input_down (GtkButton *button, gpointer data) gtk_tree_path_free (path); update_button_sensitivity (builder); + update_configuration (model); } static void @@ -249,8 +417,10 @@ show_selected_layout (GtkButton *button, gpointer data) GtkBuilder *builder = data; GtkTreeModel *model; GtkTreeIter iter; - gchar *layout; + gchar *id; gchar *kbd_viewer_args; + const gchar *xkb_layout; + const gchar *xkb_variant; g_debug ("show selected layout"); @@ -258,14 +428,29 @@ show_selected_layout (GtkButton *button, gpointer data) return; gtk_tree_model_get (model, &iter, - 0, &layout, + ID_COLUMN, &id, -1); - kbd_viewer_args = g_strdup_printf ("gkbd-keyboard-display -l %s", layout); + gnome_xkb_info_get_layout_info (xkb_info, id, NULL, NULL, &xkb_layout, &xkb_variant); + + if (!xkb_layout || !xkb_layout[0]) + { + g_warning ("Couldn't find XKB input source '%s'", id); + goto exit; + } + + if (xkb_variant[0]) + kbd_viewer_args = g_strdup_printf ("gkbd-keyboard-display -l \"%s\t%s\"", + xkb_layout, xkb_variant); + else + kbd_viewer_args = g_strdup_printf ("gkbd-keyboard-display -l %s", + xkb_layout); + g_spawn_command_line_async (kbd_viewer_args, NULL); g_free (kbd_viewer_args); - g_free (layout); + exit: + g_free (id); } static gboolean @@ -286,6 +471,20 @@ go_to_shortcuts (GtkLinkButton *button, return TRUE; } +static void +input_sources_changed (GSettings *settings, + gchar *key, + GtkBuilder *builder) +{ + GtkWidget *treeview; + GtkTreeModel *store; + + treeview = WID("active_input_sources"); + store = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + gtk_list_store_clear (GTK_LIST_STORE (store)); + populate_with_active_sources (GTK_LIST_STORE (store)); +} + void setup_input_tabs (GtkBuilder *builder, CcRegionPanel *panel) @@ -299,15 +498,20 @@ setup_input_tabs (GtkBuilder *builder, gchar *next = NULL; GtkWidget *label; + input_sources_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + xkb_info = gnome_xkb_info_new (); + /* set up the list of active inputs */ treeview = WID("active_input_sources"); column = gtk_tree_view_column_new (); cell = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, cell, TRUE); - gtk_tree_view_column_add_attribute (column, cell, "text", 0); + gtk_tree_view_column_add_attribute (column, cell, "text", NAME_COLUMN); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); - store = gtk_list_store_new (1, G_TYPE_STRING); + store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + populate_with_active_sources (store); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); g_signal_connect_swapped (selection, "changed", @@ -343,6 +547,11 @@ setup_input_tabs (GtkBuilder *builder, g_signal_connect (WID("jump-to-shortcuts"), "activate-link", G_CALLBACK (go_to_shortcuts), panel); + + input_sources_settings_changed_id = g_signal_connect (G_OBJECT (input_sources_settings), + "changed::" KEY_INPUT_SOURCES, + G_CALLBACK (input_sources_changed), + builder); } static void @@ -444,7 +653,7 @@ filter_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { - gchar *desc = NULL; + gchar *name = NULL; gchar **pattern; gboolean rv = TRUE; @@ -452,13 +661,13 @@ filter_func (GtkTreeModel *model, return TRUE; gtk_tree_model_get (model, iter, - 0, &desc, + NAME_COLUMN, &name, -1); pattern = search_pattern_list; do { gboolean is_pattern_found = FALSE; - gchar *udesc = g_utf8_strup (desc, -1); + gchar *udesc = g_utf8_strup (name, -1); if (udesc != NULL && g_strstr_len (udesc, -1, *pattern)) { is_pattern_found = TRUE; @@ -473,13 +682,14 @@ filter_func (GtkTreeModel *model, } while (*++pattern != NULL); - g_free (desc); + g_free (name); return rv; } static GtkWidget * -input_chooser_new (GtkWindow *main_window) +input_chooser_new (GtkWindow *main_window, + GtkListStore *active_sources) { GtkBuilder *builder; GtkWidget *chooser; @@ -504,9 +714,9 @@ input_chooser_new (GtkWindow *main_window) g_object_set_data (G_OBJECT (chooser), "filtered_input_source_list", filtered_list); visible_column = - gtk_tree_view_column_new_with_attributes ("Layout", + gtk_tree_view_column_new_with_attributes ("Input Sources", gtk_cell_renderer_text_new (), - "text", 0, + "text", NAME_COLUMN, NULL); gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window); @@ -528,8 +738,10 @@ input_chooser_new (GtkWindow *main_window) filtered_model = GTK_TREE_MODEL_FILTER (gtk_builder_get_object (builder, "filtered_input_source_model")); model = GTK_LIST_STORE (gtk_builder_get_object (builder, "input_source_model")); + populate_model (model, active_sources); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), - 0, GTK_SORT_ASCENDING); + NAME_COLUMN, GTK_SORT_ASCENDING); gtk_tree_model_filter_set_visible_func (filtered_model, filter_func,