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