1 /*
2  * TilEm II
3  *
4  * Copyright (c) 2010-2011 Thibault Duponchelle
5  * Copyright (c) 2010-2011 Benjamin Moody
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <ticalcs.h>
29 #include <tilem.h>
30 
31 #include "gui.h"
32 #include "msgbox.h"
33 #include "files.h"
34 
35 /* Get the associated calculator key name */
calc_key_from_name(const TilemCalc * calc,const char * name)36 static int calc_key_from_name(const TilemCalc *calc, const char *name)
37 {
38 	int i;
39 
40 	for (i = 0; i < 64; i++)
41 		if (calc->hw.keynames[i]
42 		    && !strcmp(calc->hw.keynames[i], name))
43 			return i + 1;
44 
45 	/* kludge: accept aliases for a few keys */
46 	for (i = 0; i < 64; i++) {
47 		if (!calc->hw.keynames[i])
48 			continue;
49 
50 		if (!strcmp(name, "Matrix")
51 		    && !strcmp(calc->hw.keynames[i], "Apps"))
52 			return i + 1;
53 		if (!strcmp(name, "Apps")
54 		    && !strcmp(calc->hw.keynames[i], "AppsMenu"))
55 			return i + 1;
56 		if (!strcmp(name, "List")
57 		    && !strcmp(calc->hw.keynames[i], "StatEd"))
58 			return i + 1;
59 		if (!strcmp(name, "Power")
60 		    && !strcmp(calc->hw.keynames[i], "Expon"))
61 			return i + 1;
62 		if (!strcmp(name, "Stat")
63 		    && !strcmp(calc->hw.keynames[i], "Table"))
64 			return i + 1;
65 	}
66 
67 	return 0;
68 }
69 
70 /* Parse a line of the group (model) in the keybindings file */
parse_binding(TilemKeyBinding * kb,const char * pckeys,const char * tikeys,const TilemCalc * calc)71 static gboolean parse_binding(TilemKeyBinding *kb,
72                               const char *pckeys, const char *tikeys,
73                               const TilemCalc *calc)
74 {
75 	const char *p;
76 	char *s;
77 	int n, k;
78 
79 	kb->modifiers = 0;
80 	kb->keysym = 0;
81 	kb->nscancodes = 0;
82 	kb->scancodes = NULL;
83 
84 	/* Parse modifiers */
85 
86 	while ((p = strchr(pckeys, '+'))) {
87 		s = g_strndup(pckeys, p - pckeys);
88 		g_strstrip(s);
89 		if (!g_ascii_strcasecmp(s, "ctrl")
90 		    || !g_ascii_strcasecmp(s, "control"))
91 			kb->modifiers |= GDK_CONTROL_MASK;
92 		else if (!g_ascii_strcasecmp(s, "shift"))
93 			kb->modifiers |= GDK_SHIFT_MASK;
94 		else if (!g_ascii_strcasecmp(s, "alt")
95 		         || !g_ascii_strcasecmp(s, "mod1"))
96 			kb->modifiers |= GDK_MOD1_MASK;
97 		else if (!g_ascii_strcasecmp(s, "mod2"))
98 			kb->modifiers |= GDK_MOD2_MASK;
99 		else if (!g_ascii_strcasecmp(s, "mod3"))
100 			kb->modifiers |= GDK_MOD3_MASK;
101 		else if (!g_ascii_strcasecmp(s, "mod4"))
102 			kb->modifiers |= GDK_MOD4_MASK;
103 		else if (!g_ascii_strcasecmp(s, "mod5"))
104 			kb->modifiers |= GDK_MOD5_MASK;
105 		else if (!g_ascii_strcasecmp(s, "lock")
106 		         || !g_ascii_strcasecmp(s, "capslock"))
107 			kb->modifiers |= GDK_LOCK_MASK;
108 		else {
109 			g_free(s);
110 			return FALSE;
111 		}
112 		g_free(s);
113 		pckeys = p + 1;
114 	}
115 
116 	/* Parse keysym */
117 
118 	s = g_strstrip(g_strdup(pckeys));
119 	kb->keysym = gdk_keyval_from_name(s);
120 	g_free(s);
121 	if (!kb->keysym)
122 		return FALSE;
123 
124 	/* Parse calculator keys */
125 
126 	/* FIXME: allow combinations of simultaneous keys (separated
127 	   by '+'); current TilemKeyBinding struct doesn't provide for
128 	   this */
129 
130 	n = 0;
131 	do {
132 		if ((p = strchr(tikeys, ',')))
133 			s = g_strndup(tikeys, p - tikeys);
134 		else
135 			s = g_strdup(tikeys);
136 		g_strstrip(s);
137 
138 		k = calc_key_from_name(calc, s);
139 		g_free(s);
140 
141 		if (!k) {
142 			g_free(kb->scancodes);
143 			kb->scancodes = NULL;
144 			return FALSE;
145 		}
146 
147 		kb->nscancodes++;
148 		if (kb->nscancodes >= n) {
149 			n = kb->nscancodes * 2;
150 			kb->scancodes = g_renew(byte, kb->scancodes, n);
151 		}
152 		kb->scancodes[kb->nscancodes - 1] = k;
153 
154 		tikeys = (p ? p + 1 : NULL);
155 	} while (tikeys);
156 
157 	return TRUE;
158 }
159 
160 /* Parse a group (model) in the keybindings file */
parse_binding_group(TilemCalcEmulator * emu,GKeyFile * gkf,const char * group,int maxdepth)161 static void parse_binding_group(TilemCalcEmulator *emu, GKeyFile *gkf,
162                                 const char *group, int maxdepth)
163 {
164 	gchar **keys, **groups;
165 	char *k, *v;
166 	int i, n;
167 
168 	keys = g_key_file_get_keys(gkf, group, NULL, NULL);
169 	if (!keys) {
170 		printf("no bindings for %s\n", group);
171 		return;
172 	}
173 
174 	for (i = 0; keys[i]; i++)
175 		;
176 
177 	n = emu->nkeybindings;
178 	emu->keybindings = g_renew(TilemKeyBinding, emu->keybindings, n + i);
179 
180 	for(i = 0; keys[i]; i++) {
181 		k = keys[i];
182 		if (!strcmp(k, "INHERIT"))
183 			continue;
184 
185 		v = g_key_file_get_value(gkf, group, k, NULL);
186 		if (!v)
187 			continue;
188 
189 		if (parse_binding(&emu->keybindings[n], k, v, emu->calc))
190 			n++;
191 		else
192 			g_printerr("syntax error in key bindings: '%s=%s'\n",
193 			           k, v);
194 		g_free(v);
195 	}
196 
197 	emu->nkeybindings = n;
198 
199 	g_strfreev(keys);
200 
201 	/* Include all bindings from groups marked as INHERIT */
202 
203 	if (maxdepth == 0)
204 		return;
205 
206 	groups = g_key_file_get_string_list(gkf, group, "INHERIT",
207 	                                    NULL, NULL);
208 	for (i = 0; groups && groups[i]; i++)
209 		parse_binding_group(emu, gkf, groups[i], maxdepth - 1);
210 	g_strfreev(groups);
211 
212 }
213 
214 /* Init the keybindings struct and open the keybindings file */
tilem_keybindings_init(TilemCalcEmulator * emu,const char * model)215 void tilem_keybindings_init(TilemCalcEmulator *emu, const char *model)
216 {
217 	char *kfname = get_shared_file_path("keybindings.ini", NULL);
218 	char *dname;
219 	GKeyFile *gkf;
220 	GError *err = NULL;
221 
222 	g_return_if_fail(emu != NULL);
223 	g_return_if_fail(emu != NULL);
224 	g_return_if_fail(emu->calc != NULL);
225 
226 	if (kfname == NULL) {
227 		messagebox00(NULL, GTK_MESSAGE_ERROR,
228 		             "Unable to load key bindings",
229 		             "The file keybindings.ini could not be found."
230 		             "  TilEm may not have been installed correctly.");
231 		return;
232 	}
233 
234 	gkf = g_key_file_new();
235 	if (!g_key_file_load_from_file(gkf, kfname, 0, &err)) {
236 		dname = g_filename_display_name(kfname);
237 		messagebox02(NULL, GTK_MESSAGE_ERROR,
238 		             "Unable to load key bindings",
239 		             "An error occurred while reading %s: %s",
240 		             dname, err->message);
241 		g_error_free(err);
242 		g_free(dname);
243 		g_free(kfname);
244 		return;
245 	}
246 
247 	g_free(emu->keybindings);
248 	emu->keybindings = NULL;
249 	emu->nkeybindings = 0;
250 
251 	parse_binding_group(emu, gkf, model, 5);
252 
253 	g_key_file_free(gkf);
254 	g_free(kfname);
255 }
256