GCC Code Coverage Report


Directory: ./
File: panels/keyboard/cc-xkb-modifier-dialog.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 141 0.0%
Functions: 0 16 0.0%
Branches: 0 75 0.0%

Line Branch Exec Source
1 /* cc-xkb-modifier-dialog.c
2 *
3 * Copyright 2019 Bastien Nocera <hadess@hadess.net>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-2.0-or-later
19 */
20
21 #include <glib/gi18n.h>
22 #include <adwaita.h>
23
24 #include "cc-xkb-modifier-dialog.h"
25
26 struct _CcXkbModifierDialog
27 {
28 AdwDialog parent_instance;
29
30 AdwPreferencesPage *xkb_modifier_page;
31 GtkSwitch *enabled_switch;
32 AdwPreferencesGroup *options_group;
33 AdwPreferencesGroup *switch_group;
34 AdwActionRow *switch_row;
35
36 GSettings *input_source_settings;
37 const CcXkbModifier *modifier;
38 GSList *radio_group;
39 };
40
41 G_DEFINE_TYPE (CcXkbModifierDialog, cc_xkb_modifier_dialog, ADW_TYPE_DIALOG)
42
43 static const CcXkbOption*
44 get_xkb_option_from_name (const CcXkbModifier *modifier, const gchar* name)
45 {
46 const CcXkbOption *options = modifier->options;
47 int i;
48
49 for (i = 0; options[i].label && options[i].xkb_option; i++)
50 {
51 if (g_str_equal (name, options[i].xkb_option))
52 return &options[i];
53 }
54
55 return NULL;
56 }
57
58 static GtkCheckButton *
59 get_radio_button_from_xkb_option_name (CcXkbModifierDialog *self,
60 const gchar *name)
61 {
62 gchar *xkb_option;
63 GSList *l;
64
65 for (l = self->radio_group; l != NULL; l = l->next)
66 {
67 xkb_option = g_object_get_data (l->data, "xkb-option");
68 if (g_strcmp0 (xkb_option, name) == 0)
69 return l->data;
70 }
71
72 return NULL;
73 }
74
75 static void
76 update_active_radio (CcXkbModifierDialog *self)
77 {
78 g_auto(GStrv) options = NULL;
79 GtkCheckButton *rightalt_radio;
80 const CcXkbOption *default_option;
81 guint i;
82
83 options = g_settings_get_strv (self->input_source_settings, "xkb-options");
84
85 for (i = 0; options != NULL && options[i] != NULL; i++)
86 {
87 GtkCheckButton *radio;
88
89 if (!g_str_has_prefix (options[i], self->modifier->prefix))
90 continue;
91
92 radio = get_radio_button_from_xkb_option_name (self, options[i]);
93
94 if (!radio)
95 continue;
96
97 gtk_check_button_set_active (GTK_CHECK_BUTTON (radio), TRUE);
98 gtk_switch_set_active (self->enabled_switch, TRUE);
99 return;
100 }
101
102 if (self->modifier->default_option != NULL)
103 {
104 default_option = get_xkb_option_from_name(self->modifier, self->modifier->default_option);
105 rightalt_radio = get_radio_button_from_xkb_option_name (self, default_option->xkb_option);
106 gtk_check_button_set_active (GTK_CHECK_BUTTON (rightalt_radio), TRUE);
107 gtk_switch_set_active (self->enabled_switch, TRUE);
108 }
109 else
110 {
111 gtk_switch_set_active (self->enabled_switch, FALSE);
112 }
113 }
114
115 static void
116 set_xkb_option (CcXkbModifierDialog *self,
117 gchar *xkb_option)
118 {
119 g_autoptr(GPtrArray) array = NULL;
120 g_auto(GStrv) options = NULL;
121 gboolean found;
122 guint i;
123
124 /* Either replace the existing "<modifier>:" option in the string
125 * array, or add the option at the end
126 */
127 array = g_ptr_array_new ();
128 options = g_settings_get_strv (self->input_source_settings, "xkb-options");
129 found = FALSE;
130
131 for (i = 0; options != NULL && options[i] != NULL; i++)
132 {
133 if (g_str_has_prefix (options[i], self->modifier->prefix))
134 {
135 if (!found && xkb_option != NULL)
136 g_ptr_array_add (array, xkb_option);
137 found = TRUE;
138 }
139 else
140 {
141 g_ptr_array_add (array, options[i]);
142 }
143 }
144
145 if (!found && xkb_option != NULL)
146 g_ptr_array_add (array, xkb_option);
147
148 g_ptr_array_add (array, NULL);
149
150 g_settings_set_strv (self->input_source_settings,
151 "xkb-options",
152 (const gchar * const *) array->pdata);
153 }
154
155 static void
156 on_active_radio_changed_cb (CcXkbModifierDialog *self,
157 GtkCheckButton *radio)
158 {
159 gchar *xkb_option;
160
161 if (!gtk_check_button_get_active (GTK_CHECK_BUTTON (radio)))
162 return;
163
164 if (!gtk_switch_get_state (self->enabled_switch))
165 return;
166
167 xkb_option = (gchar *)g_object_get_data (G_OBJECT (radio), "xkb-option");
168 set_xkb_option (self, xkb_option);
169 }
170
171 static void
172 on_xkb_options_changed_cb (CcXkbModifierDialog *self)
173 {
174 if (self->modifier == NULL)
175 update_active_radio (self);
176 }
177
178 static gboolean
179 enable_switch_changed_cb (CcXkbModifierDialog *self,
180 gboolean state)
181 {
182 gchar *xkb_option;
183 GSList *l;
184
185 gtk_widget_set_sensitive (GTK_WIDGET (self->options_group), state);
186
187 if (state)
188 {
189 for (l = self->radio_group; l != NULL; l = l->next)
190 {
191 if (gtk_check_button_get_active (l->data))
192 {
193 xkb_option = (gchar *)g_object_get_data (l->data, "xkb-option");
194 set_xkb_option (self, xkb_option);
195 break;
196 }
197 }
198 }
199 else
200 {
201 set_xkb_option (self, NULL);
202 }
203
204 return FALSE;
205 }
206
207 static void
208 cc_xkb_modifier_dialog_finalize (GObject *object)
209 {
210 CcXkbModifierDialog *self = (CcXkbModifierDialog *)object;
211
212 g_clear_object (&self->input_source_settings);
213
214 G_OBJECT_CLASS (cc_xkb_modifier_dialog_parent_class)->finalize (object);
215 }
216
217 static void
218 cc_xkb_modifier_dialog_class_init (CcXkbModifierDialogClass *klass)
219 {
220 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
221 GObjectClass *object_class = G_OBJECT_CLASS (klass);
222
223 object_class->finalize = cc_xkb_modifier_dialog_finalize;
224
225 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "window.close", NULL);
226
227 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/keyboard/cc-xkb-modifier-dialog.ui");
228
229 gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, xkb_modifier_page);
230 gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, enabled_switch);
231 gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, options_group);
232 gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, switch_group);
233 gtk_widget_class_bind_template_child (widget_class, CcXkbModifierDialog, switch_row);
234
235 gtk_widget_class_bind_template_callback (widget_class, enable_switch_changed_cb);
236 }
237
238 static void
239 add_radio_buttons (CcXkbModifierDialog *self)
240 {
241 g_autoptr (GSList) group = NULL;
242 GtkWidget *row, *radio_button, *last_button = NULL;
243 CcXkbOption *options = self->modifier->options;
244 int i;
245
246 for (i = 0; options[i].label && options[i].xkb_option; i++)
247 {
248 row = g_object_new (ADW_TYPE_ACTION_ROW,
249 "selectable", FALSE,
250 NULL);
251 adw_preferences_group_add (self->options_group, row);
252
253 radio_button = g_object_new (GTK_TYPE_CHECK_BUTTON,
254 "valign", GTK_ALIGN_CENTER,
255 "group", last_button,
256 NULL);
257 g_object_set_data (G_OBJECT (radio_button), "xkb-option", options[i].xkb_option);
258 g_signal_connect_object (radio_button, "toggled", (GCallback)on_active_radio_changed_cb, self, G_CONNECT_SWAPPED);
259 adw_action_row_add_prefix (ADW_ACTION_ROW (row), radio_button);
260 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), options[i].label);
261 adw_action_row_set_activatable_widget (ADW_ACTION_ROW (row), radio_button);
262
263 last_button = radio_button;
264 group = g_slist_prepend (group, radio_button);
265 }
266
267 self->radio_group = NULL;
268 if (last_button != NULL)
269 self->radio_group = g_steal_pointer (&group);
270 }
271
272 static void
273 cc_xkb_modifier_dialog_init (CcXkbModifierDialog *self)
274 {
275 gtk_widget_init_template (GTK_WIDGET (self));
276
277 self->modifier = NULL;
278
279 self->input_source_settings = g_settings_new ("org.gnome.desktop.input-sources");
280 g_signal_connect_object (self->input_source_settings,
281 "changed::xkb-options",
282 G_CALLBACK (on_xkb_options_changed_cb),
283 self, G_CONNECT_SWAPPED);
284 }
285
286 CcXkbModifierDialog *
287 cc_xkb_modifier_dialog_new (GSettings *input_settings,
288 const CcXkbModifier *modifier)
289 {
290 CcXkbModifierDialog *self;
291
292 self = g_object_new (CC_TYPE_XKB_MODIFIER_DIALOG, NULL);
293 self->input_source_settings = g_object_ref (input_settings);
294
295 self->modifier = modifier;
296 adw_dialog_set_title (ADW_DIALOG (self), gettext (modifier->title));
297 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->switch_row), gettext (modifier->title));
298 adw_preferences_page_set_description (self->xkb_modifier_page, gettext (modifier->description));
299 gtk_widget_set_visible (GTK_WIDGET (self->switch_group), modifier->default_option == NULL);
300 add_radio_buttons (self);
301 update_active_radio (self);
302 gtk_widget_set_sensitive (GTK_WIDGET (self->options_group), gtk_switch_get_state (self->enabled_switch));
303
304 return self;
305 }
306
307 gboolean
308 xcb_modifier_transform_binding_to_label (GValue *value,
309 GVariant *variant,
310 gpointer user_data)
311 {
312 const CcXkbModifier *modifier = user_data;
313 const CcXkbOption *entry = NULL;
314 const char **items;
315 guint i;
316
317 items = g_variant_get_strv (variant, NULL);
318
319 for (i = 0; items != NULL && items[i] != NULL; i++)
320 {
321 entry = get_xkb_option_from_name (modifier, items[i]);
322 if (entry != NULL)
323 break;
324 }
325
326 if (entry == NULL && modifier->default_option == NULL)
327 {
328 g_value_set_string (value, _("Disabled"));
329 return TRUE;
330 }
331 else if (entry == NULL)
332 {
333 entry = get_xkb_option_from_name(modifier, modifier->default_option);
334 }
335
336 g_value_set_string (value,
337 g_dpgettext2 (NULL, "keyboard key", entry->label));
338 return TRUE;
339 }
340