diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:47:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:47:04 +0000 |
commit | e05fb7b3e36c052baf0dd607ddeb22c0a2b5cbde (patch) | |
tree | 7a27d70e96502edf2b5576d3ca403f1b8512f55b /gnome-initial-setup/pages/keyboard | |
parent | Initial commit. (diff) | |
download | gnome-initial-setup-upstream/43.2.tar.xz gnome-initial-setup-upstream/43.2.zip |
Adding upstream version 43.2.upstream/43.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gnome-initial-setup/pages/keyboard')
10 files changed, 1710 insertions, 0 deletions
diff --git a/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c new file mode 100644 index 0000000..424c69e --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 Red Hat, 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/>. + */ + +#include <config.h> + +#ifdef HAVE_IBUS +#include "cc-ibus-utils.h" + +gchar * +engine_get_display_name (IBusEngineDesc *engine_desc) +{ + const gchar *name; + const gchar *language_code; + const gchar *language; + const gchar *textdomain; + gchar *display_name; + + name = ibus_engine_desc_get_longname (engine_desc); + language_code = ibus_engine_desc_get_language (engine_desc); + language = ibus_get_language_name (language_code); + textdomain = ibus_engine_desc_get_textdomain (engine_desc); + if (*textdomain != '\0' && *name != '\0') + name = g_dgettext (textdomain, name); + display_name = g_strdup_printf ("%s (%s)", language, name); + + return display_name; +} + +#endif /* HAVE_IBUS */ diff --git a/gnome-initial-setup/pages/keyboard/cc-ibus-utils.h b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.h new file mode 100644 index 0000000..da3d996 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/cc-ibus-utils.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 Red Hat, 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/>. + */ + +#ifndef __GIS_IBUS_UTILS_H__ +#define __GIS_IBUS_UTILS_H__ + +#include <ibus.h> + +G_BEGIN_DECLS + +gchar *engine_get_display_name (IBusEngineDesc *engine_desc); + +G_END_DECLS + +#endif /* __GIS_IBUS_UTILS_H__ */ diff --git a/gnome-initial-setup/pages/keyboard/cc-input-chooser.c b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c new file mode 100644 index 0000000..efba249 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/cc-input-chooser.c @@ -0,0 +1,883 @@ +/* + * Copyright (C) 2013 Red Hat, 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/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + * Matthias Clasen <mclasen@redhat.com> + */ + +#include "config.h" +#include "cc-input-chooser.h" + +#include <locale.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include <gtk/gtk.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> +#include "cc-ibus-utils.h" +#endif + +#include "cc-common-language.h" + +#include <glib-object.h> + +#define INPUT_SOURCE_TYPE_XKB "xkb" +#define INPUT_SOURCE_TYPE_IBUS "ibus" + +#define MIN_ROWS 5 + +struct _CcInputChooserPrivate +{ + GtkWidget *filter_entry; + GtkWidget *input_list; + GHashTable *inputs; + + GtkWidget *no_results; + GtkWidget *more_item; + + gboolean showing_extra; + gchar *locale; + gchar *id; + gchar *type; + GnomeXkbInfo *xkb_info; +#ifdef HAVE_IBUS + IBusBus *ibus; + GHashTable *ibus_engines; + GCancellable *ibus_cancellable; +#endif +}; +typedef struct _CcInputChooserPrivate CcInputChooserPrivate; +G_DEFINE_TYPE_WITH_PRIVATE (CcInputChooser, cc_input_chooser, GTK_TYPE_BOX); + +enum { + PROP_0, + PROP_SHOWING_EXTRA, + PROP_LAST +}; + +static GParamSpec *obj_props[PROP_LAST]; + +enum { + CHANGED, + CONFIRM, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + GtkWidget *box; + GtkWidget *label; + GtkWidget *checkmark; + + gchar *id; + gchar *type; + gchar *name; + gboolean is_extra; +} InputWidget; + +static InputWidget * +get_input_widget (GtkWidget *widget) +{ + return g_object_get_data (G_OBJECT (widget), "input-widget"); +} + +static GtkWidget * +padded_label_new (char *text) +{ + GtkWidget *widget; + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); + gtk_widget_set_halign (widget, GTK_ALIGN_CENTER); + gtk_widget_set_margin_top (widget, 10); + gtk_widget_set_margin_bottom (widget, 10); + gtk_box_append (GTK_BOX (widget), gtk_label_new (text)); + return widget; +} + +static void +input_widget_free (gpointer data) +{ + InputWidget *widget = data; + + g_free (widget->id); + g_free (widget->type); + g_free (widget->name); + g_free (widget); +} + +static gboolean +get_layout (CcInputChooser *chooser, + const gchar *type, + const gchar *id, + const gchar **layout, + const gchar **variant) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + + if (g_strcmp0 (type, INPUT_SOURCE_TYPE_XKB) == 0) { + gnome_xkb_info_get_layout_info (priv->xkb_info, + id, NULL, NULL, + layout, variant); + return TRUE; + } +#ifdef HAVE_IBUS + if (g_strcmp0 (type, INPUT_SOURCE_TYPE_IBUS) == 0) { + IBusEngineDesc *engine_desc = NULL; + + if (priv->ibus_engines) + engine_desc = g_hash_table_lookup (priv->ibus_engines, id); + + if (!engine_desc) + return FALSE; + + *layout = ibus_engine_desc_get_layout (engine_desc); + *variant = ""; + return TRUE; + } +#endif + return FALSE; +} + +static gboolean +preview_cb (GtkLabel *label, + const gchar *uri, + CcInputChooser *chooser) +{ + GtkWidget *row; + InputWidget *widget; + const gchar *layout; + const gchar *variant; + gchar *commandline; + + row = gtk_widget_get_parent (GTK_WIDGET (label)); + widget = get_input_widget (row); + + if (!get_layout (chooser, widget->type, widget->id, &layout, &variant)) + return TRUE; + + if (variant[0]) + commandline = g_strdup_printf ("gkbd-keyboard-display -l \"%s\t%s\"", layout, variant); + else + commandline = g_strdup_printf ("gkbd-keyboard-display -l %s", layout); + g_spawn_command_line_async (commandline, NULL); + g_free (commandline); + + return TRUE; +} + +static GtkWidget * +input_widget_new (CcInputChooser *chooser, + const char *type, + const char *id, + gboolean is_extra) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + GtkWidget *label; + InputWidget *widget = g_new0 (InputWidget, 1); + const gchar *name; + gchar *text; + + if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { + gnome_xkb_info_get_layout_info (priv->xkb_info, id, &name, NULL, NULL, NULL); + } +#ifdef HAVE_IBUS + else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { + if (priv->ibus_engines) + name = engine_get_display_name (g_hash_table_lookup (priv->ibus_engines, id)); + else + name = id; + } +#endif + else { + name = "ERROR"; + } + + widget->id = g_strdup (id); + widget->type = g_strdup (type); + widget->name = g_strdup (name); + widget->is_extra = is_extra; + + widget->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); + gtk_widget_set_halign (widget->box, GTK_ALIGN_FILL); + gtk_widget_set_margin_top (widget->box, 10); + gtk_widget_set_margin_bottom (widget->box, 10); + gtk_widget_set_margin_start (widget->box, 10); + gtk_widget_set_margin_end (widget->box, 10); + + widget->label = gtk_label_new (name); + gtk_label_set_xalign (GTK_LABEL (widget->label), 0); + gtk_label_set_yalign (GTK_LABEL (widget->label), 0.5); + gtk_label_set_ellipsize (GTK_LABEL (widget->label), PANGO_ELLIPSIZE_END); + gtk_label_set_max_width_chars (GTK_LABEL (widget->label), 40); + gtk_label_set_width_chars (GTK_LABEL (widget->label), 40); + gtk_box_append (GTK_BOX (widget->box), widget->label); + + + widget->checkmark = gtk_image_new_from_icon_name ("object-select-symbolic"); + gtk_box_append (GTK_BOX (widget->box), widget->checkmark); + + gtk_widget_set_margin_start (widget->checkmark, 10); + gtk_widget_set_margin_end (widget->checkmark, 10); + gtk_widget_set_halign (widget->box, GTK_ALIGN_START); + + text = g_strdup_printf ("<a href='preview'>%s</a>", _("Preview")); + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL (label), text); + g_free (text); + g_signal_connect (label, "activate-link", + G_CALLBACK (preview_cb), chooser); + gtk_box_append (GTK_BOX (widget->box), label); + + g_object_set_data_full (G_OBJECT (widget->box), "input-widget", widget, + input_widget_free); + + return widget->box; +} + +static void +sync_all_checkmarks (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv; + GtkWidget *row; + + priv = cc_input_chooser_get_instance_private (chooser); + row = gtk_widget_get_first_child (priv->input_list); + while (row) { + InputWidget *widget; + GtkWidget *child; + gboolean should_be_visible; + + child = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row)); + widget = get_input_widget (child); + + if (widget == NULL) + return; + + if (priv->id == NULL || priv->type == NULL) + should_be_visible = FALSE; + else + should_be_visible = g_strcmp0 (widget->id, priv->id) == 0 && + g_strcmp0 (widget->type, priv->type) == 0; + gtk_widget_set_opacity (widget->checkmark, should_be_visible ? 1.0 : 0.0); + + if (widget->is_extra && should_be_visible) + widget->is_extra = FALSE; + + row = gtk_widget_get_next_sibling (row); + } + + gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->input_list)); +} + +static GtkWidget * +more_widget_new (void) +{ + GtkWidget *widget; + GtkWidget *arrow; + + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); + gtk_widget_set_tooltip_text (widget, _("Moreā¦")); + + arrow = gtk_image_new_from_icon_name ("view-more-symbolic"); + gtk_style_context_add_class (gtk_widget_get_style_context (arrow), "dim-label"); + gtk_widget_set_margin_top (widget, 12); + gtk_widget_set_margin_bottom (widget, 12); + gtk_widget_set_hexpand (arrow, TRUE); + gtk_widget_set_halign (arrow, GTK_ALIGN_CENTER); + gtk_widget_set_valign (arrow, GTK_ALIGN_CENTER); + gtk_box_append (GTK_BOX (widget), arrow); + + return widget; +} + +static GtkWidget * +no_results_widget_new (void) +{ + GtkWidget *widget; + + /* Translators: a search for input methods or keyboard layouts + * did not yield any results + */ + widget = padded_label_new (_("No inputs found")); + gtk_widget_set_sensitive (widget, FALSE); + return widget; +} + +static void +choose_non_extras (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv; + GtkWidget *row; + guint count = 0; + + priv = cc_input_chooser_get_instance_private (chooser); + row = gtk_widget_get_first_child (priv->input_list); + while (row) { + InputWidget *widget; + GtkWidget *child; + + if (++count > MIN_ROWS) + break; + + child = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row)); + widget = get_input_widget (child); + if (widget == NULL) + break; + + widget->is_extra = FALSE; + + row = gtk_widget_get_next_sibling (row); + } +} + +static void +add_rows_to_list (CcInputChooser *chooser, + GList *list, + const gchar *type, + const gchar *default_id) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + const gchar *id; + GtkWidget *widget; + gchar *key; + + for (; list; list = list->next) { + id = (const gchar *) list->data; + + if (g_strcmp0 (id, default_id) == 0) + continue; + + key = g_strdup_printf ("%s::%s", type, id); + if (g_hash_table_contains (priv->inputs, key)) { + g_free (key); + continue; + } + g_hash_table_add (priv->inputs, key); + + widget = input_widget_new (chooser, type, id, TRUE); + gtk_list_box_append (GTK_LIST_BOX (priv->input_list), widget); + } +} + +static void +add_row_to_list (CcInputChooser *chooser, + const gchar *type, + const gchar *id) +{ + GList tmp = { 0 }; + tmp.data = (gpointer)id; + add_rows_to_list (chooser, &tmp, type, NULL); +} + +static void +get_locale_infos (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + const gchar *type = NULL; + const gchar *id = NULL; + gchar *lang, *country; + GList *list; + + if (gnome_get_input_source_from_locale (priv->locale, &type, &id)) { + add_row_to_list (chooser, type, id); + if (!priv->id) { + priv->id = g_strdup (id); + priv->type = g_strdup (type); + } + } + + if (!gnome_parse_locale (priv->locale, &lang, &country, NULL, NULL)) + goto out; + + list = gnome_xkb_info_get_layouts_for_language (priv->xkb_info, lang); + add_rows_to_list (chooser, list, INPUT_SOURCE_TYPE_XKB, id); + g_list_free (list); + + if (country != NULL) { + list = gnome_xkb_info_get_layouts_for_country (priv->xkb_info, country); + add_rows_to_list (chooser, list, INPUT_SOURCE_TYPE_XKB, id); + g_list_free (list); + } + + choose_non_extras (chooser); + + list = gnome_xkb_info_get_all_layouts (priv->xkb_info); + add_rows_to_list (chooser, list, INPUT_SOURCE_TYPE_XKB, id); + g_list_free (list); + +out: + g_free (lang); + g_free (country); +} + +static gboolean +input_visible (GtkListBoxRow *row, + gpointer user_data) +{ + CcInputChooser *chooser = user_data; + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + InputWidget *widget; + gboolean visible; + GtkWidget *child; + const char *search_term; + + child = gtk_list_box_row_get_child (row); + if (child == priv->more_item) + return !priv->showing_extra && g_hash_table_size (priv->inputs) > MIN_ROWS; + + widget = get_input_widget (child); + + if (!priv->showing_extra && widget->is_extra) + return FALSE; + + search_term = gtk_editable_get_text (GTK_EDITABLE (priv->filter_entry)); + if (!search_term || !*search_term) + return TRUE; + + visible = g_str_match_string (search_term, widget->name, TRUE); + return visible; +} + +static gint +sort_inputs (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer data) +{ + InputWidget *la, *lb; + + la = get_input_widget (gtk_list_box_row_get_child (a)); + lb = get_input_widget (gtk_list_box_row_get_child (b)); + + if (la == NULL) + return 1; + + if (lb == NULL) + return -1; + + if (la->is_extra && !lb->is_extra) + return 1; + + if (!la->is_extra && lb->is_extra) + return -1; + + return strcmp (la->name, lb->name); +} + +static void +filter_changed (GtkEntry *entry, + CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->input_list)); +} + +static void +show_more (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + + if (g_hash_table_size (priv->inputs) <= MIN_ROWS) + return; + + gtk_widget_grab_focus (priv->filter_entry); + + gtk_widget_set_valign (GTK_WIDGET (chooser), GTK_ALIGN_FILL); + + priv->showing_extra = TRUE; + gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->input_list)); + g_object_notify_by_pspec (G_OBJECT (chooser), obj_props[PROP_SHOWING_EXTRA]); +} + +static void +set_input (CcInputChooser *chooser, + const gchar *id, + const gchar *type) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + + if (g_strcmp0 (priv->id, id) == 0 && + g_strcmp0 (priv->type, type) == 0) + return; + + g_free (priv->id); + g_free (priv->type); + priv->id = g_strdup (id); + priv->type = g_strdup (type); + + sync_all_checkmarks (chooser); + + g_signal_emit (chooser, signals[CHANGED], 0); +} + +static gboolean +confirm_choice (gpointer data) +{ + GtkWidget *widget = data; + + g_signal_emit (widget, signals[CONFIRM], 0); + + return G_SOURCE_REMOVE; +} + +static void +row_activated (GtkListBox *box, + GtkListBoxRow *row, + CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + GtkWidget *child; + InputWidget *widget; + + if (row == NULL) + return; + + child = gtk_list_box_row_get_child (row); + if (child == priv->more_item) { + show_more (chooser); + } else { + widget = get_input_widget (child); + if (widget == NULL) + return; + if (g_strcmp0 (priv->id, widget->id) == 0 && + g_strcmp0 (priv->type, widget->type) == 0) + confirm_choice (chooser); + else + set_input (chooser, widget->id, widget->type); + } +} + +#ifdef HAVE_IBUS +static void +update_ibus_active_sources (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv; + IBusEngineDesc *engine_desc; + GtkWidget *child; + const gchar *type; + const gchar *id; + gchar *name; + + priv = cc_input_chooser_get_instance_private (chooser); + child = gtk_widget_get_first_child (priv->input_list); + while (child) { + InputWidget *row; + + row = get_input_widget (child); + child = gtk_widget_get_next_sibling (child); + + if (row == NULL) + continue; + + type = row->type; + id = row->id; + if (g_strcmp0 (type, INPUT_SOURCE_TYPE_IBUS) != 0) + continue; + + engine_desc = g_hash_table_lookup (priv->ibus_engines, id); + if (engine_desc) { + name = engine_get_display_name (engine_desc); + gtk_label_set_text (GTK_LABEL (row->label), name); + g_free (name); + } + } +} + +static void +get_ibus_locale_infos (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + GHashTableIter iter; + const gchar *engine_id; + IBusEngineDesc *engine; + + if (!priv->ibus_engines) + return; + + g_hash_table_iter_init (&iter, priv->ibus_engines); + while (g_hash_table_iter_next (&iter, (gpointer *) &engine_id, (gpointer *) &engine)) + add_row_to_list (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id); +} + +static void +fetch_ibus_engines_result (GObject *object, + GAsyncResult *result, + CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv; + GList *list, *l; + GError *error; + + 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); + g_error_free (error); + return; + } + + priv = cc_input_chooser_get_instance_private (chooser); + g_clear_object (&priv->ibus_cancellable); + + /* Maps engine ids to engine description objects */ + priv->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; + + 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 (priv->ibus_engines, (gpointer)engine_id, engine); + } + g_list_free (list); + + update_ibus_active_sources (chooser); + get_ibus_locale_infos (chooser); + + sync_all_checkmarks (chooser); +} + +static void +fetch_ibus_engines (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + + priv->ibus_cancellable = g_cancellable_new (); + + ibus_bus_list_engines_async (priv->ibus, + -1, + priv->ibus_cancellable, + (GAsyncReadyCallback)fetch_ibus_engines_result, + chooser); + + /* We've got everything we needed, don't want to be called again. */ + g_signal_handlers_disconnect_by_func (priv->ibus, fetch_ibus_engines, chooser); +} + +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 +cc_input_chooser_constructed (GObject *object) +{ + CcInputChooser *chooser = CC_INPUT_CHOOSER (object); + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + + G_OBJECT_CLASS (cc_input_chooser_parent_class)->constructed (object); + + priv->xkb_info = gnome_xkb_info_new (); + +#ifdef HAVE_IBUS + ibus_init (); + if (!priv->ibus) { + priv->ibus = ibus_bus_new_async (); + if (ibus_bus_is_connected (priv->ibus)) + fetch_ibus_engines (chooser); + else + g_signal_connect_swapped (priv->ibus, "connected", + G_CALLBACK (fetch_ibus_engines), chooser); + } + maybe_start_ibus (); +#endif + + priv->inputs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + priv->more_item = more_widget_new (); + priv->no_results = no_results_widget_new (); + + gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->input_list), + sort_inputs, chooser, NULL); + gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->input_list), + input_visible, chooser, NULL); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->input_list), + GTK_SELECTION_NONE); + + if (priv->locale == NULL) { + priv->locale = cc_common_language_get_current_language (); + } + + get_locale_infos (chooser); +#ifdef HAVE_IBUS + get_ibus_locale_infos (chooser); +#endif + + gtk_list_box_append (GTK_LIST_BOX (priv->input_list), priv->more_item); + gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->input_list), priv->no_results); + + g_signal_connect (priv->filter_entry, "changed", + G_CALLBACK (filter_changed), + chooser); + + g_signal_connect (priv->input_list, "row-activated", + G_CALLBACK (row_activated), chooser); + + sync_all_checkmarks (chooser); +} + +static void +cc_input_chooser_finalize (GObject *object) +{ + CcInputChooser *chooser = CC_INPUT_CHOOSER (object); + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + + g_clear_object (&priv->xkb_info); + g_hash_table_unref (priv->inputs); +#ifdef HAVE_IBUS + g_clear_object (&priv->ibus); + if (priv->ibus_cancellable) + g_cancellable_cancel (priv->ibus_cancellable); + g_clear_object (&priv->ibus_cancellable); + g_clear_pointer (&priv->ibus_engines, g_hash_table_destroy); +#endif + + G_OBJECT_CLASS (cc_input_chooser_parent_class)->finalize (object); +} + +static void +cc_input_chooser_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcInputChooser *chooser = CC_INPUT_CHOOSER (object); + switch (prop_id) { + case PROP_SHOWING_EXTRA: + g_value_set_boolean (value, cc_input_chooser_get_showing_extra (chooser)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +cc_input_chooser_class_init (CcInputChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/input-chooser.ui"); + + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcInputChooser, filter_entry); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), CcInputChooser, input_list); + + object_class->finalize = cc_input_chooser_finalize; + object_class->get_property = cc_input_chooser_get_property; + object_class->constructed = cc_input_chooser_constructed; + + obj_props[PROP_SHOWING_EXTRA] = + g_param_spec_string ("showing-extra", "", "", "", + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[CONFIRM] = + g_signal_new ("confirm", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_object_class_install_properties (object_class, PROP_LAST, obj_props); +} + +static void +cc_input_chooser_init (CcInputChooser *chooser) +{ + gtk_widget_init_template (GTK_WIDGET (chooser)); +} + +void +cc_input_chooser_clear_filter (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + gtk_editable_set_text (GTK_EDITABLE (priv->filter_entry), ""); +} + +const gchar * +cc_input_chooser_get_input_id (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + return priv->id; +} + +const gchar * +cc_input_chooser_get_input_type (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + return priv->type; +} + +void +cc_input_chooser_get_layout (CcInputChooser *chooser, + const gchar **layout, + const gchar **variant) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + + if (!get_layout (chooser, priv->type, priv->id, layout, variant)) { + if (layout != NULL) + *layout = NULL; + if (variant != NULL) + *variant = NULL; + } +} + +void +cc_input_chooser_set_input (CcInputChooser *chooser, + const gchar *id, + const gchar *type) +{ + set_input (chooser, id, type); +} + +gboolean +cc_input_chooser_get_showing_extra (CcInputChooser *chooser) +{ + CcInputChooserPrivate *priv = cc_input_chooser_get_instance_private (chooser); + return priv->showing_extra; +} diff --git a/gnome-initial-setup/pages/keyboard/cc-input-chooser.h b/gnome-initial-setup/pages/keyboard/cc-input-chooser.h new file mode 100644 index 0000000..dfd6a28 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/cc-input-chooser.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Red Hat, 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/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + * Matthias Clasen <mclasen@redhat.com> + */ + +#ifndef __GIS_INPUT_CHOOSER_H__ +#define __GIS_INPUT_CHOOSER_H__ + +#include <gtk/gtk.h> +#include <glib-object.h> + +#define CC_TYPE_INPUT_CHOOSER (cc_input_chooser_get_type ()) +#define CC_INPUT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_INPUT_CHOOSER, CcInputChooser)) +#define CC_INPUT_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_INPUT_CHOOSER, CcInputChooserClass)) +#define CC_IS_INPUT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_INPUT_CHOOSER)) +#define CC_IS_INPUT_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_INPUT_CHOOSER)) +#define CC_INPUT_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_INPUT_CHOOSER, CcInputChooserClass)) + +G_BEGIN_DECLS + +typedef struct _CcInputChooser CcInputChooser; +typedef struct _CcInputChooserClass CcInputChooserClass; + +struct _CcInputChooser +{ + GtkBox parent; +}; + +struct _CcInputChooserClass +{ + GtkBoxClass parent_class; +}; + +GType cc_input_chooser_get_type (void); + +void cc_input_chooser_clear_filter (CcInputChooser *chooser); +const gchar * cc_input_chooser_get_input_id (CcInputChooser *chooser); +const gchar * cc_input_chooser_get_input_type (CcInputChooser *chooser); +void cc_input_chooser_set_input (CcInputChooser *chooser, + const gchar *id, + const gchar *type); +void cc_input_chooser_get_layout (CcInputChooser *chooser, + const gchar **layout, + const gchar **variant); +gboolean cc_input_chooser_get_showing_extra (CcInputChooser *chooser); + +G_END_DECLS + +#endif /* __GIS_INPUT_CHOOSER_H__ */ diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c new file mode 100644 index 0000000..3adfd66 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c @@ -0,0 +1,541 @@ +/* + * 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> + * Michael Wood <michael.g.wood@intel.com> + * + * Based on gnome-control-center cc-region-panel.c + */ + +#define PAGE_ID "keyboard" + +#include "config.h" + +#include <locale.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <polkit/polkit.h> + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include <libgnome-desktop/gnome-languages.h> + +#include "gis-keyboard-page.h" +#include "keyboard-resources.h" +#include "cc-input-chooser.h" + +#include "cc-common-language.h" + +#include "gis-page-header.h" + +#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" +#define KEY_CURRENT_INPUT_SOURCE "current" +#define KEY_INPUT_SOURCES "sources" + +struct _GisKeyboardPagePrivate { + GtkWidget *input_chooser; + + GDBusProxy *localed; + GCancellable *cancellable; + GPermission *permission; + GSettings *input_settings; + + GSList *system_sources; +}; +typedef struct _GisKeyboardPagePrivate GisKeyboardPagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GisKeyboardPage, gis_keyboard_page, GIS_TYPE_PAGE); + +static void +gis_keyboard_page_finalize (GObject *object) +{ + GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object); + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + + if (priv->cancellable) + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + + g_clear_object (&priv->permission); + g_clear_object (&priv->localed); + g_clear_object (&priv->input_settings); + + g_slist_free_full (priv->system_sources, g_free); + + G_OBJECT_CLASS (gis_keyboard_page_parent_class)->finalize (object); +} + +static void +set_input_settings (GisKeyboardPage *self) +{ + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + const gchar *type; + const gchar *id; + GVariantBuilder builder; + GSList *l; + gboolean is_xkb_source = FALSE; + + type = cc_input_chooser_get_input_type (CC_INPUT_CHOOSER (priv->input_chooser)); + id = cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + + if (g_str_equal (type, "xkb")) { + g_variant_builder_add (&builder, "(ss)", type, id); + is_xkb_source = TRUE; + } + + for (l = priv->system_sources; l; l = l->next) { + const gchar *sid = l->data; + + if (g_str_equal (id, sid) && g_str_equal (type, "xkb")) + continue; + + g_variant_builder_add (&builder, "(ss)", "xkb", sid); + } + + if (!is_xkb_source) + g_variant_builder_add (&builder, "(ss)", type, id); + + g_settings_set_value (priv->input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); + g_settings_set_uint (priv->input_settings, KEY_CURRENT_INPUT_SOURCE, 0); + + g_settings_apply (priv->input_settings); +} + +static void +set_localed_input (GisKeyboardPage *self) +{ + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + const gchar *layout, *variant; + GString *layouts; + GString *variants; + GSList *l; + + if (!priv->localed) + return; + + cc_input_chooser_get_layout (CC_INPUT_CHOOSER (priv->input_chooser), &layout, &variant); + if (layout == NULL) + layout = ""; + if (variant == NULL) + variant = ""; + + layouts = g_string_new (layout); + variants = g_string_new (variant); + +#define LAYOUT(a) (a[0]) +#define VARIANT(a) (a[1] ? a[1] : "") + for (l = priv->system_sources; l; l = l->next) { + const gchar *sid = l->data; + gchar **lv = g_strsplit (sid, "+", -1); + + if (!g_str_equal (LAYOUT (lv), layout) || + !g_str_equal (VARIANT (lv), variant)) { + if (layouts->str[0]) { + g_string_append_c (layouts, ','); + g_string_append_c (variants, ','); + } + g_string_append (layouts, LAYOUT (lv)); + g_string_append (variants, VARIANT (lv)); + } + g_strfreev (lv); + } +#undef LAYOUT +#undef VARIANT + + g_dbus_proxy_call (priv->localed, + "SetX11Keyboard", + g_variant_new ("(ssssbb)", layouts->str, "", variants->str, "", TRUE, TRUE), + G_DBUS_CALL_FLAGS_NONE, + -1, NULL, NULL, NULL); + g_string_free (layouts, TRUE); + g_string_free (variants, TRUE); +} + +static void +change_locale_permission_acquired (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GisKeyboardPage *page = GIS_KEYBOARD_PAGE (data); + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (page); + GError *error = NULL; + gboolean allowed; + + allowed = g_permission_acquire_finish (priv->permission, res, &error); + if (error) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to acquire permission: %s", error->message); + g_error_free (error); + return; + } + + if (allowed) + set_localed_input (GIS_KEYBOARD_PAGE (data)); +} + +static void +update_input (GisKeyboardPage *self) +{ + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + + set_input_settings (self); + + if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) { + if (g_permission_get_allowed (priv->permission)) { + set_localed_input (self); + } else if (g_permission_get_can_acquire (priv->permission)) { + g_permission_acquire_async (priv->permission, + NULL, + change_locale_permission_acquired, + self); + } + } +} + +static gboolean +gis_keyboard_page_apply (GisPage *page, + GCancellable *cancellable) +{ + update_input (GIS_KEYBOARD_PAGE (page)); + return FALSE; +} + +static GSList * +get_localed_input (GDBusProxy *proxy) +{ + GVariant *v; + const gchar *s; + gchar *id; + guint i, n; + gchar **layouts = NULL; + gchar **variants = NULL; + GSList *sources = NULL; + + v = g_dbus_proxy_get_cached_property (proxy, "X11Layout"); + if (v) { + s = g_variant_get_string (v, NULL); + layouts = g_strsplit (s, ",", -1); + g_variant_unref (v); + } + + v = g_dbus_proxy_get_cached_property (proxy, "X11Variant"); + if (v) { + s = g_variant_get_string (v, NULL); + if (s && *s) + variants = g_strsplit (s, ",", -1); + g_variant_unref (v); + } + + 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++) { + if (variants && variants[i] && variants[i][0]) + id = g_strdup_printf ("%s+%s", layouts[i], variants[i]); + else + id = g_strdup (layouts[i]); + sources = g_slist_prepend (sources, id); + } + + g_strfreev (variants); + g_strfreev (layouts); + + return sources; +} + +static void +add_default_keyboard_layout (GDBusProxy *proxy, + GVariantBuilder *builder) +{ + GSList *sources = get_localed_input (proxy); + sources = g_slist_reverse (sources); + + for (; sources; sources = sources->next) + g_variant_builder_add (builder, "(ss)", "xkb", + (const gchar *) sources->data); + + g_slist_free_full (sources, g_free); +} + +static void +add_default_input_sources (GisKeyboardPage *self, + GDBusProxy *proxy) +{ + const gchar *type; + const gchar *id; + gchar *language; + GVariantBuilder builder; + GSettings *input_settings; + + input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + + add_default_keyboard_layout (proxy, &builder); + + /* add other input sources */ + language = cc_common_language_get_current_language (); + if (gnome_get_input_source_from_locale (language, &type, &id)) { + if (!g_str_equal (type, "xkb")) + g_variant_builder_add (&builder, "(ss)", type, id); + } + g_free (language); + + g_settings_delay (input_settings); + g_settings_set_value (input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); + g_settings_set_uint (input_settings, KEY_CURRENT_INPUT_SOURCE, 0); + g_settings_apply (input_settings); + + g_object_unref (input_settings); +} + +static void +skip_proxy_ready (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GisKeyboardPage *self = data; + GDBusProxy *proxy; + 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", error->message); + g_error_free (error); + return; + } + + add_default_input_sources (self, proxy); + + g_object_unref (proxy); +} + +static void +gis_keyboard_page_skip (GisPage *page) +{ + GisKeyboardPage *self = GIS_KEYBOARD_PAGE (page); + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + priv->cancellable, + (GAsyncReadyCallback) skip_proxy_ready, + self); +} + +static void +preselect_input_source (GisKeyboardPage *self) +{ + const gchar *type; + const gchar *id; + gchar *language; + gboolean desktop_got_something; + gboolean desktop_got_input_method; + + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + GSList *sources = get_localed_input (priv->localed); + + /* These will be added silently after the user selection when + * writing out the settings. */ + g_slist_free_full (priv->system_sources, g_free); + priv->system_sources = g_slist_reverse (sources); + + /* We have two potential sources of information as to which + * source to pre-select here: the keyboard layout that is + * configured system-wide (read from priv->system_sources), + * and a gnome-desktop function that lets us look up a default + * input source for a given language. + * + * An important limitation here is that there is no system-wide + * configuration for input methods, so if the best choice for the + * language is an input method, we will only find it from the + * gnome-desktop lookup. But if both sources give us keyboard layouts, + * we want to prefer the one that's configured system-wide over the one + * from gnome-desktop. + * + * So we first do the gnome-desktop lookup, and keep track of what we + * got. + * + * - If we got an input method, we preselect that, and we're done. + * - If we got a keyboard layout, and there's no system-wide keyboard + * layout set, we preselect the layout we got from gnome-desktop. + * - If we didn't get an input method from gnome-desktop and there + * is a system-wide keyboard layout set, we preselect that. + * - If we got nothing from gnome-desktop and there's no system-wide + * keyboard layout set, we don't preselect anything. + * + * See: + * - https://bugzilla.gnome.org/show_bug.cgi?id=776189 + * - https://gitlab.gnome.org/GNOME/gnome-initial-setup/-/issues/104 + */ + language = cc_common_language_get_current_language (); + + desktop_got_something = gnome_get_input_source_from_locale (language, &type, &id); + desktop_got_input_method = (desktop_got_something && g_strcmp0 (type, "xkb") != 0); + + if (desktop_got_something && (desktop_got_input_method || !priv->system_sources)) { + cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), + id, type); + } else if (priv->system_sources) { + cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), + (const gchar *) priv->system_sources->data, + "xkb"); + } + + g_free (language); +} + +static void +update_page_complete (GisKeyboardPage *self) +{ + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + gboolean complete; + + complete = (priv->localed != NULL && + cc_input_chooser_get_input_id (CC_INPUT_CHOOSER (priv->input_chooser)) != NULL); + gis_page_set_complete (GIS_PAGE (self), complete); +} + +static void +localed_proxy_ready (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GisKeyboardPage *self = data; + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + GDBusProxy *proxy; + 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", error->message); + g_error_free (error); + return; + } + + priv->localed = proxy; + + preselect_input_source (self); + update_page_complete (self); +} + +static void +input_confirmed (CcInputChooser *chooser, + GisKeyboardPage *self) +{ + gis_assistant_next_page (gis_driver_get_assistant (GIS_PAGE (self)->driver)); +} + +static void +input_changed (CcInputChooser *chooser, + GisKeyboardPage *self) +{ + update_page_complete (self); +} + +static void +gis_keyboard_page_constructed (GObject *object) +{ + GisKeyboardPage *self = GIS_KEYBOARD_PAGE (object); + GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self); + + g_type_ensure (CC_TYPE_INPUT_CHOOSER); + + G_OBJECT_CLASS (gis_keyboard_page_parent_class)->constructed (object); + + g_signal_connect (priv->input_chooser, "confirm", + G_CALLBACK (input_confirmed), self); + g_signal_connect (priv->input_chooser, "changed", + G_CALLBACK (input_changed), self); + + priv->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + g_settings_delay (priv->input_settings); + + priv->cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + priv->cancellable, + (GAsyncReadyCallback) localed_proxy_ready, + self); + + /* If we're in new user mode then we're manipulating system settings */ + if (gis_driver_get_mode (GIS_PAGE (self)->driver) == GIS_DRIVER_MODE_NEW_USER) + priv->permission = polkit_permission_new_sync ("org.freedesktop.locale1.set-keyboard", NULL, NULL, NULL); + + update_page_complete (self); + + gtk_widget_show (GTK_WIDGET (self)); +} + +static void +gis_keyboard_page_locale_changed (GisPage *page) +{ + gis_page_set_title (GIS_PAGE (page), _("Typing")); +} + +static void +gis_keyboard_page_class_init (GisKeyboardPageClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GisPageClass * page_class = GIS_PAGE_CLASS (klass); + + gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-keyboard-page.ui"); + + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, input_chooser); + + page_class->page_id = PAGE_ID; + page_class->apply = gis_keyboard_page_apply; + page_class->skip = gis_keyboard_page_skip; + page_class->locale_changed = gis_keyboard_page_locale_changed; + object_class->constructed = gis_keyboard_page_constructed; + object_class->finalize = gis_keyboard_page_finalize; +} + +static void +gis_keyboard_page_init (GisKeyboardPage *self) +{ + g_resources_register (keyboard_get_resource ()); + g_type_ensure (GIS_TYPE_PAGE_HEADER); + g_type_ensure (CC_TYPE_INPUT_CHOOSER); + + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GisPage * +gis_prepare_keyboard_page (GisDriver *driver) +{ + return g_object_new (GIS_TYPE_KEYBOARD_PAGE, + "driver", driver, + NULL); +} diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.h b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.h new file mode 100644 index 0000000..d5710a0 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.h @@ -0,0 +1,70 @@ +/* + * 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> + * + */ + + +#ifndef _GIS_KEYBOARD_PAGE_H +#define _GIS_KEYBOARD_PAGE_H + +#include "gnome-initial-setup.h" + +G_BEGIN_DECLS + +#define GIS_TYPE_KEYBOARD_PAGE gis_keyboard_page_get_type() + +#define GIS_KEYBOARD_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GIS_TYPE_KEYBOARD_PAGE, GisKeyboardPage)) + +#define GIS_KEYBOARD_PAGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GIS_TYPE_KEYBOARD_PAGE, GisKeyboardPageClass)) + +#define GIS_IS_KEYBOARD_PAGE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GIS_TYPE_KEYBOARD_PAGE)) + +#define GIS_IS_KEYBOARD_PAGE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GIS_TYPE_KEYBOARD_PAGE)) + +#define GIS_KEYBOARD_PAGE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GIS_TYPE_KEYBOARD_PAGE, GisKeyboardPageClass)) + +typedef struct _GisKeyboardPage GisKeyboardPage; +typedef struct _GisKeyboardPageClass GisKeyboardPageClass; + +struct _GisKeyboardPage +{ + GisPage parent; +}; + +struct _GisKeyboardPageClass +{ + GisPageClass parent_class; +}; + +GType gis_keyboard_page_get_type (void) G_GNUC_CONST; + +GisPage *gis_prepare_keyboard_page (GisDriver *driver); + +G_END_DECLS + +#endif /* _GIS_KEYBOARD_PAGE_H */ diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui new file mode 100644 index 0000000..a47d8a1 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <template class="GisKeyboardPage" parent="GisPage"> + <child> + <object class="AdwPreferencesPage"> + <child> + <object class="AdwPreferencesGroup"> + + <child> + <object class="GtkBox" id="page"> + <property name="orientation">vertical</property> + <child> + <object class="GisPageHeader" id="header"> + <property name="margin_top">24</property> + <property name="title" translatable="yes">Typing</property> + <property name="subtitle" translatable="yes">Select your keyboard layout or an input method.</property> + <property name="icon_name">input-keyboard-symbolic</property> + <property name="show_icon" bind-source="GisKeyboardPage" bind-property="small-screen" bind-flags="invert-boolean|sync-create"/> + </object> + </child> + <child> + <object class="CcInputChooser" id="input_chooser"> + <property name="margin_top">18</property> + <property name="margin_bottom">18</property> + </object> + </child> + </object> + </child> + + </object> + </child> + + </object> + </child> + </template> +</interface> diff --git a/gnome-initial-setup/pages/keyboard/input-chooser.ui b/gnome-initial-setup/pages/keyboard/input-chooser.ui new file mode 100644 index 0000000..5fe2229 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/input-chooser.ui @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<interface> + <template class="CcInputChooser" parent="GtkBox"> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkSearchEntry" id="filter_entry"> + <property name="hexpand">True</property> + </object> + </child> + <child> + <object class="GtkListBox" id="input_list"> + <property name="vexpand">True</property> + <property name="halign">fill</property> + <property name="valign">start</property> + <style> + <class name="boxed-list" /> + </style> + </object> + </child> + </template> +</interface> diff --git a/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml b/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml new file mode 100644 index 0000000..103b3f1 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/org/gnome/initial-setup"> + <file preprocess="xml-stripblanks">gis-keyboard-page.ui</file> + <file preprocess="xml-stripblanks">input-chooser.ui</file> + </gresource> +</gresources> diff --git a/gnome-initial-setup/pages/keyboard/meson.build b/gnome-initial-setup/pages/keyboard/meson.build new file mode 100644 index 0000000..69d6de8 --- /dev/null +++ b/gnome-initial-setup/pages/keyboard/meson.build @@ -0,0 +1,14 @@ +sources += gnome.compile_resources( + 'keyboard-resources', + files('keyboard.gresource.xml'), + c_name: 'keyboard' +) + +sources += files( + 'cc-input-chooser.c', + 'cc-input-chooser.h', + 'cc-ibus-utils.c', + 'cc-ibus-utils.h', + 'gis-keyboard-page.c', + 'gis-keyboard-page.h' +) |