diff --git a/panels/region/Makefile.am b/panels/region/Makefile.am index 8f6a6cb40..b33deeca3 100644 --- a/panels/region/Makefile.am +++ b/panels/region/Makefile.am @@ -18,14 +18,14 @@ libregion_la_SOURCES = \ $(BUILT_SOURCES) \ cc-region-panel.c \ cc-region-panel.h \ - gnome-region-panel-formats.c \ - gnome-region-panel-formats.h \ - gnome-region-panel-lang.c \ - gnome-region-panel-lang.h \ - gnome-region-panel-system.c \ - gnome-region-panel-system.h \ - gnome-region-panel-input.c \ - gnome-region-panel-input.h \ + cc-format-chooser.c \ + cc-format-chooser.h \ + cc-input-options.c \ + cc-input-options.h \ + cc-input-chooser.c \ + cc-input-chooser.h \ + cc-ibus-utils.c \ + cc-ibus-utils.h \ $(NULL) libregion_la_LIBADD = $(PANEL_LIBS) $(REGION_PANEL_LIBS) $(builddir)/../common/liblanguage.la diff --git a/panels/region/cc-format-chooser.c b/panels/region/cc-format-chooser.c new file mode 100644 index 000000000..cde9e77ab --- /dev/null +++ b/panels/region/cc-format-chooser.c @@ -0,0 +1,521 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Matthias Clasen + */ + +#define _GNU_SOURCE +#include +#include "cc-format-chooser.h" + +#include +#include +#include +#include + +#include "egg-list-box/egg-list-box.h" + +#include "cc-common-language.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include + + +typedef struct { + GtkWidget *dialog; + GtkWidget *no_results; + GtkWidget *more_item; + GtkWidget *filter_entry; + GtkWidget *list; + GtkWidget *scrolledwindow; + GtkWidget *full_date; + GtkWidget *medium_date; + GtkWidget *short_date; + GtkWidget *time; + GtkWidget *number; + GtkWidget *measurement; + GtkWidget *paper; + gboolean adding; + gboolean showing_extra; + gchar *region; +} CcFormatChooserPrivate; + +#define GET_PRIVATE(chooser) ((CcFormatChooserPrivate *) g_object_get_data (G_OBJECT (chooser), "private")) + +static void +display_date (GtkWidget *label, GDateTime *dt, const gchar *format) +{ + gchar *s; + s = g_date_time_format (dt, format); + s = g_strstrip (s); + gtk_label_set_text (GTK_LABEL (label), s); + g_free (s); +} + +static void +update_format_examples (GtkDialog *chooser) +{ + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + gchar *locale; + GDateTime *dt; + gchar *s; + const gchar *fmt; + GtkPaperSize *paper; + + locale = g_strdup (setlocale (LC_TIME, NULL)); + setlocale (LC_TIME, priv->region); + + dt = g_date_time_new_now_local (); + display_date (priv->full_date, dt, "%A %e %B %Y"); + display_date (priv->medium_date, dt, "%e %b %Y"); + display_date (priv->short_date, dt, "%x"); + display_date (priv->time, dt, "%X"); + + setlocale (LC_TIME, locale); + g_free (locale); + + locale = g_strdup (setlocale (LC_NUMERIC, NULL)); + setlocale (LC_NUMERIC, priv->region); + + s = g_strdup_printf ("%'.2f", 123456789.00); + gtk_label_set_text (GTK_LABEL (priv->number), s); + g_free (s); + + setlocale (LC_NUMERIC, locale); + g_free (locale); + +#if 0 + locale = g_strdup (setlocale (LC_MONETARY, NULL)); + setlocale (LC_MONETARY, priv->region); + + num_info = localeconv (); + if (num_info != NULL) { + gtk_label_set_text (GTK_LABEL (priv->currency), num_info->currency_symbol); + } + + setlocale (LC_MONETARY, locale); + g_free (locale); +#endif + +#ifdef LC_MEASUREMENT + locale = g_strdup (setlocale (LC_MEASUREMENT, NULL)); + setlocale (LC_MEASUREMENT, priv->region); + + fmt = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT); + if (fmt && *fmt == 2) + gtk_label_set_text (GTK_LABEL (priv->measurement), _("Imperial")); + else + gtk_label_set_text (GTK_LABEL (priv->measurement), _("Metric")); + + setlocale (LC_MEASUREMENT, locale); + g_free (locale); +#endif + +#ifdef LC_PAPER + locale = g_strdup (setlocale (LC_PAPER, NULL)); + setlocale (LC_PAPER, priv->region); + + paper = gtk_paper_size_new (gtk_paper_size_get_default ()); + gtk_label_set_text (GTK_LABEL (priv->paper), gtk_paper_size_get_display_name (paper)); + gtk_paper_size_free (paper); + + setlocale (LC_PAPER, locale); + g_free (locale); +#endif +} + +static void +set_locale_id (GtkDialog *chooser, + const gchar *locale_id) +{ + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + GList *children, *l; + + children = gtk_container_get_children (GTK_CONTAINER (priv->list)); + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkWidget *check = g_object_get_data (G_OBJECT (row), "check"); + const gchar *region = g_object_get_data (G_OBJECT (row), "locale-id"); + if (check == NULL || region == NULL) + continue; + + if (strcmp (locale_id, region) == 0) { + gboolean is_extra; + + /* mark as selected */ + gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU); + + /* make sure this row is shown */ + is_extra = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row), "is-extra")); + if (!priv->showing_extra && is_extra) { + g_object_set_data (G_OBJECT (row), "is-extra", GINT_TO_POINTER (FALSE)); + egg_list_box_refilter (EGG_LIST_BOX (priv->list)); + } + + } else { + /* mark as unselected */ + gtk_image_clear (GTK_IMAGE (check)); + g_object_set (check, "icon-size", GTK_ICON_SIZE_MENU, NULL); + } + } + g_list_free (children); + + g_free (priv->region); + priv->region = g_strdup (locale_id); + + update_format_examples (chooser); +} + +static gint +sort_regions (gconstpointer a, + gconstpointer b, + gpointer data) +{ + const gchar *la; + const gchar *lb; + gboolean iea; + gboolean ieb; + + if (g_object_get_data (G_OBJECT (a), "locale-id") == NULL) { + return 1; + } + if (g_object_get_data (G_OBJECT (b), "locale-id") == NULL) { + return -1; + } + + la = g_object_get_data (G_OBJECT (a), "locale-name"); + lb = g_object_get_data (G_OBJECT (b), "locale-name"); + + iea = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (a), "is-extra")); + ieb = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (b), "is-extra")); + + if (iea != ieb) { + return ieb - iea; + } else { + return strcmp (la, lb); + } +} + +static GtkWidget * +padded_label_new (char *text, gboolean narrow) +{ + 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_widget_set_margin_left (widget, narrow ? 10 : 80); + gtk_widget_set_margin_right (widget, narrow ? 10 : 80); + gtk_box_pack_start (GTK_BOX (widget), gtk_label_new (text), FALSE, FALSE, 0); + + return widget; +} + +static GtkWidget * +region_widget_new (const gchar *locale_id, + gboolean is_extra) +{ + gchar *locale_name; + GtkWidget *widget; + GtkWidget *check; + + locale_name = gnome_get_region_from_name (locale_id, locale_id); + + widget = padded_label_new (locale_name, is_extra); + + check = gtk_image_new (); + g_object_set (check, "icon-size", GTK_ICON_SIZE_MENU, NULL); + gtk_box_pack_start (GTK_BOX (widget), check, FALSE, FALSE, 0); + + g_object_set_data (G_OBJECT (widget), "check", check); + g_object_set_data_full (G_OBJECT (widget), "locale-id", g_strdup (locale_id), g_free); + g_object_set_data_full (G_OBJECT (widget), "locale-name", g_strdup (locale_name), g_free); + g_object_set_data (G_OBJECT (widget), "is-extra", GUINT_TO_POINTER (is_extra)); + + g_free (locale_name); + + return widget; +} + +static GtkWidget * +more_widget_new (void) +{ + GtkWidget *widget; + + widget = padded_label_new ("…", FALSE); + gtk_widget_set_tooltip_text (widget, _("More…")); + return widget; +} + +static GtkWidget * +no_results_widget_new (void) +{ + GtkWidget *widget; + + widget = padded_label_new (_("No regions found"), TRUE); + gtk_widget_set_sensitive (widget, FALSE); + return widget; +} + +static void +add_regions (GtkDialog *chooser, + gchar **locale_ids, + GHashTable *initial) +{ + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + + priv->adding = TRUE; + + while (*locale_ids) { + gchar *locale_id; + gboolean is_initial; + GtkWidget *widget; + + locale_id = *locale_ids; + locale_ids ++; + + if (!cc_common_language_has_font (locale_id)) + continue; + + is_initial = (g_hash_table_lookup (initial, locale_id) != NULL); + widget = region_widget_new (locale_id, !is_initial); + gtk_container_add (GTK_CONTAINER (priv->list), widget); + } + + gtk_container_add (GTK_CONTAINER (priv->list), priv->more_item); + gtk_container_add (GTK_CONTAINER (priv->list), priv->no_results); + + gtk_widget_show_all (priv->list); + + priv->adding = FALSE; +} + +static void +add_all_regions (GtkDialog *chooser) +{ + gchar **locale_ids; + GHashTable *initial; + + locale_ids = gnome_get_all_language_names (); + initial = cc_common_language_get_initial_languages (); + add_regions (chooser, locale_ids, initial); +} + +static gboolean +region_visible (GtkWidget *child, + gpointer user_data) +{ + GtkDialog *chooser = user_data; + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + gchar *locale_name; + const gchar *filter_contents; + gboolean is_extra; + + if (child == priv->more_item) + return !priv->showing_extra; + + /* We hide this in the after-refilter handler below. */ + if (child == priv->no_results) + return TRUE; + + is_extra = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), "is-extra")); + locale_name = g_object_get_data (G_OBJECT (child), "locale-name"); + + filter_contents = gtk_entry_get_text (GTK_ENTRY (priv->filter_entry)); + if (*filter_contents && strcasestr (locale_name, filter_contents) == NULL) + return FALSE; + + if (!priv->showing_extra && is_extra) + return FALSE; + + return TRUE; +} + +static void +show_more (GtkDialog *chooser) +{ + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + GtkWidget *widget; + gint width, height; + + gtk_window_get_size (GTK_WINDOW (chooser), &width, &height); + gtk_widget_set_size_request (GTK_WIDGET (chooser), width, height); + gtk_window_set_resizable (GTK_WINDOW (chooser), TRUE); + + widget = priv->scrolledwindow; + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + + gtk_widget_show (priv->filter_entry); + gtk_widget_grab_focus (priv->filter_entry); + + priv->showing_extra = TRUE; + + egg_list_box_refilter (EGG_LIST_BOX (priv->list)); +} + +static void +child_activated (EggListBox *box, + GtkWidget *child, + GtkDialog *chooser) +{ + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + gchar *new_locale_id; + + if (priv->adding) + return; + + if (child == NULL) + return; + else if (child == priv->no_results) + return; + else if (child == priv->more_item) + show_more (chooser); + else { + new_locale_id = g_object_get_data (G_OBJECT (child), "locale-id"); + set_locale_id (chooser, new_locale_id); + } +} + +typedef struct { + gint count; + GtkWidget *ignore; +} CountChildrenData; + +static void +count_visible_children (GtkWidget *widget, + gpointer user_data) +{ + CountChildrenData *data = user_data; + if (widget!= data->ignore && + gtk_widget_get_child_visible (widget) && + gtk_widget_get_visible (widget)) + data->count++; +} + +static void +end_refilter (EggListBox *list_box, + gpointer user_data) +{ + GtkDialog *chooser = user_data; + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + CountChildrenData data = { 0 }; + + data.ignore = priv->no_results; + gtk_container_foreach (GTK_CONTAINER (list_box), + count_visible_children, &data); + + gtk_widget_set_visible (priv->no_results, (data.count == 0)); +} + +static void +cc_format_chooser_private_free (gpointer data) +{ + g_free (data); +} + +#define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name)) + +GtkWidget * +cc_format_chooser_new (GtkWidget *parent) +{ + GtkBuilder *builder; + GtkWidget *chooser; + CcFormatChooserPrivate *priv; + GError *error = NULL; + + builder = gtk_builder_new (); + gtk_builder_add_from_resource (builder, "/org/gnome/control-center/region/format-chooser.ui", &error); + if (error) { + g_warning ("failed to load format chooser: %s", error->message); + g_error_free (error); + return NULL; + } + + chooser = WID ("dialog"); + priv = g_new0 (CcFormatChooserPrivate, 1); + g_object_set_data_full (G_OBJECT (chooser), "private", priv, cc_format_chooser_private_free); + + priv->filter_entry = WID ("region-filter-entry"); + priv->list = WID ("region-list"); + priv->scrolledwindow = WID ("region-scrolledwindow"); + priv->more_item = more_widget_new (); + priv->no_results = no_results_widget_new (); + + priv->full_date = WID ("full-date-format"); + priv->medium_date = WID ("medium-date-format"); + priv->short_date = WID ("short-date-format"); + priv->time = WID ("time-format"); + priv->number = WID ("number-format"); + priv->measurement = WID ("measurement-format"); + priv->paper = WID ("paper-format"); + + egg_list_box_set_adjustment (EGG_LIST_BOX (priv->list), + gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow))); + + egg_list_box_set_sort_func (EGG_LIST_BOX (priv->list), + sort_regions, chooser, NULL); + egg_list_box_set_filter_func (EGG_LIST_BOX (priv->list), + region_visible, chooser, NULL); + egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->list), + GTK_SELECTION_NONE); + + add_all_regions (GTK_DIALOG (chooser)); + + g_signal_connect_swapped (priv->filter_entry, "changed", + G_CALLBACK (egg_list_box_refilter), + priv->list); + + g_signal_connect (priv->list, "child-activated", + G_CALLBACK (child_activated), chooser); + + g_signal_connect_after (priv->list, "refilter", + G_CALLBACK (end_refilter), chooser); + + egg_list_box_refilter (EGG_LIST_BOX (priv->list)); + + gtk_window_set_transient_for (GTK_WINDOW (chooser), GTK_WINDOW (parent)); + + return chooser; +} + +void +cc_format_chooser_clear_filter (GtkWidget *chooser) +{ + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + + gtk_entry_set_text (GTK_ENTRY (priv->filter_entry), ""); +} + +const gchar * +cc_format_chooser_get_region (GtkWidget *chooser) +{ + CcFormatChooserPrivate *priv = GET_PRIVATE (chooser); + + return priv->region; +} + +void +cc_format_chooser_set_region (GtkWidget *chooser, + const gchar *region) +{ + set_locale_id (GTK_DIALOG (chooser), region); +} diff --git a/panels/region/cc-format-chooser.h b/panels/region/cc-format-chooser.h new file mode 100644 index 000000000..77152a7b8 --- /dev/null +++ b/panels/region/cc-format-chooser.h @@ -0,0 +1,39 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Matthias Clasen + */ + +#ifndef __CC_FORMAT_CHOOSER_H__ +#define __CC_FORMAT_CHOOSER_H__ + +#include +#include + +G_BEGIN_DECLS + +GtkWidget *cc_format_chooser_new (GtkWidget *parent); +void cc_format_chooser_clear_filter (GtkWidget *chooser); +const gchar *cc_format_chooser_get_region (GtkWidget *chooser); +void cc_format_chooser_set_region (GtkWidget *chooser, + const gchar *region); + +G_END_DECLS + +#endif /* __CC_FORMAT_CHOOSER_H__ */ diff --git a/panels/region/cc-ibus-utils.c b/panels/region/cc-ibus-utils.c new file mode 100644 index 000000000..54f9fb7b0 --- /dev/null +++ b/panels/region/cc-ibus-utils.c @@ -0,0 +1,41 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#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; + 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); + display_name = g_strdup_printf ("%s (%s)", language, name); + + return display_name; +} + +#endif /* HAVE_IBUS */ diff --git a/panels/region/cc-ibus-utils.h b/panels/region/cc-ibus-utils.h new file mode 100644 index 000000000..12f254538 --- /dev/null +++ b/panels/region/cc-ibus-utils.h @@ -0,0 +1,31 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __CC_IBUS_UTILS_H__ +#define __CC_IBUS_UTILS_H__ + +#include + +G_BEGIN_DECLS + +gchar *engine_get_display_name (IBusEngineDesc *engine_desc); + +G_END_DECLS + +#endif /* __CC_IBUS_UTILS_H__ */ diff --git a/panels/region/cc-input-chooser.c b/panels/region/cc-input-chooser.c new file mode 100644 index 000000000..049075814 --- /dev/null +++ b/panels/region/cc-input-chooser.c @@ -0,0 +1,326 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include + +#include "cc-input-chooser.h" + +#ifdef HAVE_IBUS +#include +#include "cc-ibus-utils.h" +#endif + +#define INPUT_SOURCE_TYPE_XKB "xkb" +#define INPUT_SOURCE_TYPE_IBUS "ibus" + +#define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name)) + +enum { + NAME_COLUMN, + TYPE_COLUMN, + ID_COLUMN, + SETUP_COLUMN, + N_COLUMNS +}; + +static void +filter_clear (GtkEntry *entry, + GtkEntryIconPosition icon_pos, + GdkEvent *event, + gpointer user_data) +{ + gtk_entry_set_text (entry, ""); +} + +static gchar **search_pattern_list; + +static void +filter_changed (GtkBuilder *builder) +{ + GtkTreeModelFilter *filtered_model; + GtkTreeView *tree_view; + GtkTreeSelection *selection; + GtkTreeIter selected_iter; + GtkWidget *filter_entry; + const gchar *pattern; + gchar *upattern; + + filter_entry = WID ("input_source_filter"); + pattern = gtk_entry_get_text (GTK_ENTRY (filter_entry)); + upattern = g_utf8_strup (pattern, -1); + if (!g_strcmp0 (pattern, "")) + g_object_set (G_OBJECT (filter_entry), + "secondary-icon-name", "edit-find-symbolic", + "secondary-icon-activatable", FALSE, + "secondary-icon-sensitive", FALSE, + NULL); + else + g_object_set (G_OBJECT (filter_entry), + "secondary-icon-name", "edit-clear-symbolic", + "secondary-icon-activatable", TRUE, + "secondary-icon-sensitive", TRUE, + NULL); + + if (search_pattern_list != NULL) + g_strfreev (search_pattern_list); + + search_pattern_list = g_strsplit (upattern, " ", -1); + g_free (upattern); + filtered_model = GTK_TREE_MODEL_FILTER (gtk_builder_get_object (builder, "filtered_input_source_model")); + gtk_tree_model_filter_refilter (filtered_model); + + tree_view = GTK_TREE_VIEW (WID ("filtered_input_source_list")); + selection = gtk_tree_view_get_selection (tree_view); + if (gtk_tree_selection_get_selected (selection, NULL, &selected_iter)) + { + GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (filtered_model), + &selected_iter); + gtk_tree_view_scroll_to_cell (tree_view, path, NULL, TRUE, 0.5, 0.5); + gtk_tree_path_free (path); + } + else + { + GtkTreeIter iter; + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (filtered_model), &iter)) + gtk_tree_selection_select_iter (selection, &iter); + } +} + +static void +selection_changed (GtkTreeSelection *selection, + GtkBuilder *builder) +{ + gtk_widget_set_sensitive (WID ("ok-button"), + gtk_tree_selection_get_selected (selection, NULL, NULL)); +} + +static void +row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GtkBuilder *builder) +{ + GtkWidget *add_button; + GtkWidget *dialog; + + add_button = WID ("ok-button"); + dialog = WID ("input_source_chooser"); + if (gtk_widget_is_sensitive (add_button)) + gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); +} + +static void +entry_activated (GtkBuilder *builder, + gpointer data) +{ + row_activated (NULL, NULL, NULL, builder); +} + +static gboolean +filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gchar *name = NULL; + gchar **pattern; + gboolean rv = TRUE; + + if (search_pattern_list == NULL || search_pattern_list[0] == NULL) + return TRUE; + + gtk_tree_model_get (model, iter, + NAME_COLUMN, &name, + -1); + + pattern = search_pattern_list; + do { + gboolean is_pattern_found = FALSE; + gchar *udesc = g_utf8_strup (name, -1); + if (udesc != NULL && g_strstr_len (udesc, -1, *pattern)) + { + is_pattern_found = TRUE; + } + g_free (udesc); + + if (!is_pattern_found) + { + rv = FALSE; + break; + } + + } while (*++pattern != NULL); + g_free (name); + + return rv; +} + +static void +populate_model (GtkListStore *store, + GnomeXkbInfo *xkb_info, + GHashTable *ibus_engines) +{ + GtkTreeIter iter; + const gchar *name; + GList *sources, *tmp; + + sources = gnome_xkb_info_get_all_layouts (xkb_info); + for (tmp = sources; tmp; tmp = tmp->next) + { + 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_list_free (sources); + +#ifdef HAVE_IBUS + if (ibus_engines) + { + gchar *display_name; + + sources = g_hash_table_get_keys (ibus_engines); + for (tmp = sources; tmp; tmp = tmp->next) + { + display_name = engine_get_display_name (g_hash_table_lookup (ibus_engines, tmp->data)); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + NAME_COLUMN, display_name, + TYPE_COLUMN, INPUT_SOURCE_TYPE_IBUS, + ID_COLUMN, tmp->data, + -1); + g_free (display_name); + } + g_list_free (sources); + } +#endif +} + + +GtkWidget * +cc_input_chooser_new (GtkWindow *main_window, + GnomeXkbInfo *xkb_info, + GHashTable *ibus_engines) +{ + GtkBuilder *builder; + GtkWidget *chooser; + GtkWidget *filtered_list; + GtkWidget *filter_entry; + GtkTreeViewColumn *visible_column; + GtkTreeSelection *selection; + GtkListStore *model; + GtkTreeModelFilter *filtered_model; + GtkTreeIter iter; + + builder = gtk_builder_new (); + gtk_builder_add_from_resource (builder, + "/org/gnome/control-center/region/gnome-region-panel-input-chooser.ui", + NULL); + chooser = WID ("input_source_chooser"); + g_object_set_data_full (G_OBJECT (chooser), "builder", builder, g_object_unref); + + filtered_list = WID ("filtered_input_source_list"); + filter_entry = WID ("input_source_filter"); + + g_object_set_data (G_OBJECT (chooser), + "filtered_input_source_list", filtered_list); + visible_column = + gtk_tree_view_column_new_with_attributes ("Input Sources", + gtk_cell_renderer_text_new (), + "text", NAME_COLUMN, + NULL); + + gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window); + + gtk_tree_view_append_column (GTK_TREE_VIEW (filtered_list), + visible_column); + /* We handle searching ourselves, thank you. */ + gtk_tree_view_set_enable_search (GTK_TREE_VIEW (filtered_list), FALSE); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (filtered_list), -1); + + g_signal_connect_swapped (G_OBJECT (filter_entry), "activate", + G_CALLBACK (entry_activated), builder); + g_signal_connect_swapped (G_OBJECT (filter_entry), "notify::text", + G_CALLBACK (filter_changed), builder); + + g_signal_connect (G_OBJECT (filter_entry), "icon-release", + G_CALLBACK (filter_clear), NULL); + + 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")); + + g_object_set_data_full (G_OBJECT (chooser), "xkb-info", g_object_ref (xkb_info), g_object_unref); + g_object_set_data_full (G_OBJECT (chooser), "ibus-engines", g_hash_table_ref (ibus_engines), (GDestroyNotify)g_hash_table_unref); + + populate_model (model, xkb_info, ibus_engines); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), + NAME_COLUMN, GTK_SORT_ASCENDING); + gtk_tree_model_filter_set_visible_func (filtered_model, + filter_func, + NULL, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (filtered_list)); + + g_signal_connect (G_OBJECT (selection), "changed", + G_CALLBACK (selection_changed), builder); + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (filtered_model), &iter)) + gtk_tree_selection_select_iter (selection, &iter); + + g_signal_connect (G_OBJECT (filtered_list), "row-activated", + G_CALLBACK (row_activated), builder); + + gtk_widget_grab_focus (filter_entry); + + gtk_widget_show (chooser); + + return chooser; +} + +gboolean +cc_input_chooser_get_selected (GtkWidget *chooser, + gchar **type, + gchar **id, + gchar **name) +{ + GtkWidget *tv; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeSelection *selection; + + tv = g_object_get_data (G_OBJECT (chooser), "filtered_input_source_list"); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv)); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, + TYPE_COLUMN, type, + ID_COLUMN, id, + NAME_COLUMN, name, + -1); + return TRUE; + } + + return FALSE; +} diff --git a/panels/region/cc-input-chooser.h b/panels/region/cc-input-chooser.h new file mode 100644 index 000000000..394db82bd --- /dev/null +++ b/panels/region/cc-input-chooser.h @@ -0,0 +1,42 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __CC_INPUT_CHOOSER_H__ +#define __CC_INPUT_CHOOSER_H__ + +#include +#include + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include + + +G_BEGIN_DECLS + +GtkWidget *cc_input_chooser_new (GtkWindow *parent, + GnomeXkbInfo *xkb_info, + GHashTable *ibus_engines); +gboolean cc_input_chooser_get_selected (GtkWidget *chooser, + gchar **type, + gchar **id, + gchar **name); + +G_END_DECLS + +#endif /* __CC_INPUT_CHOOSER_H__ */ diff --git a/panels/region/cc-input-options.c b/panels/region/cc-input-options.c new file mode 100644 index 000000000..51c54ebd5 --- /dev/null +++ b/panels/region/cc-input-options.c @@ -0,0 +1,203 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Matthias Clasen + */ + +#define _GNU_SOURCE +#include +#include "cc-input-options.h" + +#include + + +typedef struct { + GtkWidget *dialog; + GtkWidget *same_source; + GtkWidget *per_window_source; + GtkWidget *previous_source; + GtkWidget *next_source; + GtkWidget *alt_next_source; + GSettings *settings; +} CcInputOptionsPrivate; + +#define GET_PRIVATE(options) ((CcInputOptionsPrivate *) g_object_get_data (G_OBJECT (options), "private")) + +static void +cc_input_options_private_free (gpointer data) +{ + CcInputOptionsPrivate *priv = data; + + g_clear_object (&priv->settings); + g_free (priv); +} + +static void +update_shortcut_label (GtkWidget *widget, + const gchar *value) +{ + gchar *text; + guint accel_key, *keycode; + GdkModifierType mods; + + if (value == NULL || *value == '\0') { + gtk_widget_hide (widget); + return; + } + + gtk_accelerator_parse_with_keycode (value, &accel_key, &keycode, &mods); + if (accel_key == 0 && keycode == NULL && mods == 0) { + g_warning ("Failed to parse keyboard shortcut: '%s'", value); + gtk_widget_hide (widget); + return; + } + + text = gtk_accelerator_get_label_with_keycode (gtk_widget_get_display (widget), accel_key, *keycode, mods); + g_free (keycode); + gtk_label_set_text (GTK_LABEL (widget), text); + g_free (text); +} + +static struct +{ + const gchar *value; + const gchar *description; +} input_switcher_options[] = +{ + { "off", N_("Disabled") }, + { "shift-l", N_("Left Shift") }, + { "alt-l", N_("Left Alt") }, + { "ctrl-l", N_("Left Ctrl") }, + { "shift-r", N_("Right Shift") }, + { "alt-r", N_("Right Alt") }, + { "ctrl-r", N_("Right Ctrl") }, + { "alt-shift-l", N_("Left Alt+Shift") }, + { "alt-shift-r", N_("Right Alt+Shift") }, + { "ctrl-shift-l", N_("Left Ctrl+Shift") }, + { "ctrl-shift-r", N_("Right Ctrl+Shift") }, + { "shift-l-shift-r", N_("Left+Right Shift") }, + { "alt-l-alt-r", N_("Left+Right Alt") }, + { "ctrl-l-ctrl-r", N_("Left+Right Ctrl") }, + { "alt-shift", N_("Alt+Shift") }, + { "ctrl-shift", N_("Ctrl+Shift") }, + { "alt-ctrl", N_("Alt+Ctrl") }, + { "caps", N_("Caps") }, + { "shift-caps", N_("Shift+Caps") }, + { "alt-caps", N_("Alt+Caps") }, + { "ctrl-caps", N_("Ctrl+Caps") }, + { NULL, NULL } +}; + +static void +update_shortcuts (GtkWidget *options) +{ + CcInputOptionsPrivate *priv = GET_PRIVATE (options); + gchar **previous; + gchar **next; + gchar *previous_shortcut; + GSettings *settings; + gchar *s; + gint i; + + settings = g_settings_new ("org.gnome.desktop.wm.keybindings"); + + previous = g_settings_get_strv (settings, "switch-input-source-backward"); + next = g_settings_get_strv (settings, "switch-input-source"); + + previous_shortcut = g_strdup (previous[0]); + if (!previous_shortcut && next[0]) { + previous_shortcut = g_strconcat ("", next[0], NULL); + } + + update_shortcut_label (priv->previous_source, previous_shortcut); + update_shortcut_label (priv->next_source, next[0]); + + g_free (previous_shortcut); + + g_strfreev (previous); + g_strfreev (next); + + g_object_unref (settings); + + settings = g_settings_new ("org.gnome.settings-daemon.peripherals.keyboard"); + s = g_settings_get_string (settings, "input-sources-switcher"); + + if (strcmp (s, "off") == 0) { + gtk_widget_hide (priv->alt_next_source); + } else { + for (i = 0; input_switcher_options[i].value; i++) { + if (strcmp (s, input_switcher_options[i].value) == 0) { + gtk_label_set_text (GTK_LABEL (priv->alt_next_source), _(input_switcher_options[i].description)); + break; + } + } + } + g_free (s); + g_object_unref (settings); +} + +#define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name)) + +GtkWidget * +cc_input_options_new (GtkWidget *parent) +{ + GtkBuilder *builder; + GtkWidget *options; + CcInputOptionsPrivate *priv; + GError *error = NULL; + + builder = gtk_builder_new (); + gtk_builder_add_from_resource (builder, "/org/gnome/control-center/region/input-options.ui", &error); + if (error) { + g_warning ("failed to load input options: %s", error->message); + g_error_free (error); + return NULL; + } + + options = WID ("dialog"); + priv = g_new0 (CcInputOptionsPrivate, 1); + g_object_set_data_full (G_OBJECT (options), "private", priv, cc_input_options_private_free); + + priv->same_source = WID ("same-source"); + priv->per_window_source = WID ("per-window-source"); + priv->previous_source = WID ("previous-source"); + priv->next_source = WID ("next-source"); + priv->alt_next_source = WID ("alt-next-source"); + + g_object_bind_property (priv->previous_source, "visible", + WID ("previous-source-label"), "visible", + G_BINDING_DEFAULT); + g_object_bind_property (priv->next_source, "visible", + WID ("next-source-label"), "visible", + G_BINDING_DEFAULT); + g_object_bind_property (priv->alt_next_source, "visible", + WID ("alt-next-source-label"), "visible", + G_BINDING_DEFAULT); + + priv->settings = g_settings_new ("org.gnome.desktop.input-sources"); + g_settings_bind (priv->settings, "per-window", + priv->per_window_source, "active", + G_SETTINGS_BIND_DEFAULT); + + update_shortcuts (options); + + gtk_window_set_transient_for (GTK_WINDOW (options), GTK_WINDOW (parent)); + + return options; +} diff --git a/panels/region/cc-input-options.h b/panels/region/cc-input-options.h new file mode 100644 index 000000000..5d7c4f2c9 --- /dev/null +++ b/panels/region/cc-input-options.h @@ -0,0 +1,35 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: + * Matthias Clasen + */ + +#ifndef __CC_INPUT_OPTIONS_H__ +#define __CC_INPUT_OPTIONS_H__ + +#include +#include + +G_BEGIN_DECLS + +GtkWidget *cc_input_options_new (GtkWidget *parent); + +G_END_DECLS + +#endif /* __CC_FORMAT_CHOOSER_H__ */ diff --git a/panels/region/cc-region-panel.c b/panels/region/cc-region-panel.c index f27120814..f39038661 100644 --- a/panels/region/cc-region-panel.c +++ b/panels/region/cc-region-panel.c @@ -21,42 +21,108 @@ #include #include +#include #include "cc-region-panel.h" #include "cc-region-resources.h" +#include "cc-language-chooser.h" +#include "cc-format-chooser.h" +#include "cc-input-chooser.h" +#include "cc-input-options.h" #include -#include "gnome-region-panel-input.h" -#include "gnome-region-panel-lang.h" -#include "gnome-region-panel-formats.h" -#include "gnome-region-panel-system.h" +#include "cc-common-language.h" + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include +#include + +#ifdef HAVE_IBUS +#include +#include "supported-ibus-engines.h" +#include "cc-ibus-utils.h" +#endif #include "egg-list-box/egg-list-box.h" +#define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" +#define KEY_CURRENT_INPUT_SOURCE "current" +#define KEY_INPUT_SOURCES "sources" + +#define GNOME_SYSTEM_LOCALE_DIR "org.gnome.system.locale" +#define KEY_REGION "region" + +#define INPUT_SOURCE_TYPE_XKB "xkb" +#define INPUT_SOURCE_TYPE_IBUS "ibus" + + +static gboolean +strv_contains (const gchar * const *strv, + const gchar *str) +{ + const gchar * const *p = strv; + for (p = strv; *p; p++) + if (g_strcmp0 (*p, str) == 0) + return TRUE; + + return FALSE; +} + CC_PANEL_REGISTER (CcRegionPanel, cc_region_panel) +#define WID(s) GTK_WIDGET (gtk_builder_get_object (self->priv->builder, s)) + #define REGION_PANEL_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_REGION_PANEL, CcRegionPanelPrivate)) struct _CcRegionPanelPrivate { GtkBuilder *builder; GtkWidget *login_button; + GtkWidget *language_row; + GtkWidget *language_label; GtkWidget *formats_row; + GtkWidget *formats_label; + + GDBusProxy *user; + GSettings *locale_settings; + + gchar *language; + gchar *region; + GtkWidget *options_button; - GtkWidget *input_source_list; + GtkWidget *input_list; + GtkWidget *add_input; + GtkWidget *remove_input; + GtkWidget *show_config; + GtkWidget *show_layout; + + GSettings *input_settings; + GnomeXkbInfo *xkb_info; +#ifdef HAVE_IBUS + IBusBus *ibus; + GHashTable *ibus_engines; + GCancellable *ibus_cancellable; +#endif }; static void -cc_region_panel_finalize (GObject * object) +cc_region_panel_finalize (GObject *object) { - CcRegionPanel *panel; + CcRegionPanel *self = CC_REGION_PANEL (object); + CcRegionPanelPrivate *priv = self->priv; - panel = CC_REGION_PANEL (object); - - if (panel->priv && panel->priv->builder) - g_object_unref (panel->priv->builder); + g_clear_object (&priv->builder); + g_clear_object (&priv->user); + g_clear_object (&priv->locale_settings); + g_clear_object (&priv->input_settings); + g_clear_object (&priv->xkb_info); + 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); G_OBJECT_CLASS (cc_region_panel_parent_class)->finalize (object); } @@ -65,20 +131,23 @@ static void cc_region_panel_constructed (GObject *object) { CcRegionPanel *self = CC_REGION_PANEL (object); + CcRegionPanelPrivate *priv = self->priv; G_OBJECT_CLASS (cc_region_panel_parent_class)->constructed (object); - self->priv->login_button = gtk_button_new_with_label (_("Login Screen")); +#if 0 + priv->login_button = gtk_button_new_with_label (_("Login Screen")); cc_shell_embed_widget_in_header (cc_panel_get_shell (CC_PANEL (object)), - self->priv->login_button); - gtk_widget_show_all (self->priv->login_button); + priv->login_button); + gtk_widget_show_all (priv->login_button); +#endif } static const char * cc_region_panel_get_help_uri (CcPanel *panel) { - return "help:gnome-help/prefs-language"; + return "help:gnome-help/prefs-language"; } static void @@ -101,112 +170,296 @@ update_separator_func (GtkWidget **separator, GtkWidget *before, gpointer user_data) { - if (before == NULL) - return; + if (before == NULL) + return; - if (*separator == NULL) - { - *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); - g_object_ref_sink (*separator); - gtk_widget_show (*separator); - } + if (*separator == NULL) { + *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + g_object_ref_sink (*separator); + gtk_widget_show (*separator); + } } static void -activate_input_child (CcRegionPanel *self, GtkWidget child) -{ -} - -static void -add_language_section (CcRegionPanel *self) +update_language (CcRegionPanel *self, + const gchar *language) +{ + CcRegionPanelPrivate *priv = self->priv; + gchar *name; + + g_free (priv->language); + priv->language = g_strdup (language); + + name = gnome_get_language_from_name (language, language); + gtk_label_set_label (GTK_LABEL (priv->language_label), name); + g_free (name); +} + +static void +store_language (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + g_dbus_proxy_call (priv->user, + "SetLanguage", + g_variant_new ("(s)", priv->language), + 0, + G_MAXINT, + NULL, + NULL, + NULL); +} + +static void +language_response (GtkDialog *chooser, + gint response_id, + CcRegionPanel *self) +{ + const gchar *language; + + language = cc_language_chooser_get_language (GTK_WIDGET (chooser)); + update_language (self, language); + store_language (self); + gtk_widget_destroy (GTK_WIDGET (chooser)); +} + +static void +format_response (GtkDialog *chooser, + gint response_id, + CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + const gchar *region; + + region = cc_format_chooser_get_region (GTK_WIDGET (chooser)); + g_settings_set_string (priv->locale_settings, KEY_REGION, region); + gtk_widget_destroy (GTK_WIDGET (chooser)); +} + +static void +activate_language_child (CcRegionPanel *self, GtkWidget *child) +{ + CcRegionPanelPrivate *priv = self->priv; + GtkWidget *chooser; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self)); + if (child == priv->language_row) { + chooser = cc_language_chooser_new (toplevel); + cc_language_chooser_set_language (chooser, priv->language); + g_signal_connect (chooser, "response", + G_CALLBACK (language_response), self); + gtk_window_present (GTK_WINDOW (chooser)); + } else if (child == priv->formats_row) { + chooser = cc_format_chooser_new (toplevel); + cc_format_chooser_set_region (chooser, priv->region); + g_signal_connect (chooser, "response", + G_CALLBACK (format_response), self); + gtk_window_present (GTK_WINDOW (chooser)); + } +} + +static void +set_initial_language (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + GVariant *p; + gchar *lang; + + p = g_dbus_proxy_get_cached_property (priv->user, "Language"); + lang = g_variant_dup_string (p, NULL); + update_language (self, lang); + g_free (lang); + g_variant_unref (p); +} + +static void +update_region (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + gchar *name; + + g_free (priv->region); + priv->region = g_settings_get_string (priv->locale_settings, KEY_REGION); + name = gnome_get_region_from_name (priv->region, priv->region); + gtk_label_set_label (GTK_LABEL (priv->formats_label), name); + g_free (name); +} + +static void +setup_language_section (CcRegionPanel *self) { CcRegionPanelPrivate *priv = self->priv; - GtkWidget *vbox; - GtkWidget *frame; GtkWidget *widget; - GtkWidget *row; - GtkWidget *label; + gchar *path; + GError *error = NULL; - vbox = GTK_WIDGET (gtk_builder_get_object (priv->builder, "vbox_region")); + path = g_strdup_printf ("/org/freedesktop/Accounts/User%d", getuid ()); + priv->user = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.Accounts", + path, + "org.freedesktop.Accounts.User", + NULL, + &error); + if (priv->user == NULL) { + g_warning ("Failed to get proxy for user '%s': %s", + path, error->message); + g_error_free (error); + g_free (path); + return; + } + g_free (path); - widget = GTK_WIDGET (egg_list_box_new ()); + priv->locale_settings = g_settings_new (GNOME_SYSTEM_LOCALE_DIR); + g_signal_connect_swapped (priv->locale_settings, "changed::" KEY_REGION, + G_CALLBACK (update_region), self); + + priv->language_row = WID ("language_row"); + priv->language_label = WID ("language_label"); + priv->formats_row = WID ("formats_row"); + priv->formats_label = WID ("formats_label"); + + widget = WID ("language_list"); egg_list_box_set_selection_mode (EGG_LIST_BOX (widget), GTK_SELECTION_NONE); egg_list_box_set_separator_funcs (EGG_LIST_BOX (widget), update_separator_func, NULL, NULL); g_signal_connect_swapped (widget, "child-activated", - G_CALLBACK (activate_input_child), self); + G_CALLBACK (activate_language_child), self); - - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); - gtk_widget_set_margin_left (frame, 134); - gtk_widget_set_margin_right (frame, 134); - gtk_widget_set_margin_bottom (frame, 22); - - gtk_container_add (GTK_CONTAINER (frame), widget); - gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0); - - priv->language_row = row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_container_add (GTK_CONTAINER (widget), priv->language_row); - label = gtk_label_new (_("Language")); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_widget_set_margin_left (label, 20); - gtk_widget_set_margin_right (label, 20); - gtk_widget_set_margin_top (label, 6); - gtk_widget_set_margin_bottom (label, 6); - gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0); - - label = gtk_label_new ("English (United Kingdom)"); - gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5); - gtk_widget_set_margin_left (label, 20); - gtk_widget_set_margin_right (label, 20); - gtk_widget_set_margin_top (label, 6); - gtk_widget_set_margin_bottom (label, 6); - gtk_box_pack_start (GTK_BOX (row), label, FALSE, TRUE, 0); - - priv->formats_row = row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_container_add (GTK_CONTAINER (widget), priv->formats_row); - label = gtk_label_new (_("Formats")); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_widget_set_margin_left (label, 20); - gtk_widget_set_margin_right (label, 20); - gtk_widget_set_margin_top (label, 6); - gtk_widget_set_margin_bottom (label, 6); - gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0); - - label = gtk_label_new ("United Kingdom"); - gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5); - gtk_widget_set_margin_left (label, 20); - gtk_widget_set_margin_right (label, 20); - gtk_widget_set_margin_top (label, 6); - gtk_widget_set_margin_bottom (label, 6); - gtk_box_pack_start (GTK_BOX (row), label, FALSE, TRUE, 0); - - gtk_widget_show_all (frame); + set_initial_language (self); + update_region (self); } +#ifdef HAVE_IBUS static void -add_keyboard_layout_row (CcRegionPanel *self, const gchar *name) +update_ibus_active_sources (CcRegionPanel *self) { + CcRegionPanelPrivate *priv = self->priv; + GList *rows, *l; GtkWidget *row; + const gchar *type; + const gchar *id; + IBusEngineDesc *engine_desc; + gchar *display_name; GtkWidget *label; - row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - label = gtk_label_new (name); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_widget_set_margin_left (label, 20); - gtk_widget_set_margin_right (label, 20); - gtk_widget_set_margin_top (label, 6); - gtk_widget_set_margin_bottom (label, 6); - gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0); - gtk_widget_show_all (row); - gtk_container_add (GTK_CONTAINER (self->priv->input_source_list), row); + rows = gtk_container_get_children (GTK_CONTAINER (priv->input_list)); + for (l = rows; l; l = l->next) { + row = l->data; + type = g_object_get_data (G_OBJECT (row), "type"); + id = g_object_get_data (G_OBJECT (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) { + display_name = engine_get_display_name (engine_desc); + label = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "label")); + gtk_label_set_text (GTK_LABEL (label), display_name); + g_free (display_name); + } + } + g_list_free (rows); } static void -add_input_method_row (CcRegionPanel *self, const gchar *name) +fetch_ibus_engines_result (GObject *object, + GAsyncResult *result, + CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + gboolean show_all_sources; + GList *list, *l; + GError *error; + + error = NULL; + list = ibus_bus_list_engines_async_finish (priv->ibus, result, &error); + g_clear_object (&priv->ibus_cancellable); + if (!list && error) { + g_warning ("Couldn't finish IBus request: %s", error->message); + g_error_free (error); + return; + } + + show_all_sources = g_settings_get_boolean (priv->input_settings, "show-all-sources"); + + /* 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 = ibus_engine_desc_get_name (engine); + + if (show_all_sources || strv_contains (supported_ibus_engines, engine_id)) + g_hash_table_replace (priv->ibus_engines, (gpointer)engine_id, engine); + else + g_object_unref (engine); + } + g_list_free (list); + + update_ibus_active_sources (self); +} + +static void +fetch_ibus_engines (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + + priv->ibus_cancellable = g_cancellable_new (); + + ibus_bus_list_engines_async (priv->ibus, + -1, + priv->ibus_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 (priv->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)); +} + +static GDesktopAppInfo * +setup_app_info_for_id (const gchar *id) +{ + GDesktopAppInfo *app_info; + gchar *desktop_file_name; + gchar **strv; + + strv = g_strsplit (id, ":", 2); + desktop_file_name = g_strdup_printf ("ibus-setup-%s.desktop", strv[0]); + g_strfreev (strv); + + app_info = g_desktop_app_info_new (desktop_file_name); + g_free (desktop_file_name); + + return app_info; +} +#endif + +static GtkWidget * +add_input_row (CcRegionPanel *self, + const gchar *type, + const gchar *id, + const gchar *name, + GDesktopAppInfo *app_info) { GtkWidget *row; GtkWidget *label; @@ -221,72 +474,477 @@ add_input_method_row (CcRegionPanel *self, const gchar *name) gtk_widget_set_margin_bottom (label, 6); gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0); - image = gtk_image_new_from_icon_name ("system-run-symbolic", GTK_ICON_SIZE_BUTTON); - gtk_widget_set_margin_left (image, 20); - gtk_widget_set_margin_right (image, 20); - gtk_widget_set_margin_top (image, 6); - gtk_widget_set_margin_bottom (image, 6); - gtk_box_pack_start (GTK_BOX (row), image, FALSE, TRUE, 0); + if (strcmp (type, INPUT_SOURCE_TYPE_IBUS) == 0) { + image = gtk_image_new_from_icon_name ("system-run-symbolic", GTK_ICON_SIZE_BUTTON); + gtk_widget_set_margin_left (image, 20); + gtk_widget_set_margin_right (image, 20); + gtk_widget_set_margin_top (image, 6); + gtk_widget_set_margin_bottom (image, 6); + gtk_box_pack_start (GTK_BOX (row), image, FALSE, TRUE, 0); + } gtk_widget_show_all (row); - gtk_container_add (GTK_CONTAINER (self->priv->input_source_list), row); + gtk_container_add (GTK_CONTAINER (self->priv->input_list), row); + + g_object_set_data (G_OBJECT (row), "label", label); + g_object_set_data (G_OBJECT (row), "type", (gpointer)type); + g_object_set_data_full (G_OBJECT (row), "id", g_strdup (id), g_free); + if (app_info) { + g_object_set_data_full (G_OBJECT (row), "app-info", g_object_ref (app_info), g_object_unref); + } + + return row; } static void -add_input_section (CcRegionPanel *self) +populate_with_active_sources (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + GVariant *sources; + GVariantIter iter; + const gchar *type; + const gchar *id; + const gchar *name; + gchar *display_name; + GDesktopAppInfo *app_info; + + sources = g_settings_get_value (priv->input_settings, "sources"); + g_variant_iter_init (&iter, sources); + while (g_variant_iter_next (&iter, "(&s&s)", &type, &id)) { + display_name = NULL; + app_info = NULL; + + if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { + gnome_xkb_info_get_layout_info (priv->xkb_info, id, &name, NULL, NULL, NULL); + if (!name) { + g_warning ("Couldn't find XKB input source '%s'", id); + continue; + } + display_name = g_strdup (name); + type = INPUT_SOURCE_TYPE_XKB; +#ifdef HAVE_IBUS + } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { + IBusEngineDesc *engine_desc = NULL; + + if (priv->ibus_engines) + engine_desc = g_hash_table_lookup (priv->ibus_engines, id); + if (engine_desc) + display_name = engine_get_display_name (engine_desc); + + app_info = setup_app_info_for_id (id); + type = INPUT_SOURCE_TYPE_IBUS; +#endif + } else { + g_warning ("Unhandled input source type '%s'", type); + continue; + } + + add_input_row (self, type, id, display_name ? display_name : id, app_info); + g_free (display_name); + g_clear_object (&app_info); + } + g_variant_unref (sources); +} + +static void +container_remove_all (GtkContainer *container) +{ + GList *list, *l; + list = gtk_container_get_children (container); + for (l = list; l; l = l->next) { + gtk_container_remove (container, GTK_WIDGET (l->data)); + } + g_list_free (list); +} + +static void +select_by_id (GtkWidget *row, + gpointer data) +{ + const gchar *id = data; + const gchar *row_id; + + row_id = (const gchar *)g_object_get_data (G_OBJECT (row), "id"); + if (g_strcmp0 (row_id, id) == 0) + egg_list_box_select_child (EGG_LIST_BOX (gtk_widget_get_parent (row)), row); +} + +static void +select_input (CcRegionPanel *self, + const gchar *id) +{ + gtk_container_foreach (GTK_CONTAINER (self->priv->input_list), + select_by_id, (gpointer)id); +} + +static void +input_sources_changed (GSettings *settings, + const gchar *key, + CcRegionPanel *self) { CcRegionPanelPrivate *priv = self->priv; - GtkWidget *vbox; - GtkWidget *box; - GtkWidget *widget; - GtkWidget *row; - GtkWidget *label; - GtkWidget *frame; - gchar *s; + GtkWidget *selected; + const gchar *id = NULL; - vbox = GTK_WIDGET (gtk_builder_get_object (priv->builder, "vbox_region")); + selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list)); + if (selected) + id = (const gchar *)g_object_get_data (G_OBJECT (selected), "id"); + container_remove_all (GTK_CONTAINER (priv->input_list)); + populate_with_active_sources (self); + if (id) + select_input (self, id); +} - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_widget_set_margin_left (box, 134); - gtk_widget_set_margin_right (box, 134); - gtk_widget_set_margin_top (box, 0); - gtk_widget_set_margin_bottom (box, 22); - gtk_box_pack_start (GTK_BOX (vbox), box, TRUE, TRUE, 0); - s = g_strdup_printf ("%s", _("Input Sources")); - label = gtk_label_new (s); - g_free (s); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_widget_set_margin_left (label, 6); - gtk_widget_set_margin_right (label, 6); - gtk_widget_set_margin_bottom (label, 6); - row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0); +static void +update_button_sensitivity (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + GtkWidget *selected; + GList *children; + gboolean multiple_sources; - self->priv->options_button = gtk_button_new_with_label (_("Options")); - gtk_widget_set_margin_bottom (label, 6); - gtk_box_pack_start (GTK_BOX (row), self->priv->options_button, FALSE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (box), row, FALSE, TRUE, 0); + children = gtk_container_get_children (GTK_CONTAINER (priv->input_list)); + multiple_sources = g_list_next (children) != NULL; + g_list_free (children); - priv->input_source_list = widget = GTK_WIDGET (egg_list_box_new ()); - egg_list_box_set_selection_mode (EGG_LIST_BOX (widget), + selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list)); + if (selected == NULL) { + gtk_widget_set_sensitive (priv->remove_input, FALSE); + gtk_widget_set_sensitive (priv->show_config, FALSE); + gtk_widget_set_sensitive (priv->show_layout, FALSE); + } else { + GDesktopAppInfo *app_info; + + app_info = (GDesktopAppInfo *)g_object_get_data (G_OBJECT (selected), "app-info"); + + gtk_widget_set_sensitive (priv->show_config, app_info != NULL); + gtk_widget_set_sensitive (priv->show_layout, TRUE); + gtk_widget_set_sensitive (priv->remove_input, multiple_sources); + } + gtk_widget_set_visible (priv->options_button, multiple_sources); +} + +static void +update_configuration (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + const gchar *type; + const gchar *id; + GVariantBuilder builder; + GVariant *old_sources; + const gchar *old_current_type; + const gchar *old_current_id; + guint old_current; + guint old_n_sources; + guint index; + GList *list, *l; + + old_sources = g_settings_get_value (priv->input_settings, KEY_INPUT_SOURCES); + old_current = g_settings_get_uint (priv->input_settings, KEY_CURRENT_INPUT_SOURCE); + old_n_sources = g_variant_n_children (old_sources); + + if (old_n_sources > 0 && old_current < old_n_sources) { + g_variant_get_child (old_sources, old_current, + "(&s&s)", &old_current_type, &old_current_id); + } else { + old_current_type = ""; + old_current_id = ""; + } + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); + index = 0; + list = gtk_container_get_children (GTK_CONTAINER (priv->input_list)); + for (l = list; l; l = l->next) { + type = (const gchar *)g_object_get_data (G_OBJECT (l->data), "type"); + id = (const gchar *)g_object_get_data (G_OBJECT (l->data), "id"); + if (index != old_current && + g_str_equal (type, old_current_type) && + g_str_equal (id, old_current_id)) { + g_settings_set_uint (priv->input_settings, KEY_CURRENT_INPUT_SOURCE, index); + } + g_variant_builder_add (&builder, "(ss)", type, id); + index += 1; + } + g_list_free (list); + + g_settings_set_value (priv->input_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); + g_settings_apply (priv->input_settings); + + g_variant_unref (old_sources); +} + +static void +select_input_child (CcRegionPanel *self, GtkWidget *child) +{ + update_button_sensitivity (self); +} + +static void +chooser_response (GtkWidget *chooser, gint response_id, gpointer data) +{ + CcRegionPanel *self = data; + CcRegionPanelPrivate *priv = self->priv; + gchar *type; + gchar *id; + gchar *name; + GDesktopAppInfo *app_info = NULL; + + if (cc_input_chooser_get_selected (chooser, &type, &id, &name)) { + if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { + g_free (type); + type = INPUT_SOURCE_TYPE_IBUS; +#ifdef HAVE_IBUS + app_info = setup_app_info_for_id (id); +#endif + } else { + g_free (type); + type = INPUT_SOURCE_TYPE_XKB; + } + add_input_row (self, type, id, name, app_info); + g_free (id); + g_free (name); + g_clear_object (&app_info); + + update_button_sensitivity (self); + update_configuration (self); + } + + gtk_widget_destroy (chooser); +} + +static void +add_input (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + GtkWidget *chooser; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self)); + chooser = cc_input_chooser_new (GTK_WINDOW (toplevel), + priv->xkb_info, + priv->ibus_engines); + g_signal_connect (chooser, "response", + G_CALLBACK (chooser_response), self); +} + +static GtkWidget * +find_sibling (GtkContainer *container, GtkWidget *child) +{ + GList *list, *c; + GList *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)) + goto out; + } + + for (l = c->prev; l; l = l->prev) { + sibling = l->data; + if (gtk_widget_get_visible (sibling) && gtk_widget_get_child_visible (sibling)) + goto out; + } + + sibling = NULL; + +out: + g_list_free (list); + + return sibling; +} + +static void +remove_selected_input (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + GtkWidget *selected; + GtkWidget *sibling; + + selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list)); + if (selected == NULL) + return; + + sibling = find_sibling (GTK_CONTAINER (priv->input_list), selected); + gtk_container_remove (GTK_CONTAINER (priv->input_list), selected); + egg_list_box_select_child (EGG_LIST_BOX (priv->input_list), sibling); + + update_button_sensitivity (self); + update_configuration (self); +} + +static void +show_selected_settings (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + GtkWidget *selected; + GdkAppLaunchContext *ctx; + GDesktopAppInfo *app_info; + const gchar *id; + GError *error = NULL; + + selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list)); + if (selected == NULL) + return; + + app_info = (GDesktopAppInfo *)g_object_get_data (G_OBJECT (selected), "app-info"); + 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 ()); + + id = (const gchar *)g_object_get_data (G_OBJECT (selected), "id"); + g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (ctx), + "IBUS_ENGINE_NAME", id); + + 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); + g_error_free (error); + } + + g_object_unref (ctx); +} + +static void +show_selected_layout (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + GtkWidget *selected; + const gchar *type; + const gchar *id; + const gchar *layout; + const gchar *variant; + gchar *commandline; + + selected = egg_list_box_get_selected_child (EGG_LIST_BOX (priv->input_list)); + if (selected == NULL) + return; + + type = (const gchar *)g_object_get_data (G_OBJECT (selected), "type"); + id = (const gchar *)g_object_get_data (G_OBJECT (selected), "id"); + + if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { + gnome_xkb_info_get_layout_info (priv->xkb_info, + id, NULL, NULL, + &layout, &variant); + + if (!layout || !layout[0]) { + g_warning ("Couldn't find XKB input source '%s'", id); + return; + } +#ifdef HAVE_IBUS + } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { + IBusEngineDesc *engine_desc = NULL; + + if (priv->ibus_engines) + engine_desc = g_hash_table_lookup (priv->ibus_engines, id); + + if (engine_desc) { + layout = ibus_engine_desc_get_layout (engine_desc); + variant = ""; + } else { + g_warning ("Couldn't find IBus input source '%s'", id); + return; + } +#endif + } else { + g_warning ("Unhandled input source type '%s'", type); + return; + } + + 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); +} + +static void +options_response (GtkDialog *options, + gint response_id, + CcRegionPanel *self) +{ + gtk_widget_destroy (GTK_WIDGET (options)); +} + + +static void +show_input_options (CcRegionPanel *self) +{ + GtkWidget *toplevel; + GtkWidget *options; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self)); + options = cc_input_options_new (toplevel); + g_signal_connect (options, "response", + G_CALLBACK (options_response), self); + gtk_window_present (GTK_WINDOW (options)); +} + +static void +setup_input_section (CcRegionPanel *self) +{ + CcRegionPanelPrivate *priv = self->priv; + + priv->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); + g_settings_delay (priv->input_settings); + + 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 (self); + else + g_signal_connect_swapped (priv->ibus, "connected", + G_CALLBACK (fetch_ibus_engines), self); + } + maybe_start_ibus (); +#endif + + priv->options_button = WID ("input_options"); + priv->input_list = WID ("input_list"); + priv->add_input = WID ("input_source_add"); + priv->remove_input = WID ("input_source_remove"); + priv->show_config = WID ("input_source_config"); + priv->show_layout = WID ("input_source_layout"); + + g_signal_connect_swapped (priv->options_button, "clicked", + G_CALLBACK (show_input_options), self); + g_signal_connect_swapped (priv->add_input, "clicked", + G_CALLBACK (add_input), self); + g_signal_connect_swapped (priv->remove_input, "clicked", + G_CALLBACK (remove_selected_input), self); + g_signal_connect_swapped (priv->show_config, "clicked", + G_CALLBACK (show_selected_settings), self); + g_signal_connect_swapped (priv->show_layout, "clicked", + G_CALLBACK (show_selected_layout), self); + + egg_list_box_set_selection_mode (EGG_LIST_BOX (priv->input_list), GTK_SELECTION_SINGLE); - egg_list_box_set_separator_funcs (EGG_LIST_BOX (widget), + egg_list_box_set_separator_funcs (EGG_LIST_BOX (priv->input_list), update_separator_func, NULL, NULL); + g_signal_connect_swapped (priv->input_list, "child-selected", + G_CALLBACK (select_input_child), self); - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + g_signal_connect (priv->input_settings, "changed::" KEY_INPUT_SOURCES, + G_CALLBACK (input_sources_changed), self); - gtk_container_add (GTK_CONTAINER (frame), widget); - gtk_box_pack_start (GTK_BOX (box), frame, FALSE, TRUE, 0); + populate_with_active_sources (self); - add_keyboard_layout_row (self, _("English (UK)")); - add_input_method_row (self, _("Japanese (Anthy)")); - add_input_method_row (self, _("Chinese (Pinyin)")); - - gtk_widget_show_all (box); + update_button_sensitivity (self); } static void @@ -310,8 +968,8 @@ cc_region_panel_init (CcRegionPanel *self) return; } - add_language_section (self); - add_input_section (self); + setup_language_section (self); + setup_input_section (self); vbox = GTK_WIDGET (gtk_builder_get_object (priv->builder, "vbox_region")); gtk_widget_reparent (vbox, GTK_WIDGET (self)); diff --git a/panels/region/format-chooser.ui b/panels/region/format-chooser.ui new file mode 100644 index 000000000..3b3d59887 --- /dev/null +++ b/panels/region/format-chooser.ui @@ -0,0 +1,344 @@ + + + + + False + 5 + Formats + dialog + + + False + vertical + 2 + + + False + True + end + + + _Done + True + True + True + True + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + False + 6 + 6 + 6 + 6 + True + 20 + + + True + False + True + vertical + 6 + + + True + True + True + never + never + in + + + True + False + + + True + True + True + fill + fill + + + + + + + False + True + 0 + + + + + False + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + start + start + 20 + True + 6 + 6 + + + True + False + 6 + True + 0 + Preview + + + + + + 0 + 0 + 2 + 1 + + + + + True + False + 1 + Dates + + + + 0 + 1 + 1 + 1 + + + + + True + False + 0 + Wednesday, January 23 + + + 1 + 1 + 1 + 1 + + + + + True + False + 0 + 23 January 2013 + + + 1 + 2 + 1 + 1 + + + + + True + False + 0 + 23/1/13 + + + 1 + 3 + 1 + 1 + + + + + True + False + 1 + Times + + + + 0 + 4 + 1 + 1 + + + + + True + False + 0 + 11:31 AM + + + 1 + 4 + 1 + 1 + + + + + True + False + 1 + Numbers + + + + 0 + 5 + 1 + 1 + + + + + True + False + Measurement + + + + 0 + 6 + 1 + 1 + + + + + True + False + 1 + Paper + + + + 0 + 7 + 1 + 1 + + + + + True + False + 0 + 123,456,789.00 + + + 1 + 5 + 1 + 1 + + + + + True + False + 0 + Metric + + + 1 + 6 + 1 + 1 + + + + + True + False + 0 + A4 + + + 1 + 7 + 1 + 1 + + + + + + + + + + + False + True + 1 + + + + + True + True + 1 + + + + + + ok-button + + + diff --git a/panels/region/input-options.ui b/panels/region/input-options.ui new file mode 100644 index 000000000..570b21919 --- /dev/null +++ b/panels/region/input-options.ui @@ -0,0 +1,225 @@ + + + + + False + 5 + Input Source Options + dialog + + + False + vertical + 2 + + + False + end + + + gtk-close + True + True + True + True + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + False + 6 + 6 + 6 + 6 + 6 + 6 + + + Use the _same source for all windows + True + True + False + True + 0 + True + True + + + 0 + 0 + 2 + 1 + + + + + Allow _different sources for each window + True + True + False + True + 0 + True + True + same-source + + + 0 + 1 + 2 + 1 + + + + + True + False + 6 + 0 + Keyboard Shortcuts + + + + + + 0 + 2 + 2 + 1 + + + + + True + False + 1 + Switch to previous source + + + + 0 + 3 + 1 + 1 + + + + + True + False + 0 + Super+Shift+Space + + + 1 + 3 + 1 + 1 + + + + + True + False + 1 + Switch to next source + + + + 0 + 4 + 1 + 1 + + + + + True + False + 0 + Super+Space + + + 1 + 4 + 1 + 1 + + + + + True + False + 6 + You can change these shortcuts in the keyboard settings + + + + 0 + 6 + 2 + 1 + + + + + True + False + 1 + Alternative switch to next source + + + 0 + 5 + 1 + 1 + + + + + True + False + 0 + Left+Right Alt + + + 1 + 5 + 1 + 1 + + + + + False + True + 1 + + + + + + ok-button + + + diff --git a/panels/region/region.gresource.xml b/panels/region/region.gresource.xml index 815ed369c..8370c0a60 100644 --- a/panels/region/region.gresource.xml +++ b/panels/region/region.gresource.xml @@ -1,8 +1,9 @@ - gnome-region-panel.ui - gnome-region-panel-input-chooser.ui region.ui + format-chooser.ui + input-options.ui + gnome-region-panel-input-chooser.ui diff --git a/panels/region/region.ui b/panels/region/region.ui index 1c885d7c4..4ef26bd82 100644 --- a/panels/region/region.ui +++ b/panels/region/region.ui @@ -5,13 +5,343 @@ False False - + True False - 12 - 3 + vertical - + + True + False + 134 + 134 + 24 + 0 + in + + + True + True + + + True + False + + + True + False + 20 + 20 + 6 + 6 + 0 + Language + + + True + True + 0 + + + + + True + False + 1 + 20 + 20 + 6 + 6 + English (United Kingdom) + + + False + True + 1 + + + + + + + True + False + + + True + False + 20 + 20 + 6 + 6 + 0 + Formats + + + True + True + 0 + + + + + True + False + 1 + 20 + 20 + 6 + 6 + United Kingdom + + + False + True + 1 + + + + + + + + + + + + False + True + 0 + + + + + True + False + 134 + 134 + 24 + vertical + + + True + False + 6 + + + True + False + 6 + 6 + 0 + Input Sources + + + + + + True + True + 0 + + + + + Options + True + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 0 + in + + + True + True + + + + + False + True + 1 + + + + + True + False + icons + False + 1 + + + + True + False + + + True + False + + + True + True + True + + + True + False + list-add-symbolic + 1 + + + + + False + True + 0 + + + + + True + True + True + + + True + False + list-remove-symbolic + 1 + + + + + False + True + 1 + + + + + + + False + + + + + True + False + False + + + True + + + + + True + False + + + True + False + + + True + True + True + + + True + False + emblem-system-symbolic + 1 + + + + + False + True + 0 + + + + + + + False + + + + + True + False + + + True + False + + + True + True + True + + + True + False + input-keyboard-symbolic + 1 + + + + + False + True + 0 + + + + + + + False + + + + + False + True + 2 + + + + + False + True + 1 + diff --git a/panels/region/supported-ibus-engines.h b/panels/region/supported-ibus-engines.h new file mode 100644 index 000000000..33e60f9c4 --- /dev/null +++ b/panels/region/supported-ibus-engines.h @@ -0,0 +1,212 @@ +static const gchar *supported_ibus_engines[] = { + /* Simplified Chinese */ + "pinyin", + "bopomofo", + "wubi", + "erbi", + /* Default in Fedora, where ibus-libpinyin replaces ibus-pinyin */ + "libpinyin", + "libbopomofo", + + /* Traditional Chinese */ + /* https://bugzilla.gnome.org/show_bug.cgi?id=680840 */ + "chewing", + "cangjie5", + "cangjie3", + "quick5", + "quick3", + "stroke5", + + /* Japanese */ + "anthy", + "mozc-jp", + "skk", + + /* Korean */ + "hangul", + + /* Thai */ + "m17n:th:kesmanee", + "m17n:th:pattachote", + "m17n:th:tis820", + + /* Vietnamese */ + "m17n:vi:tcvn", + "m17n:vi:telex", + "m17n:vi:viqr", + "m17n:vi:vni", + "Unikey", + + /* Sinhala */ + "m17n:si:wijesekera", + "m17n:si:phonetic-dynamic", + "m17n:si:trans", + "sayura", + + /* Indic */ + /* https://fedoraproject.org/wiki/I18N/Indic#Keyboard_Layouts */ + + /* Assamese */ + "m17n:as:phonetic", + "m17n:as:inscript", + "m17n:as:itrans", + + /* Bengali */ + "m17n:bn:inscript", + "m17n:bn:itrans", + "m17n:bn:probhat", + + /* Gujarati */ + "m17n:gu:inscript", + "m17n:gu:itrans", + "m17n:gu:phonetic", + + /* Hindi */ + "m17n:hi:inscript", + "m17n:hi:itrans", + "m17n:hi:phonetic", + "m17n:hi:remington", + "m17n:hi:typewriter", + "m17n:hi:vedmata", + + /* Kannada */ + "m17n:kn:kgp", + "m17n:kn:inscript", + "m17n:kn:itrans", + + /* Kashmiri */ + "m17n:ks:inscript", + + /* Maithili */ + "m17n:mai:inscript", + + /* Malayalam */ + "m17n:ml:inscript", + "m17n:ml:itrans", + "m17n:ml:mozhi", + "m17n:ml:swanalekha", + + /* Marathi */ + "m17n:mr:inscript", + "m17n:mr:itrans", + "m17n:mr:phonetic", + + /* Nepali */ + "m17n:ne:rom", + "m17n:ne:trad", + + /* Oriya */ + "m17n:or:inscript", + "m17n:or:itrans", + "m17n:or:phonetic", + + /* Punjabi */ + "m17n:pa:inscript", + "m17n:pa:itrans", + "m17n:pa:phonetic", + "m17n:pa:jhelum", + + /* Sanskrit */ + "m17n:sa:harvard-kyoto", + + /* Sindhi */ + "m17n:sd:inscript", + + /* Tamil */ + "m17n:ta:tamil99", + "m17n:ta:inscript", + "m17n:ta:itrans", + "m17n:ta:phonetic", + "m17n:ta:lk-renganathan", + "m17n:ta:vutam", + "m17n:ta:typewriter", + + /* Telugu */ + "m17n:te:inscript", + "m17n:te:apple", + "m17n:te:pothana", + "m17n:te:rts", + + /* Urdu */ + "m17n:ur:phonetic", + + /* Inscript2 - https://bugzilla.gnome.org/show_bug.cgi?id=684854 */ + "m17n:as:inscript2", + "m17n:bn:inscript2", + "m17n:brx:inscript2-deva", + "m17n:doi:inscript2-deva", + "m17n:gu:inscript2", + "m17n:hi:inscript2", + "m17n:kn:inscript2", + "m17n:kok:inscript2-deva", + "m17n:mai:inscript2", + "m17n:ml:inscript2", + "m17n:mni:inscript2-beng", + "m17n:mni:inscript2-mtei", + "m17n:mr:inscript2", + "m17n:ne:inscript2-deva", + "m17n:or:inscript2", + "m17n:pa:inscript2-guru", + "m17n:sa:inscript2", + "m17n:sat:inscript2-deva", + "m17n:sat:inscript2-olck", + "m17n:sd:inscript2-deva", + "m17n:ta:inscript2", + "m17n:te:inscript2", + + /* No corresponding XKB map available for the languages */ + + /* Chinese Yi */ + "m17n:ii:phonetic", + + /* Tai-Viet */ + "m17n:tai:sonla", + + /* Kazakh in Arabic script */ + "m17n:kk:arabic", + + /* Yiddish */ + "m17n:yi:yivo", + + /* Canadian Aboriginal languages */ + "m17n:ath:phonetic", + "m17n:bla:phonetic", + "m17n:cr:western", + "m17n:iu:phonetic", + "m17n:nsk:phonetic", + "m17n:oj:phonetic", + + /* Non-trivial engines, like transliteration-based instead of + keymap-based. Confirmation needed that the engines below are + actually used by local language users. */ + + /* Tibetan */ + "m17n:bo:ewts", + "m17n:bo:tcrc", + "m17n:bo:wylie", + + /* Esperanto */ + "m17n:eo:h-f", + "m17n:eo:h", + "m17n:eo:plena", + "m17n:eo:q", + "m17n:eo:vi", + "m17n:eo:x", + + /* Amharic */ + "m17n:am:sera", + + /* Russian */ + "m17n:ru:translit", + + /* Classical Greek */ + "m17n:grc:mizuochi", + + /* Lao */ + "m17n:lo:lrt", + + /* Postfix modifier input methods */ + "m17n:da:post", + "m17n:sv:post", + NULL +};