1/*
2 * Copyright © 2013 Canonical Limited
3 * Copyright © 2016 Sébastien Wilmet
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the licence, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Ryan Lortie <desrt@desrt.ca>
19 * Sébastien Wilmet <swilmet@gnome.org>
20 */
21
22#include "config.h"
23
24#include "gtkapplicationaccelsprivate.h"
25
26#include "gtkactionmuxerprivate.h"
27#include "gtkshortcut.h"
28#include "gtkshortcutaction.h"
29#include "gtkshortcuttrigger.h"
30
31struct _GtkApplicationAccels
32{
33 GObject parent;
34
35 GListModel *shortcuts;
36};
37
38G_DEFINE_TYPE (GtkApplicationAccels, gtk_application_accels, G_TYPE_OBJECT)
39
40static void
41gtk_application_accels_finalize (GObject *object)
42{
43 GtkApplicationAccels *accels = GTK_APPLICATION_ACCELS (ptr: object);
44
45 g_list_store_remove_all (store: G_LIST_STORE (ptr: accels->shortcuts));
46 g_object_unref (object: accels->shortcuts);
47
48 G_OBJECT_CLASS (gtk_application_accels_parent_class)->finalize (object);
49}
50
51static void
52gtk_application_accels_class_init (GtkApplicationAccelsClass *klass)
53{
54 GObjectClass *object_class = G_OBJECT_CLASS (klass);
55
56 object_class->finalize = gtk_application_accels_finalize;
57}
58
59static void
60gtk_application_accels_init (GtkApplicationAccels *accels)
61{
62 accels->shortcuts = G_LIST_MODEL (ptr: g_list_store_new (GTK_TYPE_SHORTCUT));
63}
64
65GtkApplicationAccels *
66gtk_application_accels_new (void)
67{
68 return g_object_new (GTK_TYPE_APPLICATION_ACCELS, NULL);
69}
70
71void
72gtk_application_accels_set_accels_for_action (GtkApplicationAccels *accels,
73 const char *detailed_action_name,
74 const char * const *accelerators)
75{
76 char *action_name;
77 GVariant *target;
78 GtkShortcut *shortcut;
79 GtkShortcutTrigger *trigger = NULL;
80 GError *error = NULL;
81 guint i;
82
83 if (!g_action_parse_detailed_name (detailed_name: detailed_action_name, action_name: &action_name, target_value: &target, error: &error))
84 {
85 g_critical ("Error parsing action name: %s", error->message);
86 g_error_free (error);
87 return;
88 }
89
90 /* remove the accelerator if it already exists */
91 for (i = 0; i < g_list_model_get_n_items (list: accels->shortcuts); i++)
92 {
93 GtkShortcut *shortcut_i = g_list_model_get_item (list: accels->shortcuts, position: i);
94 GtkShortcutAction *action = gtk_shortcut_get_action (self: shortcut_i);
95 GVariant *args = gtk_shortcut_get_arguments (self: shortcut_i);
96
97 if (!GTK_IS_NAMED_ACTION (ptr: action) ||
98 !g_str_equal (v1: gtk_named_action_get_action_name (self: GTK_NAMED_ACTION (ptr: action)), v2: action_name))
99 {
100 g_object_unref (object: shortcut_i);
101 continue;
102 }
103
104 if ((target == NULL && args != NULL) ||
105 (target != NULL && (args == NULL || !g_variant_equal (one: target, two: args))))
106 {
107 g_object_unref (object: shortcut_i);
108 continue;
109 }
110
111 g_list_store_remove (store: G_LIST_STORE (ptr: accels->shortcuts), position: i);
112 g_object_unref (object: shortcut_i);
113 break;
114 }
115
116 if (accelerators == NULL)
117 goto out;
118
119 for (i = 0; accelerators[i]; i++)
120 {
121 GtkShortcutTrigger *new_trigger;
122 guint key, modifier;
123
124 if (!gtk_accelerator_parse (accelerator: accelerators[i], accelerator_key: &key, accelerator_mods: &modifier))
125 {
126 g_critical ("Unable to parse accelerator '%s': ignored request to install accelerators",
127 accelerators[i]);
128 g_clear_object (&trigger);
129 goto out;
130 }
131 new_trigger = gtk_keyval_trigger_new (keyval: key, modifiers: modifier);
132 if (trigger)
133 trigger = gtk_alternative_trigger_new (first: trigger, second: new_trigger);
134 else
135 trigger = new_trigger;
136 }
137 if (trigger == NULL)
138 goto out;
139
140 shortcut = gtk_shortcut_new (trigger, action: gtk_named_action_new (name: action_name));
141 gtk_shortcut_set_arguments (self: shortcut, args: target);
142 g_list_store_append (store: G_LIST_STORE (ptr: accels->shortcuts), item: shortcut);
143 g_object_unref (object: shortcut);
144
145out:
146 g_free (mem: action_name);
147 if (target)
148 g_variant_unref (value: target);
149}
150
151static void
152append_accelerators (GPtrArray *accels,
153 GtkShortcutTrigger *trigger)
154{
155 if (GTK_IS_KEYVAL_TRIGGER (ptr: trigger))
156 {
157 GtkKeyvalTrigger *kt = GTK_KEYVAL_TRIGGER (ptr: trigger);
158 guint keyval = gtk_keyval_trigger_get_keyval (self: kt);
159 GdkModifierType mods = gtk_keyval_trigger_get_modifiers (self: kt);
160
161 g_ptr_array_add (array: accels, data: gtk_accelerator_name (accelerator_key: keyval, accelerator_mods: mods));
162 return;
163 }
164 else if (GTK_IS_ALTERNATIVE_TRIGGER (ptr: trigger))
165 {
166 GtkAlternativeTrigger *at = GTK_ALTERNATIVE_TRIGGER (ptr: trigger);
167 GtkShortcutTrigger *first = gtk_alternative_trigger_get_first (self: at);
168 GtkShortcutTrigger *second = gtk_alternative_trigger_get_second (self: at);
169
170 append_accelerators (accels, trigger: first);
171 append_accelerators (accels, trigger: second);
172 return;
173 }
174}
175
176char **
177gtk_application_accels_get_accels_for_action (GtkApplicationAccels *accels,
178 const char *detailed_action_name)
179{
180 GPtrArray *result;
181 char *action_name;
182 GVariant *target;
183 GError *error = NULL;
184 guint i;
185
186 result = g_ptr_array_new ();
187
188 if (!g_action_parse_detailed_name (detailed_name: detailed_action_name, action_name: &action_name, target_value: &target, error: &error))
189 {
190 g_critical ("Error parsing action name: %s", error->message);
191 g_error_free (error);
192 g_ptr_array_add (array: result, NULL);
193 return (char **) g_ptr_array_free (array: result, FALSE);
194 }
195
196 for (i = 0; i < g_list_model_get_n_items (list: accels->shortcuts); i++)
197 {
198 GtkShortcut *shortcut = g_list_model_get_item (list: accels->shortcuts, position: i);
199 GtkShortcutAction *action = gtk_shortcut_get_action (self: shortcut);
200 GVariant *args = gtk_shortcut_get_arguments (self: shortcut);
201
202 if (!GTK_IS_NAMED_ACTION (ptr: action) ||
203 !g_str_equal (v1: gtk_named_action_get_action_name (self: GTK_NAMED_ACTION (ptr: action)), v2: action_name))
204 {
205 g_object_unref (object: shortcut);
206 continue;
207 }
208
209 if ((target == NULL && args != NULL) ||
210 (target != NULL && (args == NULL || !g_variant_equal (one: target, two: args))))
211 {
212 g_object_unref (object: shortcut);
213 continue;
214 }
215
216 append_accelerators (accels: result, trigger: gtk_shortcut_get_trigger (self: shortcut));
217 g_object_unref (object: shortcut);
218 break;
219 }
220
221 g_free (mem: action_name);
222 if (target)
223 g_variant_unref (value: target);
224 g_ptr_array_add (array: result, NULL);
225 return (char **) g_ptr_array_free (array: result, FALSE);
226}
227
228static gboolean
229trigger_matches_accel (GtkShortcutTrigger *trigger,
230 guint keyval,
231 GdkModifierType modifiers)
232{
233 if (GTK_IS_KEYVAL_TRIGGER (ptr: trigger))
234 {
235 GtkKeyvalTrigger *kt = GTK_KEYVAL_TRIGGER (ptr: trigger);
236
237 return gtk_keyval_trigger_get_keyval (self: kt) == keyval
238 && gtk_keyval_trigger_get_modifiers (self: kt) == modifiers;
239 }
240 else if (GTK_IS_ALTERNATIVE_TRIGGER (ptr: trigger))
241 {
242 GtkAlternativeTrigger *at = GTK_ALTERNATIVE_TRIGGER (ptr: trigger);
243
244 return trigger_matches_accel (trigger: gtk_alternative_trigger_get_first (self: at), keyval, modifiers)
245 || trigger_matches_accel (trigger: gtk_alternative_trigger_get_second (self: at), keyval, modifiers);
246 }
247 else
248 {
249 return FALSE;
250 }
251}
252
253static char *
254get_detailed_name_for_shortcut (GtkShortcut *shortcut)
255{
256 GtkShortcutAction *action = gtk_shortcut_get_action (self: shortcut);
257
258 if (!GTK_IS_NAMED_ACTION (ptr: action))
259 return NULL;
260
261 return g_action_print_detailed_name (action_name: gtk_named_action_get_action_name (self: GTK_NAMED_ACTION (ptr: action)),
262 target_value: gtk_shortcut_get_arguments (self: shortcut));
263}
264
265char **
266gtk_application_accels_get_actions_for_accel (GtkApplicationAccels *accels,
267 const char *accel)
268{
269 GPtrArray *result;
270 guint key, modifiers;
271 guint i;
272
273 if (!gtk_accelerator_parse (accelerator: accel, accelerator_key: &key, accelerator_mods: &modifiers))
274 {
275 g_critical ("invalid accelerator string '%s'", accel);
276 return NULL;
277 }
278
279 result = g_ptr_array_new ();
280
281 for (i = 0; i < g_list_model_get_n_items (list: accels->shortcuts); i++)
282 {
283 GtkShortcut *shortcut = g_list_model_get_item (list: accels->shortcuts, position: i);
284 char *detailed_name;
285
286 if (!trigger_matches_accel (trigger: gtk_shortcut_get_trigger (self: shortcut), keyval: key, modifiers))
287 {
288 g_object_unref (object: shortcut);
289 continue;
290 }
291
292 detailed_name = get_detailed_name_for_shortcut (shortcut);
293 if (detailed_name)
294 g_ptr_array_add (array: result, data: detailed_name);
295
296 g_object_unref (object: shortcut);
297 }
298
299 g_ptr_array_add (array: result, NULL);
300 return (char **) g_ptr_array_free (array: result, FALSE);
301}
302
303char **
304gtk_application_accels_list_action_descriptions (GtkApplicationAccels *accels)
305{
306 GPtrArray *result;
307 guint i;
308
309 result = g_ptr_array_new ();
310
311 for (i = 0; i < g_list_model_get_n_items (list: accels->shortcuts); i++)
312 {
313 GtkShortcut *shortcut = g_list_model_get_item (list: accels->shortcuts, position: i);
314 char *detailed_name;
315
316 detailed_name = get_detailed_name_for_shortcut (shortcut);
317 if (detailed_name)
318 g_ptr_array_add (array: result, data: detailed_name);
319
320 g_object_unref (object: shortcut);
321 }
322
323 g_ptr_array_add (array: result, NULL);
324 return (char **) g_ptr_array_free (array: result, FALSE);
325}
326
327GListModel *
328gtk_application_accels_get_shortcuts (GtkApplicationAccels *accels)
329{
330 return accels->shortcuts;
331}
332

source code of gtk/gtk/gtkapplicationaccels.c