diff options
Diffstat (limited to '')
-rw-r--r-- | panels/keyboard/cc-xkb-modifier-dialog.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/panels/keyboard/cc-xkb-modifier-dialog.c b/panels/keyboard/cc-xkb-modifier-dialog.c new file mode 100644 index 0000000..da8116e --- /dev/null +++ b/panels/keyboard/cc-xkb-modifier-dialog.c @@ -0,0 +1,355 @@ +/* cc-xkb-modifier-dialog.c + * + * Copyright 2019 Bastien Nocera <hadess@hadess.net> + * + * 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/>. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <glib/gi18n.h> +#include <adwaita.h> + +#include "cc-xkb-modifier-dialog.h" + +struct _CcXkbModifierDialog +{ + GtkDialog parent_instance; + + GtkLabel *description_label; + GtkSwitch *enabled_switch; + GtkListBox *listbox; + GtkListBox *switch_listbox; + AdwActionRow *switch_row; + + GSettings *input_source_settings; + const CcXkbModifier *modifier; + GSList *radio_group; +}; + +G_DEFINE_TYPE (CcXkbModifierDialog, cc_xkb_modifier_dialog, GTK_TYPE_DIALOG) + +static const gchar *custom_css = +".xkb-option-button {" +" padding: 12px;" +"}"; + +static const CcXkbOption* +get_xkb_option_from_name (const CcXkbModifier *modifier, const gchar* name) +{ + const CcXkbOption *options = modifier->options; + int i; + + for (i = 0; options[i].label && options[i].xkb_option; i++) + { + if (g_str_equal (name, options[i].xkb_option)) + return &options[i]; + } + + return NULL; +} + +static GtkCheckButton * +get_radio_button_from_xkb_option_name (CcXkbModifierDialog *self, + const gchar *name) +{ + gchar *xkb_option; + GSList *l; + + for (l = self->radio_group; l != NULL; l = l->next) + { + xkb_option = g_object_get_data (l->data, "xkb-option"); + if (g_strcmp0 (xkb_option, name) == 0) + return l->data; + } + + return NULL; +} + +static void +update_active_radio (CcXkbModifierDialog *self) +{ + g_auto(GStrv) options = NULL; + GtkCheckButton *rightalt_radio; + const CcXkbOption *default_option; + guint i; + + options = g_settings_get_strv (self->input_source_settings, "xkb-options"); + + for (i = 0; options != NULL && options[i] != NULL; i++) + { + GtkCheckButton *radio; + + if (!g_str_has_prefix (options[i], self->modifier->prefix)) + continue; + + radio = get_radio_button_from_xkb_option_name (self, options[i]); + + if (!radio) + continue; + + gtk_check_button_set_active (GTK_CHECK_BUTTON (radio), TRUE); + gtk_switch_set_active (self->enabled_switch, TRUE); + return; + } + + if (self->modifier->default_option != NULL) + { + default_option = get_xkb_option_from_name(self->modifier, self->modifier->default_option); + rightalt_radio = get_radio_button_from_xkb_option_name (self, default_option->xkb_option); + gtk_check_button_set_active (GTK_CHECK_BUTTON (rightalt_radio), TRUE); + gtk_switch_set_active (self->enabled_switch, TRUE); + } + else + { + gtk_switch_set_active (self->enabled_switch, FALSE); + } +} + +static void +set_xkb_option (CcXkbModifierDialog *self, + gchar *xkb_option) +{ + g_autoptr(GPtrArray) array = NULL; + g_auto(GStrv) options = NULL; + gboolean found; + guint i; + + /* Either replace the existing "<modifier>:" option in the string + * array, or add the option at the end + */ + array = g_ptr_array_new (); + options = g_settings_get_strv (self->input_source_settings, "xkb-options"); + found = FALSE; + + for (i = 0; options != NULL && options[i] != NULL; i++) + { + if (g_str_has_prefix (options[i], self->modifier->prefix)) + { + if (!found && xkb_option != NULL) + g_ptr_array_add (array, xkb_option); + found = TRUE; + } + else + { + g_ptr_array_add (array, options[i]); + } + } + + if (!found && xkb_option != NULL) + g_ptr_array_add (array, xkb_option); + + g_ptr_array_add (array, NULL); + + g_settings_set_strv (self->input_source_settings, + "xkb-options", + (const gchar * const *) array->pdata); +} + +static void +on_active_radio_changed_cb (CcXkbModifierDialog *self, + GtkCheckButton *radio) +{ + gchar *xkb_option; + + if (!gtk_check_button_get_active (GTK_CHECK_BUTTON (radio))) + return; + + if (!gtk_switch_get_state (self->enabled_switch)) + return; + + xkb_option = (gchar *)g_object_get_data (G_OBJECT (radio), "xkb-option"); + set_xkb_option (self, xkb_option); +} + +static void +on_xkb_options_changed_cb (CcXkbModifierDialog *self) +{ + if (self->modifier == NULL) + update_active_radio (self); +} + +static gboolean +enable_switch_changed_cb (GtkSwitch *widget, + gboolean state, + gpointer user_data) +{ + CcXkbModifierDialog *self = user_data; + gchar *xkb_option; + GSList *l; + + gtk_widget_set_sensitive (GTK_WIDGET (self->listbox), state); + + if (state) + { + for (l = self->radio_group; l != NULL; l = l->next) + { + if (gtk_check_button_get_active (l->data)) + { + xkb_option = (gchar *)g_object_get_data (l->data, "xkb-option"); + set_xkb_option (self, xkb_option); + break; + } + } + } + else + { + set_xkb_option (self, NULL); + } + + return FALSE; +} + +static void +cc_xkb_modifier_dialog_finalize (GObject *object) +{ + CcXkbModifierDialog *self = (CcXkbModifierDialog *)object; + + g_clear_object (&self->input_source_settings); + + G_OBJECT_CLASS (cc_xkb_modifier_dialog_parent_class)->finalize (object); +} + +static void +cc_xkb_modifier_dialog_class_init (CcXkbModifierDialogClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = cc_xkb_modifier_dialog_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/keyboard/cc-xkb-modifier-dialog.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, description_label); + gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, enabled_switch); + gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, listbox); + gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, switch_listbox); + gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, switch_row); + + gtk_widget_class_bind_template_callback (widget_class, enable_switch_changed_cb); +} + +static void +add_radio_buttons (CcXkbModifierDialog *self) +{ + g_autoptr (GSList) group = NULL; + GtkWidget *row, *radio_button, *last_button = NULL; + CcXkbOption *options = self->modifier->options; + int i; + + for (i = 0; options[i].label && options[i].xkb_option; i++) + { + row = g_object_new (GTK_TYPE_LIST_BOX_ROW, + "visible", TRUE, + "selectable", FALSE, + NULL); + gtk_list_box_append (self->listbox, row); + + radio_button = g_object_new (GTK_TYPE_CHECK_BUTTON, + "label", g_dpgettext2 (NULL, "keyboard key", options[i].label), + "group", last_button, + NULL); + gtk_widget_add_css_class (radio_button, "xkb-option-button"); + g_object_set_data (G_OBJECT (radio_button), "xkb-option", options[i].xkb_option); + g_signal_connect_object (radio_button, "toggled", (GCallback)on_active_radio_changed_cb, self, G_CONNECT_SWAPPED); + gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), radio_button); + + last_button = radio_button; + group = g_slist_prepend (group, radio_button); + } + + self->radio_group = NULL; + if (last_button != NULL) + self->radio_group = g_steal_pointer (&group); +} + +static void +cc_xkb_modifier_dialog_init (CcXkbModifierDialog *self) +{ + g_autoptr(GtkCssProvider) provider = NULL; + + gtk_widget_init_template (GTK_WIDGET (self)); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_data (provider, custom_css, -1); + + gtk_style_context_add_provider_for_display (gdk_display_get_default (), + GTK_STYLE_PROVIDER (provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1); + + self->modifier = NULL; + + self->input_source_settings = g_settings_new ("org.gnome.desktop.input-sources"); + g_signal_connect_object (self->input_source_settings, + "changed::xkb-options", + G_CALLBACK (on_xkb_options_changed_cb), + self, G_CONNECT_SWAPPED); +} + +CcXkbModifierDialog * +cc_xkb_modifier_dialog_new (GSettings *input_settings, + const CcXkbModifier *modifier) +{ + CcXkbModifierDialog *self; + + self = g_object_new (CC_TYPE_XKB_MODIFIER_DIALOG, + "use-header-bar", TRUE, + NULL); + self->input_source_settings = g_object_ref (input_settings); + + self->modifier = modifier; + gtk_window_set_title (GTK_WINDOW (self), gettext (modifier->title)); + adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->switch_row), gettext (modifier->title)); + gtk_label_set_markup (self->description_label, gettext (modifier->description)); + gtk_widget_set_visible (GTK_WIDGET (self->switch_listbox), modifier->default_option == NULL); + add_radio_buttons (self); + update_active_radio (self); + gtk_widget_set_sensitive (GTK_WIDGET (self->listbox), gtk_switch_get_state (self->enabled_switch)); + + return self; +} + +gboolean +xcb_modifier_transform_binding_to_label (GValue *value, + GVariant *variant, + gpointer user_data) +{ + const CcXkbModifier *modifier = user_data; + const CcXkbOption *entry = NULL; + const char **items; + guint i; + + items = g_variant_get_strv (variant, NULL); + + for (i = 0; items != NULL && items[i] != NULL; i++) + { + entry = get_xkb_option_from_name (modifier, items[i]); + if (entry != NULL) + break; + } + + if (entry == NULL && modifier->default_option == NULL) + { + g_value_set_string (value, _("Disabled")); + return TRUE; + } + else if (entry == NULL) + { + entry = get_xkb_option_from_name(modifier, modifier->default_option); + } + + g_value_set_string (value, + g_dpgettext2 (NULL, "keyboard key", entry->label)); + return TRUE; +} |