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