1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2019 The Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <glib/gi18n.h>
24 #include "dom.h"
25 #include "gio-utils.h"
26 #include "glib-utils.h"
27 #include "gth-shortcut.h"
28 #include "gth-user-dir.h"
29
30
31 GthShortcut *
gth_shortcut_new(const char * action_name,GVariant * param)32 gth_shortcut_new (const char *action_name,
33 GVariant *param)
34 {
35 GthShortcut *shortcut;
36
37 g_return_val_if_fail (action_name != NULL, NULL);
38
39 shortcut = g_new (GthShortcut, 1);
40 shortcut->action_name = g_strdup (action_name);
41 shortcut->action_parameter = (param != NULL) ? g_variant_ref_sink (param) : NULL;
42 shortcut->detailed_action = g_action_print_detailed_name (shortcut->action_name, shortcut->action_parameter);
43 shortcut->description = NULL;
44 shortcut->context = 0;
45 shortcut->category = NULL;
46 shortcut->default_accelerator = NULL;
47 shortcut->accelerator = NULL;
48 shortcut->label = NULL;
49 shortcut->keyval = 0;
50 shortcut->modifiers = 0;
51
52 return shortcut;
53 }
54
55
56 GthShortcut *
gth_shortcut_dup(const GthShortcut * shortcut)57 gth_shortcut_dup (const GthShortcut *shortcut)
58 {
59 GthShortcut *new_shortcut;
60
61 new_shortcut = gth_shortcut_new (shortcut->action_name, shortcut->action_parameter);
62 new_shortcut->description = g_strdup (shortcut->description);
63 new_shortcut->context = shortcut->context;
64 new_shortcut->category = shortcut->category;
65 new_shortcut->default_accelerator = g_strdup (shortcut->default_accelerator);
66 gth_shortcut_set_accelerator (new_shortcut, shortcut->accelerator);
67
68 return new_shortcut;
69 }
70
71
72 void
gth_shortcut_free(GthShortcut * shortcut)73 gth_shortcut_free (GthShortcut *shortcut)
74 {
75 g_free (shortcut->action_name);
76 if (shortcut->action_parameter != NULL)
77 g_variant_unref (shortcut->action_parameter);
78 g_free (shortcut->detailed_action);
79 g_free (shortcut->description);
80 g_free (shortcut->default_accelerator);
81 g_free (shortcut->accelerator);
82 g_free (shortcut->label);
83 g_free (shortcut);
84 }
85
86
87 void
gth_shortcut_set_accelerator(GthShortcut * shortcut,const char * accelerator)88 gth_shortcut_set_accelerator (GthShortcut *shortcut,
89 const char *accelerator)
90 {
91 guint keyval;
92 GdkModifierType modifiers;
93
94 keyval = 0;
95 modifiers = 0;
96
97 if ((shortcut->context & GTH_SHORTCUT_CONTEXT_DOC) == 0) {
98 if (accelerator != NULL)
99 gtk_accelerator_parse (accelerator, &keyval, &modifiers);
100 gth_shortcut_set_key (shortcut, keyval, modifiers);
101 }
102 else {
103 shortcut->keyval = keyval;
104 shortcut->modifiers = modifiers;
105 shortcut->accelerator = g_strdup (accelerator);
106 shortcut->label = g_strdup (accelerator);
107 }
108
109 }
110
111
112 void
gth_shortcut_set_key(GthShortcut * shortcut,guint keyval,GdkModifierType modifiers)113 gth_shortcut_set_key (GthShortcut *shortcut,
114 guint keyval,
115 GdkModifierType modifiers)
116 {
117 g_free (shortcut->accelerator);
118 g_free (shortcut->label);
119
120 shortcut->keyval = keyval;
121 shortcut->modifiers = modifiers;
122 shortcut->accelerator = gtk_accelerator_name (shortcut->keyval, shortcut->modifiers);
123 shortcut->label = gtk_accelerator_get_label (shortcut->keyval, shortcut->modifiers);
124 }
125
126
127 gboolean
gth_shortcut_customizable(GthShortcut * shortcut)128 gth_shortcut_customizable (GthShortcut *shortcut)
129 {
130 return ((shortcut->context & GTH_SHORTCUT_CONTEXT_FIXED) == 0)
131 && ((shortcut->context & GTH_SHORTCUT_CONTEXT_INTERNAL) == 0)
132 && ((shortcut->context & GTH_SHORTCUT_CONTEXT_DOC) == 0);
133 }
134
135
136 GthShortcut *
gth_shortcut_array_find(GPtrArray * shortcuts_v,int context,guint keycode,GdkModifierType modifiers)137 gth_shortcut_array_find (GPtrArray *shortcuts_v,
138 int context,
139 guint keycode,
140 GdkModifierType modifiers)
141 {
142 int i;
143
144 if (keycode == 0)
145 return NULL;
146
147 /* Remove flags not related to the window mode. */
148 context = context & GTH_SHORTCUT_CONTEXT_ANY;
149 keycode = gdk_keyval_to_lower (keycode);
150
151 for (i = 0; i < shortcuts_v->len; i++) {
152 GthShortcut *shortcut = g_ptr_array_index (shortcuts_v, i);
153
154 if (((shortcut->context & context) != 0)
155 && (shortcut->keyval == keycode)
156 && (shortcut->modifiers == modifiers))
157 {
158 return shortcut;
159 }
160 }
161
162 return NULL;
163 }
164
165
166 GthShortcut *
gth_shortcut_array_find_by_accel(GPtrArray * shortcuts_v,int context,const char * accelerator)167 gth_shortcut_array_find_by_accel (GPtrArray *shortcuts_v,
168 int context,
169 const char *accelerator)
170 {
171 int i;
172
173 if (accelerator == NULL)
174 return NULL;
175
176 /* Remove flags not related to the window mode. */
177 context = context & GTH_SHORTCUT_CONTEXT_ANY;
178
179 for (i = 0; i < shortcuts_v->len; i++) {
180 GthShortcut *shortcut = g_ptr_array_index (shortcuts_v, i);
181
182 if (((shortcut->context & context) != 0)
183 && (g_strcmp0 (shortcut->accelerator, accelerator) == 0))
184 {
185 return shortcut;
186 }
187 }
188
189 return NULL;
190 }
191
192
193 GthShortcut *
gth_shortcut_array_find_by_action(GPtrArray * shortcuts_v,const char * detailed_action)194 gth_shortcut_array_find_by_action (GPtrArray *shortcuts_v,
195 const char *detailed_action)
196 {
197 int i;
198
199 if (detailed_action == NULL)
200 return NULL;
201
202 for (i = 0; i < shortcuts_v->len; i++) {
203 GthShortcut *shortcut = g_ptr_array_index (shortcuts_v, i);
204
205 if (g_strcmp0 (shortcut->detailed_action, detailed_action) == 0)
206 return shortcut;
207 }
208
209 return NULL;
210 }
211
212
213 gboolean
gth_shortcut_valid(guint keycode,GdkModifierType modifiers)214 gth_shortcut_valid (guint keycode,
215 GdkModifierType modifiers)
216 {
217 switch (keycode) {
218 case GDK_KEY_Escape:
219 case GDK_KEY_Tab:
220 return FALSE;
221
222 /* These shortcuts are valid for us but gtk_accelerator_valid
223 * considers them not valid, hence the are added here
224 * explicitly. */
225
226 case GDK_KEY_Left:
227 case GDK_KEY_Right:
228 case GDK_KEY_Up:
229 case GDK_KEY_Down:
230 case GDK_KEY_KP_Left:
231 case GDK_KEY_KP_Right:
232 case GDK_KEY_KP_Up:
233 case GDK_KEY_KP_Down:
234 return TRUE;
235
236 default:
237 return gtk_accelerator_valid (keycode, modifiers);
238 }
239
240 return FALSE;
241 }
242
243
244 gboolean
gth_shortcuts_write_to_file(GPtrArray * shortcuts_v,GError ** error)245 gth_shortcuts_write_to_file (GPtrArray *shortcuts_v,
246 GError **error)
247 {
248 DomDocument *doc;
249 DomElement *shortcuts;
250 int i;
251 char *buffer;
252 gsize size;
253 GFile *file;
254 gboolean result;
255
256 doc = dom_document_new ();
257 shortcuts = dom_document_create_element (doc, "shortcuts", NULL);
258 for (i = 0; i < shortcuts_v->len; i++) {
259 GthShortcut *shortcut = g_ptr_array_index (shortcuts_v, i);
260
261 if ((shortcut->context & GTH_SHORTCUT_CONTEXT_INTERNAL) != 0)
262 continue;
263
264 if ((shortcut->context & GTH_SHORTCUT_CONTEXT_FIXED) != 0)
265 continue;
266
267 if ((shortcut->context & GTH_SHORTCUT_CONTEXT_DOC) != 0)
268 continue;
269
270 dom_element_append_child (shortcuts,
271 dom_document_create_element (doc, "shortcut",
272 "action", shortcut->detailed_action,
273 "accelerator", shortcut->accelerator,
274 NULL));
275 }
276 dom_element_append_child (DOM_ELEMENT (doc), shortcuts);
277
278 buffer = dom_document_dump (doc, &size);
279 file = gth_user_dir_get_file_for_write (GTH_DIR_CONFIG, GTHUMB_DIR, SHORTCUTS_FILE, NULL);
280 result = _g_file_write (file, FALSE, G_FILE_CREATE_NONE, buffer, size, NULL, error);
281
282 g_object_unref (file);
283 g_free (buffer);
284 g_object_unref (doc);
285
286 return result;
287 }
288
289
290 gboolean
gth_shortcuts_load_from_file(GPtrArray * shortcuts_v,GHashTable * shortcuts,GError ** error)291 gth_shortcuts_load_from_file (GPtrArray *shortcuts_v,
292 GHashTable *shortcuts,
293 GError **error)
294 {
295 gboolean success = FALSE;
296 GFile *file;
297 void *buffer;
298 gsize size;
299
300 file = gth_user_dir_get_file_for_write (GTH_DIR_CONFIG, GTHUMB_DIR, SHORTCUTS_FILE, NULL);
301 if (_g_file_load_in_buffer (file, &buffer, &size, NULL, error)) {
302 DomDocument *doc;
303
304 doc = dom_document_new ();
305 if (dom_document_load (doc, buffer, size, error)) {
306 DomElement *node;
307
308 for (node = DOM_ELEMENT (doc)->first_child; node; node = node->next_sibling) {
309 if (g_strcmp0 (node->tag_name, "shortcuts") == 0) {
310 DomElement *shortcut_node;
311
312 for (shortcut_node = node->first_child; shortcut_node; shortcut_node = shortcut_node->next_sibling) {
313 if (g_strcmp0 (shortcut_node->tag_name, "shortcut") == 0) {
314 const char *detailed_action;
315 const char *accelerator;
316 GthShortcut *shortcut;
317
318 detailed_action = dom_element_get_attribute (shortcut_node, "action");
319 accelerator = dom_element_get_attribute (shortcut_node, "accelerator");
320
321 if (detailed_action == NULL)
322 continue;
323
324 shortcut = g_hash_table_lookup (shortcuts, detailed_action);
325 if (shortcut != NULL)
326 gth_shortcut_set_accelerator (shortcut, accelerator);
327 }
328 }
329
330 success = TRUE;
331 }
332 }
333 }
334
335 if (! success && (error != NULL))
336 *error = g_error_new_literal (DOM_ERROR, DOM_ERROR_INVALID_FORMAT, _("Invalid file format"));
337
338 g_object_unref (doc);
339 g_free (buffer);
340 }
341
342 g_object_unref (file);
343
344 return success;
345 }
346