1 /* kbd-custom.cpp
2 * Low-level data structure routines and file I/O for customizing keyboard
3 * configuration.
4 *
5 * For Denemo, the GNU graphical music notation package
6 * (c) 2000-2005
7 * Olivier Vermersch, Matthew Hiller, Adam Tee
8 */
9
10 #include <stdio.h>
11 #include <gdk/gdk.h>
12 #include <gtk/gtk.h>
13 #if GTK_MAJOR_VERSION==3
14 #include <gdk/gdkkeysyms-compat.h> //FIXME Look for something more gtk3 like
15 #endif
16 #include <glib.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <denemo/denemo.h>
24 #include "command/commandfuncs.h"
25 #include "core/kbd-custom.h"
26 #include "ui/kbd-interface.h"
27 #include "command/keyresponses.h"
28 #include "core/prefops.h"
29 #include "command/select.h"
30 #include "core/utils.h"
31 #include "audio/audiointerface.h"
32 //#include "audio/playback.h"
33 #include "core/keyboard.h"
34 #include "export/file.h"
35 #include "core/view.h"
36 #include "core/keymapio.h"
37 #include "core/utils.h"
38
39 #define DENEMO_TWO_KEY_SEPARATOR ","
40 #if 0 //GTK_MINOR_VERSION < 10
41 //Hmm, should we define these as 0, so that they don't mask anything in gtk 2.8
42 #define GDK_SUPER_MASK ( 1 << 26)
43 #define GDK_HYPER_MASK (1 << 27)
44 #define GDK_META_MASK (1 << 28)
45 #endif
46
47
48
49 #define USER_KEYMAP "Default"
50
51 /**
52 * load_keymap_files:
53 * @files: The files to test. String are freed.
54 *
55 * Takes a list of keymap and try to load them until one is loaded successfully.
56 *
57 * Returns: FALSE if no keymap has been loaded, TRUE either.
58 **/
59
60 gboolean
load_keymap_files(GList * files)61 load_keymap_files(GList* files)
62 {
63 gboolean ret = FALSE;
64 GList *cur = NULL;
65
66 for(cur = files; cur; cur = cur->next)
67 if(g_file_test(cur->data, G_FILE_TEST_EXISTS))
68 {
69 if(!ret && load_xml_keymap(cur->data) == 0){
70 g_message("Loaded keymap %s", (char *) cur->data);
71 ret = TRUE;
72 }
73 g_free(cur->data);
74 }
75 return ret;
76 }
77
78 gboolean
is_action_id_builtin(gint id)79 is_action_id_builtin(gint id)
80 {
81 command_row *command;
82 keymap_get_command_row(Denemo.map, &command, id);
83 return command->script_type == COMMAND_BUILTIN;
84 }
85
86 gboolean
is_action_name_builtin(gchar * command_name)87 is_action_name_builtin(gchar* command_name)
88 {
89 gint* id = NULL;
90
91 id = g_hash_table_lookup (Denemo.map->idx_from_name, command_name);
92 if(!id)
93 {
94 g_debug("Requesting a invalid action name");
95 return TRUE;
96 }
97
98 return is_action_id_builtin(*id);
99 }
100
101 void
command_row_init(command_row * command)102 command_row_init(command_row *command)
103 {
104 command->name = _("No name");
105 command->label = _("No label");
106 command->tooltip = _("No indication what this done beyond the name and label");
107 command->hidden = FALSE;
108 command->deleted = FALSE;
109 command->bindings = NULL;
110 command->callback = NULL;
111 command->type = KeymapEntry;
112 command->script_type = COMMAND_BUILTIN;
113 command->locations = NULL;
114 command->after = NULL;
115 command->fallback = NULL;
116 command->menupath = NULL;
117 command->scheme = NULL;
118 }
119
120 command_row*
get_or_create_command(gchar * name)121 get_or_create_command(gchar* name){
122 if(!Denemo.map)
123 g_error("Map is not instantiated");
124 command_row* command = NULL;
125 gint* idx = (gint*) g_hash_table_lookup(Denemo.map->idx_from_name, name);
126 if(idx)
127 command = (command_row*) g_hash_table_lookup(Denemo.map->commands, idx);
128 else{
129 command = (command_row*) g_malloc(sizeof(command_row));
130 command_row_init(command);
131 command->name = name;/*
132 idx = g_malloc(sizeof(gint));
133 *idx = g_hash_table_size(Denemo.map->commands);
134 g_hash_table_insert(Denemo.map->commands, idx, command);
135 g_hash_table_insert(Denemo.map->idx_from_name, name, idx);
136 g_free(idx);*/
137 }
138 return command;
139 }
140
141 void
dnm_clean_event(GdkEventKey * event)142 dnm_clean_event (GdkEventKey * event)
143 {
144 if (!Denemo.prefs.strictshortcuts)
145 {
146 guint ret;
147 //g_debug("Called %s\n", gdk_keyval_name(event->keyval));
148 gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), event->hardware_keycode, GDK_MOD2_MASK /*NumLock forcing numeric keypad to give numbers */ ,
149 0 /*group 0 */ , &ret, NULL, NULL, NULL);
150 if (ret >= 'A' && ret <= 'G')
151 ret += ('a' - 'A');
152 event->keyval = ret;
153
154 //g_debug("Changed to %s\n", gdk_keyval_name(event->keyval));
155 }
156 }
157
158
159
160 /* Returns the state of the event after removing the modifiers consumed by the
161 * system and unwanted modifiers. Use this before doing anything based on the
162 * (keyval, state) pair in an event handler.
163 */
164 guint
dnm_sanitize_key_state(GdkEventKey * event)165 dnm_sanitize_key_state (GdkEventKey * event)
166 {
167 guint ret = event->state;
168 if (!Denemo.prefs.strictshortcuts)
169 {
170 return ret;
171 }
172
173 #if 1
174 GdkModifierType consumed;
175 /* We want to ignore irrelevant modifiers like ScrollLock */
176
177 gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), event->hardware_keycode, event->state, event->group, NULL, NULL, NULL, &consumed);
178 /* removing consumed modifiers from ret */
179 ret &= ~consumed;
180 /* removing other unwanted modifiers from event->state */
181 ret &= (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK | GDK_MOD4_MASK | GDK_MOD5_MASK /* these make numlock required to be off for example */ );
182 #endif
183 return ret;
184 }
185
186 /* Returns the state of the event after removing the modifiers consumed by the
187 * system and even more unwanted modifiers. Use this if sanitize is insufficient.
188 */
189 guint
dnm_hyper_sanitize_key_state(GdkEventKey * event)190 dnm_hyper_sanitize_key_state (GdkEventKey * event)
191 {
192 guint ret = event->state;
193 #if 1
194 GdkModifierType consumed;
195 /* We want to ignore irrelevant modifiers like ScrollLock */
196
197 gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), event->hardware_keycode, event->state, event->group, NULL, NULL, NULL, &consumed);
198 /* removing consumed modifiers from ret */
199 ret &= ~consumed;
200 /* removing other unwanted modifiers from event->state */
201 ret &= (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
202 #endif
203 return ret;
204 }
205
206 /* Returns the state of the event as for hyper_sanitize
207 * additionally undoes the effect of CapsLock on keyval when Shift is not pressed.
208 * note that event->keycode & keystring are not altered, leaving event inconsistent.
209 * this could conceivably become a problem in other developments.
210 * Use this if hyper sanitize is insufficient.
211 */
212 guint
dnm_meta_sanitize_key_state(GdkEventKey * event)213 dnm_meta_sanitize_key_state (GdkEventKey * event)
214 {
215 guint ret = event->state;
216 #if 1
217 if (ret & GDK_LOCK_MASK)
218 {
219 if (!(ret & GDK_SHIFT_MASK))
220 event->keyval += ('a' - 'A');
221 }
222
223 /* removing everything other than control shift and alt modifiers from event->state */
224 ret &= (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
225 #endif
226 return ret;
227 }
228
229
230
231 /*
232 * Returns True if the key event is just a modifier key, False otherwise
233 * TODO look for a gdk function doing that properly
234 */
235 gboolean
isModifier(GdkEventKey * event)236 isModifier (GdkEventKey * event)
237 {
238 /* This check for modifier values on the event may not be right,
239 if the contents of gdkkeysyms.h are OS-dependent. I don't believe
240 they are. */
241 return (event->keyval >= GDK_Shift_L && event->keyval <= GDK_Hyper_R) || (event->keyval == GDK_Num_Lock);
242 }
243
244 static inline gboolean
is_alt(const gchar * string)245 is_alt (const gchar * string)
246 {
247 return ((string[0] == '<') && (string[1] == 'a' || string[1] == 'A') && (string[2] == 'l' || string[2] == 'L') && (string[3] == 't' || string[3] == 'T') && (string[4] == '>'));
248 }
249
250 static inline gboolean
is_ctl(const gchar * string)251 is_ctl (const gchar * string)
252 {
253 return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 't' || string[2] == 'T') && (string[3] == 'l' || string[3] == 'L') && (string[4] == '>'));
254 }
255
256 static inline gboolean
is_modx(const gchar * string)257 is_modx (const gchar * string)
258 {
259 return ((string[0] == '<') && (string[1] == 'm' || string[1] == 'M') && (string[2] == 'o' || string[2] == 'O') && (string[3] == 'd' || string[3] == 'D') && (string[4] >= '1' && string[4] <= '5') && (string[5] == '>'));
260 }
261
262 static inline gboolean
is_ctrl(const gchar * string)263 is_ctrl (const gchar * string)
264 {
265 return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 't' || string[2] == 'T') && (string[3] == 'r' || string[3] == 'R') && (string[4] == 'l' || string[4] == 'L') && (string[5] == '>'));
266 }
267
268 static inline gboolean
is_shft(const gchar * string)269 is_shft (const gchar * string)
270 {
271 return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'h' || string[2] == 'H') && (string[3] == 'f' || string[3] == 'F') && (string[4] == 't' || string[4] == 'T') && (string[5] == '>'));
272 }
273
274 static inline gboolean
is_shift(const gchar * string)275 is_shift (const gchar * string)
276 {
277 return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'h' || string[2] == 'H') && (string[3] == 'i' || string[3] == 'I') && (string[4] == 'f' || string[4] == 'F') && (string[5] == 't' || string[5] == 'T') && (string[6] == '>'));
278 }
279
280 static inline gboolean
is_control(const gchar * string)281 is_control (const gchar * string)
282 {
283 return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 'o' || string[2] == 'O') && (string[3] == 'n' || string[3] == 'N') && (string[4] == 't' || string[4] == 'T') && (string[5] == 'r' || string[5] == 'R') && (string[6] == 'o' || string[6] == 'O') && (string[7] == 'l' || string[7] == 'L') && (string[8] == '>'));
284 }
285
286 static inline gboolean
is_release(const gchar * string)287 is_release (const gchar * string)
288 {
289 return ((string[0] == '<') && (string[1] == 'r' || string[1] == 'R') && (string[2] == 'e' || string[2] == 'E') && (string[3] == 'l' || string[3] == 'L') && (string[4] == 'e' || string[4] == 'E') && (string[5] == 'a' || string[5] == 'A') && (string[6] == 's' || string[6] == 'S') && (string[7] == 'e' || string[7] == 'E') && (string[8] == '>'));
290 }
291
292 static inline gboolean
is_meta(const gchar * string)293 is_meta (const gchar * string)
294 {
295 return ((string[0] == '<') && (string[1] == 'm' || string[1] == 'M') && (string[2] == 'e' || string[2] == 'E') && (string[3] == 't' || string[3] == 'T') && (string[4] == 'a' || string[4] == 'A') && (string[5] == '>'));
296 }
297
298 static inline gboolean
is_super(const gchar * string)299 is_super (const gchar * string)
300 {
301 return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'u' || string[2] == 'U') && (string[3] == 'p' || string[3] == 'P') && (string[4] == 'e' || string[4] == 'E') && (string[5] == 'r' || string[5] == 'R') && (string[6] == '>'));
302 }
303
304 static inline gboolean
is_hyper(const gchar * string)305 is_hyper (const gchar * string)
306 {
307 return ((string[0] == '<') && (string[1] == 'h' || string[1] == 'H') && (string[2] == 'y' || string[2] == 'Y') && (string[3] == 'p' || string[3] == 'P') && (string[4] == 'e' || string[4] == 'E') && (string[5] == 'r' || string[5] == 'R') && (string[6] == '>'));
308 }
309
310 void
dnm_accelerator_parse(const gchar * accelerator,guint * accelerator_key,GdkModifierType * accelerator_mods)311 dnm_accelerator_parse (const gchar * accelerator, guint * accelerator_key, GdkModifierType * accelerator_mods)
312 {
313 guint keyval;
314 GdkModifierType mods;
315 gint len;
316
317 if (accelerator_key)
318 *accelerator_key = 0;
319 if (accelerator_mods)
320 *accelerator_mods = 0;
321 g_return_if_fail (accelerator != NULL);
322
323 keyval = 0;
324 mods = 0;
325 len = strlen (accelerator);
326 while (len)
327 {
328 if (*accelerator == '<')
329 {
330 if (len >= 9 && is_release (accelerator))
331 {
332 accelerator += 9;
333 len -= 9;
334 mods |= GDK_RELEASE_MASK;
335 }
336 else if (len >= 9 && is_control (accelerator))
337 {
338 accelerator += 9;
339 len -= 9;
340 mods |= GDK_CONTROL_MASK;
341 }
342 else if (len >= 7 && is_shift (accelerator))
343 {
344 accelerator += 7;
345 len -= 7;
346 mods |= GDK_SHIFT_MASK;
347 }
348 else if (len >= 6 && is_shft (accelerator))
349 {
350 accelerator += 6;
351 len -= 6;
352 mods |= GDK_SHIFT_MASK;
353 }
354 else if (len >= 6 && is_ctrl (accelerator))
355 {
356 accelerator += 6;
357 len -= 6;
358 mods |= GDK_CONTROL_MASK;
359 }
360 else if (len >= 6 && is_modx (accelerator))
361 {
362 static const guint mod_vals[] = {
363 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
364 GDK_MOD4_MASK, GDK_MOD5_MASK
365 };
366
367 len -= 6;
368 accelerator += 4;
369 mods |= mod_vals[*accelerator - '1'];
370 accelerator += 2;
371 }
372 else if (len >= 5 && is_ctl (accelerator))
373 {
374 accelerator += 5;
375 len -= 5;
376 mods |= GDK_CONTROL_MASK;
377 }
378 else if (len >= 5 && is_alt (accelerator))
379 {
380 accelerator += 5;
381 len -= 5;
382 mods |= GDK_MOD1_MASK;
383 }
384 else if (len >= 6 && is_meta (accelerator))
385 {
386 accelerator += 6;
387 len -= 6;
388 mods |= GDK_META_MASK;
389 }
390 else if (len >= 7 && is_hyper (accelerator))
391 {
392 accelerator += 7;
393 len -= 7;
394 mods |= GDK_HYPER_MASK;
395 }
396 else if (len >= 7 && is_super (accelerator))
397 {
398 accelerator += 7;
399 len -= 7;
400 mods |= GDK_SUPER_MASK;
401 }
402 else
403 {
404 gchar last_ch;
405
406 last_ch = *accelerator;
407 while (last_ch && last_ch != '>')
408 {
409 last_ch = *accelerator;
410 accelerator += 1;
411 len -= 1;
412 }
413 }
414 }
415 else
416 {
417 keyval = gdk_keyval_from_name (accelerator);
418 accelerator += len;
419 len = 0;
420 }
421 }
422
423 if (accelerator_key)
424 //The line we modify, so that uppercase letter are processed as we want
425 // *accelerator_key = gdk_keyval_to_lower (keyval);
426 *accelerator_key = keyval;
427 if (accelerator_mods)
428 *accelerator_mods = mods;
429 }
430
431 //#include "gdkdisplay-x11.h"
432 //gboolean gdk_keymap_get_caps_lock_state (GdkKeymapX11 *keymap_x11) {
433 //return keymap_x11->caps_lock_state;
434 //}
435 // if((gdk_keymap_get_caps_lock_state (gdk_keymap_get_default())!=0) != ((accelerator_mods&GDK_SHIFT_MASK)!=0))
436
437
438 gchar *
dnm_accelerator_name(guint accelerator_key,GdkModifierType accelerator_mods)439 dnm_accelerator_name (guint accelerator_key, GdkModifierType accelerator_mods)
440 {
441
442 if (!Denemo.prefs.strictshortcuts)
443 {
444 GString *name;
445
446 name = g_string_new (gdk_keyval_name (accelerator_key));
447 if (name->len > 3 && (*name->str == 'K') && (*(name->str + 1) == 'P') && (*(name->str + 2) == '_'))
448 {
449 if((*(name->str + 3) !='7') && (*(name->str + 3) !='8') && (*(name->str + 3) !='9'))
450 g_string_erase (name, 0, 3); //force numeric keypad KP_ names to normal except for 7 8 9 which are not needed for duration entry
451 }
452 //g_debug("label %s\nname %s\n", gtk_accelerator_get_label(accelerator_key, 0), gdk_keyval_name(accelerator_key));
453 //g_debug("mods were %x\n", accelerator_mods);
454 #if 0
455 //do not let caps lock affect shift of backspace etc
456 if ((accelerator_key == GDK_BackSpace) || (accelerator_key == GDK_Left) || (accelerator_key == GDK_Right) || (accelerator_key == GDK_Up) || (accelerator_key == GDK_Down) || (accelerator_key == GDK_Page_Up) || (accelerator_key == GDK_Page_Down) || (accelerator_key == GDK_Home) || (accelerator_key == GDK_End) || (accelerator_key == GDK_Insert) || (accelerator_key == GDK_Delete) || (accelerator_key == GDK_KP_Decimal) || (accelerator_key == GDK_period))
457 accelerator_mods &= ~GDK_LOCK_MASK;
458 #else
459
460 if (!((name->len == 1) && (*name->str >= 'a') && (*name->str <= 'z')))
461 if (!((name->len == 1) && (*name->str >= '0') && (*name->str <= '9')))
462 accelerator_mods &= ~GDK_LOCK_MASK;
463 #endif
464 //g_debug("mods %x\n", accelerator_mods);
465 // if (accelerator_mods&GDK_SHIFT_MASK)
466
467 // if((name->len==1) && (*name->str>='A') && (*name->str<='Z'))
468 // *name->str += ('a'-'A');
469 if (!((name->len == 1) && (*name->str >= 'a') && (*name->str <= 'z')))
470 if (((accelerator_mods & GDK_LOCK_MASK) != 0) != ((accelerator_mods & GDK_SHIFT_MASK) != 0))
471 g_string_prepend (name, "Shft+");
472
473 if (((accelerator_mods & GDK_LOCK_MASK) != 0) != ((accelerator_mods & GDK_SHIFT_MASK) != 0))
474 {
475 if ((name->len == 1) && (*name->str >= 'a') && (*name->str <= 'z'))
476 *name->str -= ('a' - 'A');
477 }
478
479 if ((accelerator_mods & GDK_CONTROL_MASK))
480 g_string_prepend (name, "Ctrl+");
481
482 if ((accelerator_mods & GDK_MOD1_MASK))
483 g_string_prepend (name, "Alt+");
484 if ((accelerator_mods & GDK_HYPER_MASK))
485 g_string_prepend (name, "Hypr+");
486 if ((accelerator_mods & GDK_MOD4_MASK))
487 g_string_prepend (name, "Mod4+");
488 if ((accelerator_mods & GDK_MOD5_MASK))
489 g_string_prepend (name, "Mod5+");
490 return g_string_free (name, FALSE);
491 }
492
493
494 static const gchar text_release[] = "<Release>";
495 static const gchar text_shift[] = "<Shift>";
496 static const gchar text_control[] = "<Control>";
497 static const gchar text_mod1[] = "<Alt>";
498 static const gchar text_mod2[] = "<Mod2>";
499 static const gchar text_mod3[] = "<Mod3>";
500 static const gchar text_mod4[] = "<Mod4>";
501 static const gchar text_mod5[] = "<Mod5>";
502 static const gchar text_meta[] = "<Meta>";
503 static const gchar text_super[] = "<Super>";
504 static const gchar text_hyper[] = "<Hyper>";
505 guint l;
506 gchar *keyval_name;
507 gchar *accelerator;
508
509 accelerator_mods &= GDK_MODIFIER_MASK;
510
511 //The line we modify, so that uppercase letter are processed as we want
512 //keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
513 keyval_name = gdk_keyval_name (accelerator_key);
514 if (!keyval_name)
515 keyval_name = "";
516
517 l = 0;
518 if (accelerator_mods & GDK_RELEASE_MASK)
519 l += sizeof (text_release) - 1;
520 if (accelerator_mods & GDK_SHIFT_MASK)
521 l += sizeof (text_shift) - 1;
522 if (accelerator_mods & GDK_CONTROL_MASK)
523 l += sizeof (text_control) - 1;
524 if (accelerator_mods & GDK_MOD1_MASK)
525 l += sizeof (text_mod1) - 1;
526 if (accelerator_mods & GDK_MOD2_MASK)
527 l += sizeof (text_mod2) - 1;
528 if (accelerator_mods & GDK_MOD3_MASK)
529 l += sizeof (text_mod3) - 1;
530 if (accelerator_mods & GDK_MOD4_MASK)
531 l += sizeof (text_mod4) - 1;
532 if (accelerator_mods & GDK_MOD5_MASK)
533 l += sizeof (text_mod5) - 1;
534 l += strlen (keyval_name);
535 if (accelerator_mods & GDK_META_MASK)
536 l += sizeof (text_meta) - 1;
537 if (accelerator_mods & GDK_HYPER_MASK)
538 l += sizeof (text_hyper) - 1;
539 if (accelerator_mods & GDK_SUPER_MASK)
540 l += sizeof (text_super) - 1;
541
542 accelerator = g_new (gchar, l + 1);
543
544 l = 0;
545 accelerator[l] = 0;
546 if (accelerator_mods & GDK_RELEASE_MASK)
547 {
548 strcpy (accelerator + l, text_release);
549 l += sizeof (text_release) - 1;
550 }
551 if (accelerator_mods & GDK_SHIFT_MASK)
552 {
553 strcpy (accelerator + l, text_shift);
554 l += sizeof (text_shift) - 1;
555 }
556 if (accelerator_mods & GDK_CONTROL_MASK)
557 {
558 strcpy (accelerator + l, text_control);
559 l += sizeof (text_control) - 1;
560 }
561 if (accelerator_mods & GDK_MOD1_MASK)
562 {
563 strcpy (accelerator + l, text_mod1);
564 l += sizeof (text_mod1) - 1;
565 }
566 if (accelerator_mods & GDK_MOD2_MASK)
567 {
568 strcpy (accelerator + l, text_mod2);
569 l += sizeof (text_mod2) - 1;
570 }
571 if (accelerator_mods & GDK_MOD3_MASK)
572 {
573 strcpy (accelerator + l, text_mod3);
574 l += sizeof (text_mod3) - 1;
575 }
576 if (accelerator_mods & GDK_MOD4_MASK)
577 {
578 strcpy (accelerator + l, text_mod4);
579 l += sizeof (text_mod4) - 1;
580 }
581 if (accelerator_mods & GDK_MOD5_MASK)
582 {
583 strcpy (accelerator + l, text_mod5);
584 l += sizeof (text_mod5) - 1;
585 }
586 if (accelerator_mods & GDK_META_MASK)
587 {
588 strcpy (accelerator + l, text_meta);
589 l += sizeof (text_meta) - 1;
590 }
591 if (accelerator_mods & GDK_HYPER_MASK)
592 {
593 strcpy (accelerator + l, text_hyper);
594 l += sizeof (text_hyper) - 1;
595 }
596 if (accelerator_mods & GDK_SUPER_MASK)
597 {
598 strcpy (accelerator + l, text_super);
599 l += sizeof (text_super) - 1;
600 }
601 strcpy (accelerator + l, keyval_name);
602
603 return accelerator;
604 }
605
606
607 /**
608 * Warns user that there was no keymap available to load
609 *
610 */
611 /* UNUSED
612 static void
613 no_map_dialog ()
614 {
615 GtkWidget *dialog;
616 dialog = gtk_message_dialog_new (NULL, (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, _("Keyboard shortcuts could not be found"));
617
618
619 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("No commands file was found in either" " the systemwide Denemo directory" " or in .denemo directory within your " "home directory. This is an installation error. You can use" " Edit/Command Management to construct a custom " "interface or to load one from" " a commandset file."));
620
621 gtk_widget_show_all (dialog);
622 gtk_dialog_run (GTK_DIALOG (dialog));
623 gtk_widget_destroy (dialog);
624 }
625 */
626 /*
627 * Allocates a keymap.
628 * action_group_name is the name of the group of actions for the commands
629 * of the keymap.
630 */
631 keymap *
allocate_keymap(void)632 allocate_keymap (void)
633 {
634 keymap *the_keymap = (keymap *) g_malloc (sizeof (keymap));
635 the_keymap->commands = g_hash_table_new (g_int_hash, g_int_equal);
636
637 //empty index reference
638 the_keymap->idx_from_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
639 the_keymap->idx_from_keystring = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
640
641 the_keymap->continuations_table = g_hash_table_new (g_str_hash, g_str_equal);
642
643 the_keymap->cursors = g_hash_table_new (g_int_hash, g_int_equal);
644 //g_debug("Created hash table %p\n", the_keymap->cursors);
645 return the_keymap;
646 }
647
648
649 void
free_keymap(keymap * the_keymap)650 free_keymap (keymap * the_keymap)
651 {
652 g_object_unref (the_keymap->commands);
653 g_hash_table_destroy (the_keymap->idx_from_name);
654 g_hash_table_destroy (the_keymap->idx_from_keystring);
655 g_hash_table_destroy (the_keymap->continuations_table);
656 }
657
658
register_command_row(keymap * the_keymap,command_row * command)659 void register_command_row(keymap* the_keymap, command_row* command){
660
661 gint *idx = g_malloc(sizeof(gint));
662 *idx = g_hash_table_size (the_keymap->commands);
663 //This code is only relevant to developers, to check that no action
664 //entry masks another. Users cannot add actions. THIS IS CHANGING NOW...
665 if (g_hash_table_lookup (the_keymap->commands, idx) != NULL)
666 g_debug ("Command %s is inserted more than once...\n", command->name);
667
668 else{
669 //insert the information in the hashmap
670 g_hash_table_insert (the_keymap->commands, idx, command);
671
672 //insert the command name in the index reference
673 g_hash_table_insert (the_keymap->idx_from_name, g_strdup (command->name), idx);
674
675 //g_debug ("Inserting command %i: %s %s %s %p", *idx, command->name, command->label, command->tooltip, command->callback);
676 }
677 }
678
679 /* Used for compatibility with register_command.h */
680 void
register_command(gchar * name,gchar * label,gchar * tooltip,gpointer callback)681 register_command (gchar * name, gchar * label, gchar * tooltip, gpointer callback)
682 {
683 command_row* command = g_malloc(sizeof(command_row));
684 command_row_init(command);
685 command->name = name;
686 command->label = label;
687 command->tooltip = tooltip;
688 command->callback = callback;
689 register_command_row(Denemo.map, command);
690 }
691
692 /* UNUSED
693 static gint
694 command_iter_sort (GtkTreeModel * model, GtkTreeIter * a, GtkTreeIter * b, G_GNUC_UNUSED gpointer user_data)
695 {
696 GtkTreeIter *iters[2];
697 // KeymapCommandType type;
698 gpointer action;
699 const gchar *names[2];
700 gint i;
701 iters[0] = a;
702 iters[1] = b;
703 for (i = 0; i < 2; i++)
704 {
705 // gtk_tree_model_get (model, iters[i],
706 //COL_TYPE, &type,
707 COL_ACTION, &action, -1);
708
709 // names[i] = gtk_action_get_name (action);
710
711 gtk_tree_model_get (model, iters[i], COL_LABEL, names+i, -1);
712 }
713 return strcmp (names[0], names[1]);
714 }*/
715
716 void
alphabeticalize_commands(keymap * the_keymap)717 alphabeticalize_commands (keymap * the_keymap)
718 {
719 /* alphabeticalizing the commands causes saving new commands by the user to fail */
720 return;
721 /*
722 g_debug ("alphabeticalizing the commands");
723 gint i, n;
724 guint *value;
725 const gchar *command_name;
726 GtkTreeModel *model = GTK_TREE_MODEL (the_keymap->commands);
727 GtkTreeSortable *sortable = GTK_TREE_SORTABLE (the_keymap->commands);
728 n = gtk_tree_model_iter_n_children (model, NULL);
729 gtk_tree_sortable_set_sort_func (sortable, 0, command_iter_sort, NULL, NULL);
730 gtk_tree_sortable_set_sort_column_id (sortable, 0, GTK_SORT_ASCENDING);
731 for (i = 0; i < n; i++)
732 {
733 command_name = lookup_name_from_idx (the_keymap, i);
734 value = (guint *) g_hash_table_lookup (the_keymap->idx_from_name, command_name);
735 if (value)
736 *value = i;
737 else
738 g_warning ("Error in keymap read");
739 }
740 */
741 }
742
743 //False if command_id is an invalid index or keymap is null, true otherwise
744 //TODO keymap should not be NULL
745 gboolean
keymap_get_command_row(keymap * the_keymap,command_row ** row,guint command_id)746 keymap_get_command_row (keymap * the_keymap, command_row ** row, guint command_id)
747 {
748 if (!the_keymap)
749 {
750 warningdialog (_("This should not happen..."));
751 return FALSE;
752 }
753
754 if(g_hash_table_lookup (the_keymap->commands, &command_id) == NULL){
755 g_debug("Command not found");
756 return FALSE;
757 }
758
759 if(!row){
760 g_debug("Empty row transmitted");
761 return FALSE;
762 }
763
764 *row = g_hash_table_lookup (the_keymap->commands, &command_id);
765
766 return TRUE;
767 }
768
769
770
771 static void
keymap_clear_bindings_in_row(gpointer key,gpointer value,gpointer data)772 keymap_clear_bindings_in_row (gpointer key, gpointer value, gpointer data)
773 {
774 command_row *command = (command_row*) value;
775 g_list_free (command->bindings);
776 }
777 static void
keymap_collect_bindings_in_row(gpointer key,gpointer value,GList ** data)778 keymap_collect_bindings_in_row (gpointer key, gpointer value, GList **data)
779 {
780 GList *g;
781 command_row *command = (command_row*) value;
782 for (g = command->bindings; g; g=g->next)
783 {
784 *data = g_list_prepend (*data, g_strdup_printf("\"%s\":\n %s\n\"%s\"\n\n", (char *) g->data, (char *) command->label, (char *) command->tooltip));
785 }
786 }
787
788
789 static void
catname(gchar * name,GString * str,gchar * separator)790 catname (gchar * name, GString * str, gchar * separator)
791 {
792 if (str)
793 g_string_append_printf (str, "%s%s", name, separator);
794 }
795
796 #if 0
797 static void
798 newlinename (gchar * name, GString * str)
799 {
800 catname (name, str, "\n");
801 }
802 #endif
803
804 static void
listname(gchar * name,GString * str)805 listname (gchar * name, GString * str)
806 {
807 catname (name, str, " ");
808 }
809
810
811 /**
812 * Clears the keymap of all entries. Leaves the content of commands and
813 * idx_from_name untouched, removes the content of bindings and
814 * idx_from_keystring
815 *
816 */
817 void
keymap_clear_bindings(keymap * the_keymap)818 keymap_clear_bindings (keymap * the_keymap)
819 {
820 g_hash_table_foreach (the_keymap->commands, keymap_clear_bindings_in_row, NULL);
821 g_hash_table_remove_all (the_keymap->idx_from_keystring);
822 }
823
824
keymap_get_bindings(keymap * the_keymap)825 GString *keymap_get_bindings (keymap * the_keymap)
826 {
827 GString *ret = g_string_new ( _("List of all current command shortcuts\nThe name of the shortcut key is given first \nE.g. \"0\" is the shortcut name of the number key for the number zero.\n(a \",\" separates the two names if is a two-key shortcut)\nThen the label as it appears in the menu\nand finally the tooltip.\nYou can search the tooltip in the Command Center to locate the command. See View->Command Center.\nThis list is in alphabetical order by name.\n----------------\n"));
828 GList *g = NULL;
829 g_hash_table_foreach (the_keymap->commands, (GHFunc)keymap_collect_bindings_in_row, (gpointer)&g);
830 g = g_list_sort (g, (GCompareFunc)strcmp);
831 GList *tofree = g;
832 for(;g;g=g->next)
833 {
834 g_string_append_printf (ret, "%s%s\n----------------\n", _("Shortcut key name: "), (char *) g->data);
835 g_free(g->data);
836 }
837 g_list_free(tofree);
838 g_string_append (ret, _("\nEnd of shortcuts\n"));
839 return ret;
840 }
841
842
843 /*
844 * Returns the number of commands in the keymap
845 */
846 guint
keymap_size(keymap * the_keymap)847 keymap_size (keymap * the_keymap)
848 {
849 return g_hash_table_size (the_keymap->commands);
850 }
851
852 /* returns TRUE if command has at least one binding */
853 gboolean
command_has_binding(guint command_id)854 command_has_binding (guint command_id)
855 {
856 command_row* row;
857 if (keymap_get_command_row (Denemo.map, &row, command_id))
858 return row->bindings != NULL;
859 return FALSE;
860 }
861
862 /**
863 * Search through keybindings for a specific binding
864 * return the command idx for the command that is bound to the keyval,state pair, or -1 if none
865 */
866 gint
lookup_command_for_keybinding(keymap * the_keymap,gint keyval,GdkModifierType state)867 lookup_command_for_keybinding (keymap * the_keymap, gint keyval, GdkModifierType state)
868 {
869 gint res;
870 gchar *name = dnm_accelerator_name (keyval, state);
871 res = lookup_command_for_keybinding_name (the_keymap, name);
872 g_free (name);
873 return res;
874 }
875
876 /* weaker lookup of keybinding */
877 gint
lookup_command_for_keyevent(GdkEventKey * event)878 lookup_command_for_keyevent (GdkEventKey * event)
879 {
880 keymap *the_keymap = Denemo.map;
881 gint command_id = lookup_command_for_keybinding (the_keymap, event->keyval,
882 dnm_sanitize_key_state (event));
883 #if 0
884 if (!Denemo.prefs.strictshortcuts)
885 {
886 // lookup_command_for_keybinding (the_keymap, event->keyval,
887 // dnm_sanitize_key_state(event));
888 if (command_id == -1)
889 command_id = lookup_command_for_keybinding (the_keymap, event->keyval, dnm_hyper_sanitize_key_state (event));
890 if (command_id == -1)
891 command_id = lookup_command_for_keybinding (the_keymap, event->keyval, dnm_meta_sanitize_key_state (event));
892 }
893 #endif
894 return command_id;
895 }
896
897
898 /* looks up the command idx for the binding of name binding_name */
899 gint
lookup_command_for_keybinding_name(keymap * the_keymap,const gchar * binding_name)900 lookup_command_for_keybinding_name (keymap * the_keymap, const gchar * binding_name)
901 {
902 gpointer *value = g_hash_table_lookup (the_keymap->idx_from_keystring,
903 binding_name);
904 if (value)
905 return *((guint *) value);
906 else
907 return -1;
908 }
909
910 /**
911 * get_scheme_from_idx:
912 * @idx: The command id.
913 *
914 * Returns null if idx is not valid.
915 * Loads the scheme code if it is not yet loaded, and caches it.
916 * Finally returns the scheme code, or NULL if the command is builtin.
917 **/
918
919 gchar*
get_scheme_from_idx(gint idx)920 get_scheme_from_idx (gint idx){
921 command_row* row = g_hash_table_lookup(Denemo.map->commands, &idx);
922 if(!row)
923 return NULL;
924 if(row->scheme)
925 return row->scheme;
926 row->scheme = load_command_data (idx);
927 return row->scheme;
928 }
929
930 /**
931 * Look up for a command index.
932 *
933 * @param keymap
934 * @param name
935 */
936 gint
lookup_command_from_name(keymap * keymap,const gchar * command_name)937 lookup_command_from_name (keymap * keymap, const gchar * command_name)
938 {
939 gpointer value = g_hash_table_lookup (keymap->idx_from_name, command_name);
940 if (value)
941 return *(guint *) value;
942 else
943 return -1;
944 }
945
946 //do not free the result
947 //returns NULL if not found
948 const GtkAction *
lookup_action_from_idx(keymap * keymap,gint command_id)949 lookup_action_from_idx (keymap * keymap, gint command_id)
950 {
951 if (command_id == -1)
952 return NULL;
953 command_row* row;
954 if (!keymap_get_command_row (keymap, &row, command_id))
955 return NULL;
956 GtkAction* action = gtk_action_group_get_action(Denemo.action_group, row->name);
957 return action;
958 }
959
960 //do not free the result
961 //returns NULL if not found
962 gpointer
lookup_callback_from_idx(keymap * keymap,gint command_id)963 lookup_callback_from_idx (keymap * keymap, gint command_id)
964 {
965 if (command_id == -1)
966 return NULL;
967 command_row *row;
968 if (!keymap_get_command_row (keymap, &row, command_id))
969 return NULL;
970 return row->callback;
971 }
972
973 //do not free the result
974 //returns NULL if not found
975 const gchar *
lookup_name_from_idx(keymap * keymap,gint command_id)976 lookup_name_from_idx (keymap * keymap, gint command_id)
977 {
978 const gchar *res = NULL;
979 if (command_id == -1)
980 return NULL;
981 command_row *row;
982 if (!keymap_get_command_row (keymap, &row, command_id))
983 return NULL;
984 return row->name;
985 }
986
987 //do not free the result
988 //returns NULL if not found
989 const gchar *
lookup_tooltip_from_idx(keymap * keymap,gint command_id)990 lookup_tooltip_from_idx (keymap * keymap, gint command_id)
991 {
992 const gchar *res = NULL;
993 if (command_id == -1)
994 return NULL;
995 command_row *row;
996 if (!keymap_get_command_row (keymap, &row, command_id))
997 return NULL;
998 res = row->tooltip; //FIXME label is a property g_object_get_prop...
999 return res;
1000 }
1001
1002 gboolean
lookup_hidden_from_idx(keymap * keymap,guint command_id)1003 lookup_hidden_from_idx (keymap * keymap, guint command_id)
1004 {
1005 gboolean res = FALSE;
1006 command_row *row;
1007 if (!keymap_get_command_row (keymap, &row, command_id))
1008 return FALSE;
1009 res = row->hidden; //FIXME label is a property g_object_get_prop...
1010 return res;
1011 }
1012
1013
1014 gboolean
lookup_deleted_from_idx(keymap * keymap,guint command_id)1015 lookup_deleted_from_idx (keymap * keymap, guint command_id)
1016 {
1017 gboolean res = FALSE;
1018 command_row *row;
1019 if (!keymap_get_command_row (keymap, &row, command_id))
1020 return 0;
1021 res = row->deleted; //FIXME label is a property g_object_get_prop...
1022 return res;
1023 }
1024
1025
1026 //do not free the result
1027 //returns NULL if not found
1028 const gchar *
lookup_label_from_idx(keymap * keymap,gint command_id)1029 lookup_label_from_idx (keymap * keymap, gint command_id)
1030 {
1031 const gchar *res = NULL;
1032 if (command_id == -1)
1033 return NULL;
1034 command_row *row;
1035 if (!keymap_get_command_row (keymap, &row, command_id))
1036 return NULL;
1037 res = row->label; //FIXME label is a property g_object_get_prop...
1038
1039 return res;
1040 }
1041 const gchar *
lookup_menu_path_from_idx(keymap * keymap,gint command_id)1042 lookup_menu_path_from_idx (keymap * keymap, gint command_id)
1043 {
1044 command_row* row = NULL;
1045 if (command_id == -1)
1046 return NULL;
1047 keymap_get_command_row(Denemo.map, &row, command_id);
1048 if(row)
1049 return row->menupath;
1050 g_critical ("No row for command number %d\n", command_id);
1051 return NULL;
1052 }
1053 //returns the accel, "" if no accel defined. free the result
1054 //the accel is the first keybinding of the list
1055 #if 0
1056 static gchar *
1057 keymap_get_accel (keymap * the_keymap, guint command_id)
1058 {
1059 command_row* row;
1060 GtkTreeModel *model_bind;
1061 GtkTreeIter iter;
1062 gchar *res;
1063
1064 if (!keymap_get_command_row (the_keymap, &row, command_id))
1065 return g_strdup ("");
1066 model_bind = GTK_TREE_MODEL (row->bindings);
1067 if (!gtk_tree_model_get_iter_first (model_bind, &iter))
1068 {
1069 return g_strdup ("");
1070 }
1071 gtk_tree_model_get (model_bind, &iter, 0, &res, -1);
1072 return res;
1073 }
1074 #endif
1075
1076 #if 0
1077 static gint
1078 findActionGroupByName (gconstpointer a, gconstpointer b)
1079 {
1080 GtkActionGroup *action_group = GTK_ACTION_GROUP (a);
1081 const gchar *searched_name = (const gchar *) b;
1082 return strcmp (gtk_action_group_get_name (action_group), b);
1083 }
1084 #endif
1085
1086
1087
1088 /* Updates the label of the widgets proxying an action with the bindings
1089 * present in the keymap.
1090 */
1091 void
update_accel_labels(keymap * the_keymap,guint command_id)1092 update_accel_labels (keymap * the_keymap, guint command_id)
1093 {
1094 if(Denemo.non_interactive)
1095 return;
1096 GtkAction *action;
1097 command_row *row;
1098
1099 //Getting the accel
1100 const gchar *command_name = lookup_name_from_idx (the_keymap, command_id);
1101 if(!command_name)
1102 g_warning("Could not find command %i", command_id);
1103
1104 GString *str = g_string_new ("");
1105
1106 //TODO don't use all the bindings as accels
1107 if (keymap_get_command_row (the_keymap, &row, command_id) && row)
1108 g_list_foreach(row->bindings, (GFunc) listname, str);
1109
1110 //Prepare the new label
1111 const gchar *base;
1112 //FIXME use translate_dnm_to_gtk
1113 base = lookup_label_from_idx (the_keymap, command_id);
1114 #if 0
1115 //FIXME here generate a locale dependent name using gtk_accelerator_get_label after back-tracking to find the keyval from the binding (stripping off the prefixes we have added etc). This will be needed for language translation (i.e. _N() should be applied to the gdk_keyval_name() but the label we are writing here should be better with a translated indication of the keybinding (Left becomes Links in German etc).
1116 // we have to store an invariant gdk name, so we should look it up here to get the keyval and from that derive a locale specific name to use on the label. In any case the following transformation is redundant
1117
1118 gchar *c;
1119 for (c = str->str; *c; c++)
1120 {
1121 if (*c == '<')
1122 *c = ' ';
1123 if (*c == '>')
1124 *c = '-';
1125 }
1126 #endif
1127 gchar *escape_base = g_markup_escape_text(base, -1);
1128 gchar *markup;
1129 if(str->len)
1130 markup = g_strdup_printf ("%s <span style=\"italic\" stretch=\"condensed\" weight=\"bold\" foreground=\"blue\">%s</span>", escape_base, str->str);
1131 else
1132 markup = g_strdup (escape_base);
1133 g_free (escape_base);
1134
1135 //For all widgets proxying the action, change the label
1136 action = gtk_action_group_get_action(Denemo.action_group, row->name);
1137 GSList *h = gtk_action_get_proxies (action);
1138 for (; h; h = h->next)
1139 {
1140 GtkWidget *widget = h->data;
1141 GtkWidget *child = (GtkWidget *) gtk_bin_get_child (GTK_BIN (widget));
1142 if (GTK_IS_BUTTON (child))
1143 {
1144 child = gtk_bin_get_child (GTK_BIN (child));
1145 }
1146 //FIXME others?? toolitem ...
1147 if (GTK_IS_LABEL (child))
1148 {
1149 gtk_label_set_markup (GTK_LABEL (child), markup);
1150 }
1151 }
1152
1153 //free allocated strings
1154 g_free (markup);
1155 g_string_free (str, TRUE);
1156 }
1157
1158 void
update_all_labels(keymap * the_keymap)1159 update_all_labels (keymap * the_keymap)
1160 {
1161 gint command_id, num = keymap_size (the_keymap);
1162 for (command_id = 0; command_id < num; command_id++)
1163 update_accel_labels (the_keymap, command_id);
1164 }
1165
1166 //if binding is a two-key binding, update a table of such bindings, adding is add is true else removing
1167 static void
update_continuations_table(keymap * the_keymap,const gchar * binding,gboolean add)1168 update_continuations_table (keymap * the_keymap, const gchar * binding, gboolean add)
1169 {
1170 gchar *second = g_strrstr (binding, DENEMO_TWO_KEY_SEPARATOR);
1171 if (!second)
1172 return;
1173
1174 gchar *shortcut = g_strdup (binding);
1175 *(shortcut + (second - binding)) = 0; // split into two strings at the separator
1176 gchar *value = shortcut + (second - binding) + 1;
1177 //g_debug("Two key shortcuts %s %s\n", shortcut, value);
1178 if (add)
1179 {
1180 GList *thelist = g_hash_table_lookup (the_keymap->continuations_table, shortcut);
1181 thelist = g_list_append (thelist, value);
1182 g_hash_table_insert (the_keymap->continuations_table, shortcut, thelist);
1183 }
1184 else
1185 {
1186 GList *thelist = g_hash_table_lookup (the_keymap->continuations_table, shortcut);
1187 if (thelist == NULL)
1188 g_warning ("Missing shortcut in table");
1189 else
1190 {
1191 GList *g;
1192 for (g = thelist; g; g = g->next)
1193 {
1194 if (!strcmp (value, (gchar *) g->data))
1195 {
1196 thelist = g_list_delete_link (thelist, g);
1197 g_hash_table_insert (the_keymap->continuations_table, shortcut, thelist);
1198 //unlikely you can get the memory back... g_free(shortcut);
1199 }
1200 }
1201 }
1202 }
1203 }
1204
1205 static void
remove_keybinding_bindings_helper(keymap * the_keymap,guint command_id,const gchar * binding)1206 remove_keybinding_bindings_helper (keymap * the_keymap, guint command_id, const gchar * binding)
1207 {
1208 gchar *cur_binding;
1209 command_row *row;
1210 GtkTreeIter iter;
1211 if (!keymap_get_command_row (the_keymap, &row, command_id) || !row->bindings)
1212 return;
1213 GList * pos = g_list_find_custom(row->bindings, binding, (GCompareFunc) g_strcmp0);
1214
1215 if (pos)
1216 {
1217 row->bindings = g_list_delete_link(row->bindings, pos);
1218 update_continuations_table (the_keymap, binding, FALSE);
1219 }
1220 }
1221
1222 void
remove_keybinding(keymap * the_keymap,gint keyval,GdkModifierType state)1223 remove_keybinding (keymap * the_keymap, gint keyval, GdkModifierType state)
1224 {
1225 gchar *name = dnm_accelerator_name (keyval, state);
1226 remove_keybinding_from_name (the_keymap, name);
1227 g_free (name);
1228 }
1229
1230 void
remove_keybinding_from_name(keymap * the_keymap,const gchar * binding)1231 remove_keybinding_from_name (keymap * the_keymap, const gchar * binding)
1232 {
1233 gint *value;
1234 value = (gint *) g_hash_table_lookup (the_keymap->idx_from_keystring, binding);
1235 if (value)
1236 {
1237 remove_keybinding_bindings_helper (the_keymap, *value, binding);
1238 update_accel_labels (the_keymap, *value);
1239 g_hash_table_remove (the_keymap->idx_from_keystring, binding);
1240 }
1241 }
1242
1243 /*
1244 * Insert a binding to the bindings of command_id.
1245 * pos indicates where in the list of bindings to insert this binding.
1246 */
1247 static void
add_keybinding_bindings_helper(keymap * the_keymap,guint command_id,const gchar * binding,ListPosition pos)1248 add_keybinding_bindings_helper (keymap * the_keymap, guint command_id, const gchar * binding, ListPosition pos)
1249 {
1250 command_row *row;
1251
1252 if (!keymap_get_command_row (the_keymap, &row, command_id))
1253 return;
1254
1255 if (pos == POS_FIRST)
1256 row->bindings = g_list_prepend(row->bindings, g_strdup(binding));
1257 else if (pos == POS_LAST)
1258 row->bindings = g_list_append(row->bindings, g_strdup(binding));
1259 else
1260 return;
1261
1262 update_continuations_table (the_keymap, binding, TRUE);
1263 }
1264
1265 gint
add_keybinding_to_named_command(keymap * the_keymap,gint keyval,GdkModifierType state,const gchar * command_name,ListPosition pos)1266 add_keybinding_to_named_command (keymap * the_keymap, gint keyval, GdkModifierType state, const gchar * command_name, ListPosition pos)
1267 {
1268 gpointer value;
1269 guint command_id;
1270 value = g_hash_table_lookup (the_keymap->idx_from_name, command_name);
1271 if (!value)
1272 {
1273 g_warning ("add_keybinding: %s, command does not exist", command_name);
1274 return -1;
1275 }
1276 command_id = *(guint *) value;
1277 return add_keybinding_to_idx (the_keymap, keyval, state, command_id, pos);
1278 }
1279
1280 gint
add_named_binding_to_idx(keymap * the_keymap,gchar * kb_name,guint command_id,ListPosition pos)1281 add_named_binding_to_idx (keymap * the_keymap, gchar * kb_name, guint command_id, ListPosition pos)
1282 {
1283 guint *new_idx;
1284 gint old_command_id;
1285 old_command_id = lookup_command_for_keybinding_name (the_keymap, kb_name); //lookup_keybinding(the_keymap, keyval, state);
1286 gchar *title = NULL;
1287 gchar *prompt = NULL;
1288
1289 if (old_command_id >= 0)
1290 {
1291 if ((!Denemo.prefs.return_key_is_special) || strcmp (kb_name, "Return"))
1292 {
1293 title = g_strdup_printf (_("The Command %s Responds to the Shortcut %s"), lookup_name_from_idx (Denemo.map, old_command_id), kb_name);
1294 prompt = g_strdup_printf (_("Lose the shortcut %s for this?"), kb_name);
1295 }
1296 }
1297
1298
1299 if (title && (pos == POS_FIRST) && (old_command_id >= 0) && (!confirm (title, prompt)))
1300 {
1301 g_free (title);
1302 g_free (prompt);
1303 return old_command_id;
1304 }
1305 if ((pos == POS_FIRST) && strcmp (kb_name, "Return"))
1306 Denemo.accelerator_status = TRUE;
1307 if(g_hash_table_lookup(Denemo.map->continuations_table, kb_name))
1308 {
1309 gchar *text = g_strdup_printf (_("The key %s is the first keypress of some two key shortcuts.\nIf you wish to re-assign it you will need to remove those first.\nOpen the View->Command Center to find and remove the shortcuts."), kb_name);
1310 infodialog (text);//Warning! cannot use warningdialog() here as it is modal, resulting in a deadlock if a command which also modal is fired off from the menu at the same time.
1311 g_free(text);
1312 } else
1313 {
1314
1315 if (old_command_id >= 0)
1316 {
1317 remove_keybinding_bindings_helper (the_keymap, old_command_id, kb_name);
1318 update_accel_labels (the_keymap, old_command_id);
1319 }
1320 //add the keybinding to the binding on idx_command
1321 add_keybinding_bindings_helper (the_keymap, command_id, kb_name, pos);
1322
1323 //update the accel labels of the command
1324 update_accel_labels (the_keymap, command_id);
1325
1326 //add or modify an entry in idx_from_keystring
1327 new_idx = (guint *) g_malloc (sizeof (guint));
1328 *new_idx = command_id;
1329 g_hash_table_insert (the_keymap->idx_from_keystring, g_strdup (kb_name), new_idx);
1330 }
1331 g_free (title);
1332 g_free (prompt);
1333 return old_command_id;
1334 }
1335
1336
1337 /**
1338 * Adds a keybinding to the_keymap. If the key was already bound,
1339 * this function removes the old binding and replaces it, returning
1340 * the number of the command this keybinding was attached to. Otherwise
1341 * returns -1.
1342 * if pos is POS_FIRST, then the user is adding the binding - get confirmation before stealing
1343 * if POS_LAST it is the command set being loaded, do not ask
1344 */
1345 gint
add_keybinding_to_idx(keymap * the_keymap,gint keyval,GdkModifierType state,guint command_id,ListPosition pos)1346 add_keybinding_to_idx (keymap * the_keymap, gint keyval, GdkModifierType state, guint command_id, ListPosition pos)
1347 {
1348 gint old_command_id;
1349 gchar *kb_name;
1350 kb_name = dnm_accelerator_name (keyval, state);
1351 old_command_id = add_named_binding_to_idx (the_keymap, kb_name, command_id, pos);
1352 g_free (kb_name);
1353 //Denemo.accelerator_status = TRUE;
1354 return old_command_id;
1355 }
1356
1357 gint
add_twokeybinding_to_idx(keymap * the_keymap,gint first_keyval,GdkModifierType first_state,gint keyval,GdkModifierType state,guint command_id,ListPosition pos)1358 add_twokeybinding_to_idx (keymap * the_keymap, gint first_keyval, GdkModifierType first_state, gint keyval, GdkModifierType state, guint command_id, ListPosition pos)
1359 {
1360 gint old_command_id;
1361 gchar *kb_name;
1362 kb_name = g_strdup_printf ("%s" DENEMO_TWO_KEY_SEPARATOR "%s", dnm_accelerator_name (first_keyval, first_state), dnm_accelerator_name (keyval, state));
1363 old_command_id = add_named_binding_to_idx (the_keymap, kb_name, command_id, pos);
1364 g_free (kb_name);
1365 Denemo.accelerator_status = TRUE;
1366 return old_command_id;
1367 }
1368
1369 /* force keybinding on action of name, returning old command id */
1370 gint
add_keybinding_for_name(gchar * name,gchar * binding)1371 add_keybinding_for_name (gchar * name, gchar * binding)
1372 {
1373 guint idx = lookup_command_from_name (Denemo.map, name);
1374 if (idx != -1)
1375 {
1376 return add_named_binding_to_idx (Denemo.map, binding, idx, POS_LAST);
1377 }
1378 return -1;
1379 }
1380
1381 /* force keybinding on action of id, returning old command id */
1382 gint
add_keybinding_for_command(gint idx,gchar * binding)1383 add_keybinding_for_command (gint idx, gchar * binding)
1384 {
1385
1386 if (idx != -1)
1387 {
1388 return add_named_binding_to_idx (Denemo.map, binding, idx, POS_LAST);
1389 }
1390 return -1;
1391 }
1392
1393 #if 0
1394 //we have to reproduce this function here since it is static in gtkmenu.c
1395 static void
1396 stolen_gtk_menu_stop_navigating_submenu (GtkMenu * menu)
1397 {
1398 if (menu->navigation_region)
1399 {
1400 cairo_region_destroy (menu->navigation_region);
1401 menu->navigation_region = NULL;
1402 }
1403 if (menu->navigation_timeout)
1404 {
1405 g_source_remove (menu->navigation_timeout);
1406 menu->navigation_timeout = 0;
1407 }
1408 }
1409 #endif
1410
1411 gint
keymap_accel_quick_edit_snooper(GtkWidget * grab_widget,GdkEventKey * event)1412 keymap_accel_quick_edit_snooper (GtkWidget * grab_widget, GdkEventKey * event)
1413 {
1414 guint keyval;
1415 GdkModifierType modifiers;
1416 GtkAction *action = NULL;
1417 keymap *the_keymap = Denemo.map;
1418 GtkMenu *menu = GTK_MENU (grab_widget);
1419
1420 if (Denemo.prefs.menunavigation && ((event->keyval == 0xFF1B) || (event->keyval == 0xFF51) || (event->keyval == 0xFF52) || (event->keyval == 0xFF53) || (event->keyval == 0xFF54)))
1421 {
1422 //Esc and arrows for navigating menus
1423 return FALSE;
1424 }
1425
1426
1427 #if GTK_MAJOR_VERSION == 3
1428 //JEREMIAH PLEASE TEST!!
1429 if (GTK_IS_ACTIVATABLE (gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (menu))))
1430 action = gtk_activatable_get_related_action ((GtkActivatable*) gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (menu)));
1431 #else
1432 if (GTK_MENU_SHELL (menu)->active_menu_item)
1433 action = gtk_widget_get_action (GTK_MENU_SHELL (menu)->active_menu_item); //note this is not gtk_menu_get_active(menu) except after a selection has been made, we want the menu item that the pointer has moved to before it is selected.
1434 #endif
1435
1436
1437 //If this menu item has no action we give up
1438 if (!action)
1439 return FALSE;
1440
1441 //If the KeyEvent is only a modifier, stop processing here
1442 if (isModifier (event))
1443 return TRUE;
1444 dnm_clean_event (event);
1445 modifiers = dnm_sanitize_key_state (event);
1446 keyval = event->keyval;
1447
1448 gint idx = lookup_command_from_name (the_keymap, gtk_action_get_name (action));
1449 //If this menu item action is not registered in the
1450 //keymap, we give up
1451 if (idx == -1)
1452 return TRUE;
1453 //#if ((GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 10))
1454 //g_print ("event key %x\n", event->keyval);
1455 if (event->keyval == 0xFFBE) //f1 key
1456 {
1457 popup_help_for_action (action);
1458 return TRUE;
1459 }
1460 //#endif
1461 //Add the keybinding
1462 add_keybinding_to_idx (the_keymap, keyval, modifiers, idx, POS_FIRST);
1463 return TRUE;
1464 }
1465
1466 gboolean
idx_has_callback(keymap * the_keymap,guint command_id)1467 idx_has_callback (keymap * the_keymap, guint command_id)
1468 {
1469 if (command_id == -1)
1470 return FALSE;
1471 gboolean res = TRUE;
1472 command_row *row;
1473 if (!keymap_get_command_row (the_keymap, &row, command_id))
1474 return FALSE;
1475 res = (gboolean) (intptr_t) row->callback;
1476 return res;
1477 }
1478
1479
1480
1481 GtkAction *
lookup_action_from_name(gchar * command_name)1482 lookup_action_from_name (gchar * command_name)
1483 {
1484 return gtk_action_group_get_action (Denemo.action_group, command_name);
1485 }
1486
1487 gboolean
execute_callback_from_idx(keymap * the_keymap,guint command_id)1488 execute_callback_from_idx (keymap * the_keymap, guint command_id)
1489 {
1490 const gchar *command_name;
1491 command_name = lookup_name_from_idx (the_keymap, command_id);
1492 return execute_callback_from_name (command_name);
1493 }
1494
1495 gboolean
execute_callback_from_name(const gchar * command_name)1496 execute_callback_from_name (const gchar * command_name)
1497 {
1498 gint idx = lookup_command_from_name(Denemo.map, command_name);
1499 GtkAction *action = lookup_action_from_name ((gchar *) command_name);
1500 gboolean builtin = is_action_name_builtin((gchar*) gtk_action_get_name(action));
1501
1502 if (!builtin){
1503 gchar* text = get_scheme_from_idx (idx);
1504 if(text)
1505 call_out_to_guile (text);
1506 else
1507 gtk_action_activate (action);
1508 }
1509 else
1510 gtk_action_activate (action);
1511 return TRUE;
1512 }
1513
1514 //prints info on the data of the keymap relative to a command
1515 G_GNUC_UNUSED void
dump_command_info(keymap * the_keymap,gint command_id)1516 dump_command_info (keymap * the_keymap, gint command_id)
1517 {
1518 gchar *cur_binding;
1519 command_row *row;
1520 GtkTreeIter iter;
1521 GList* cur = NULL;
1522
1523 if (command_id == -1)
1524 {
1525 g_message ("No command.");
1526 return;
1527 }
1528 g_message ("command %s (%d)\nKeyboard Shortcuts:", lookup_name_from_idx (the_keymap, command_id), command_id);
1529 if (!keymap_get_command_row (the_keymap, &row, command_id))
1530 return;
1531
1532 cur = row->bindings;
1533 while(cur)
1534 {
1535 g_debug ("\t%s (%d)\n", (char *) cur->data, lookup_command_for_keybinding_name (the_keymap, cur->data));
1536 cur = g_list_next(cur);
1537 }
1538 }
1539
1540
1541
1542
1543 /**
1544 * This checks to see if there's a .denemo/keymaps directory in the user's
1545 * home directory, tries to create one if there isn't, and returns the
1546 * path to it
1547 *
1548 */
1549
1550 const gchar *
get_user_keymap_dir()1551 get_user_keymap_dir ()
1552 {
1553 static gchar *keymapdir = NULL;
1554
1555 gboolean err;
1556 if (!keymapdir)
1557 {
1558 keymapdir = g_build_filename (get_user_data_dir (TRUE), COMMANDS_DIR, NULL);
1559 }
1560 err = g_mkdir_with_parents (keymapdir, 0770);
1561 if (err)
1562 {
1563 warningdialog (_("Could not create .denemo/actions for your customized commands"));
1564 g_free (keymapdir);
1565 keymapdir = NULL;
1566 }
1567
1568 return keymapdir;
1569 }
1570
1571
1572
1573
1574
1575
1576
1577 /**
1578 * loads a command set (aka keymap) from a file in a file selector.
1579 * This function is a callback that is wrapper for
1580 * load_keymap_file amongst others
1581 *FIXME note that non xml file support has been commented out
1582 */
1583 void
load_keymap_from_dialog(gchar * filename)1584 load_keymap_from_dialog (gchar * filename)
1585 {
1586 GList* files = g_list_append(NULL, g_strdup(filename));
1587 if (g_file_test (filename, G_FILE_TEST_EXISTS))
1588 load_keymap_files (files);
1589 g_free (filename);
1590 Denemo.accelerator_status = TRUE;
1591 }
1592
1593 #if 0
1594 static void
1595 show_type (GtkWidget * widget, gchar * message)
1596 {
1597 g_debug ("%s%s\n", message, widget ? g_type_name (G_TYPE_FROM_INSTANCE (widget)) : "NULL widget");
1598 }
1599 #endif
1600
1601 /**
1602 * Function for loading a command set (aka keymap) from location by way of
1603 * a user dialog.
1604 */
1605 void
load_keymap_dialog_location(gchar * location)1606 load_keymap_dialog_location (gchar * location)
1607 {
1608 gchar *filename = file_dialog ("Load Command Set", TRUE, location);
1609 if (filename)
1610 if(confirm (_("Key Map Loading"), _("Load Shortcuts only?")))
1611 {//g_print("Starting filename %s\n", filename);
1612 if(g_str_has_suffix (filename, ".commands"))
1613 {
1614 *(filename+strlen(filename)-strlen(".commands")) = 0;
1615
1616 filename = g_strdup_printf ("%s.shortcuts", filename);
1617 }
1618 //g_print("Doing filename %s\n", filename);
1619 load_xml_keybindings (filename);
1620 }
1621 else
1622 load_keymap_from_dialog (filename);
1623 }
1624
1625 void
load_keymap_dialog()1626 load_keymap_dialog ()
1627 {
1628 gchar *keymapdir = g_strdup_printf ("%s%c", get_user_keymap_dir (), G_DIR_SEPARATOR);
1629 if (keymapdir)
1630 load_keymap_dialog_location (keymapdir);
1631 else
1632 warningdialog (_("Cannot access your local .denemo"));
1633 g_free (keymapdir);
1634 }
1635
1636 void
load_system_keymap_dialog(void)1637 load_system_keymap_dialog (void)
1638 {
1639 gchar *systemwide = g_build_filename (get_system_data_dir (), COMMANDS_DIR, USER_KEYMAP, KEYMAP_EXT,
1640 NULL);
1641 if (systemwide)
1642 load_keymap_dialog_location (systemwide);
1643 else
1644 warningdialog (_("Installation error"));
1645 g_free (systemwide);
1646 }
1647
1648 void
load_default_keymap_file()1649 load_default_keymap_file ()
1650 {
1651 gchar* user_keymap_file = g_strconcat (USER_KEYMAP, KEYMAP_EXT, NULL);
1652 gchar* default_keymap_file = g_strconcat (DEFAULT_KEYMAP, KEYMAP_EXT, NULL);
1653 GList* files = NULL;
1654
1655 files = g_list_append(files, g_build_filename (get_user_keymap_dir (), user_keymap_file, NULL));
1656 files = g_list_append(files, g_build_filename (PACKAGE_SOURCE_DIR, COMMANDS_DIR, user_keymap_file, NULL));
1657 files = g_list_append(files, g_build_filename (get_system_data_dir (), COMMANDS_DIR, user_keymap_file, NULL));
1658 files = g_list_append(files, g_build_filename (get_user_keymap_dir (), default_keymap_file, NULL));
1659 files = g_list_append(files, g_build_filename (get_system_data_dir (), COMMANDS_DIR, default_keymap_file, NULL));
1660
1661 if(!load_keymap_files (files))
1662 g_warning ("Unable to load default keymap");
1663
1664 if(Denemo.old_user_data_dir) {
1665 files = g_list_append(NULL, g_build_filename (Denemo.old_user_data_dir, COMMANDS_DIR, user_keymap_file, NULL));
1666
1667 if(!load_keymap_files (files))
1668 g_warning ("Unable to load former default keymap");
1669 else
1670 save_default_keymap_file ();
1671 }
1672 g_free(default_keymap_file);
1673 g_free (user_keymap_file);
1674 }
1675
1676 /* UNUSED
1677 static GScannerConfig scanner_config_template = {
1678 (" \t\r\n") // cset_skip_characters
1679 ,(G_CSET_a_2_z "_0123456789/." G_CSET_A_2_Z) // cset_identifier_first
1680 ,(G_CSET_a_2_z "_0123456789/." G_CSET_A_2_Z G_CSET_LATINS G_CSET_LATINC) // cset_identifier_nth
1681 ,("#\n") // cpair_comment_single
1682
1683 ,FALSE // case_sensitive
1684
1685 ,TRUE // skip_comment_multi
1686 ,TRUE // skip_comment_single
1687 ,TRUE // scan_comment_multi
1688 ,TRUE // scan_identifier
1689 ,TRUE // scan_identifier_1char
1690 ,FALSE //scan_identifier_NULL
1691 ,TRUE // scan_symbols
1692 ,FALSE // scan_binary
1693 ,FALSE // scan_octal
1694 ,FALSE // scan_float
1695 ,FALSE // scan_hex
1696 ,FALSE // scan_hex_dollar
1697 ,TRUE // scan_string_sq
1698 ,TRUE // scan_string_dq
1699 ,FALSE // numbers_2_int
1700 ,FALSE // int_2_float
1701 ,TRUE // identifier_2_string
1702 ,TRUE // char_2_token
1703 ,TRUE // symbol_2_token
1704 ,FALSE // scope_0_fallback
1705 };
1706 */
1707 /**
1708 * Callback for saving the keymap to a given file
1709 *
1710 */
1711 void
save_keymap_from_dialog(gchar * filename)1712 save_keymap_from_dialog (gchar * filename)
1713 {
1714
1715 gchar *extendedname = substitute_extension (filename, "commands");
1716 save_xml_keymap (extendedname); //no longer save keybindings here
1717
1718 gchar *fname = substitute_extension (extendedname, "shortcuts");
1719 save_xml_keybindings (fname);
1720 g_free (fname);
1721 g_free (extendedname);
1722 g_free (filename);
1723 }
1724
1725 /**
1726 * Function for saving a keymap to an arbitrary place by way of
1727 * a user dialog. Similar to file_saveas.
1728 */
1729 void
save_keymap_dialog(void)1730 save_keymap_dialog (void)
1731 {
1732 gchar *keymapdir = NULL;
1733 keymapdir = g_build_filename (get_user_keymap_dir (), NULL);
1734 gchar *filename = file_dialog (_("Save Command Set"), FALSE, keymapdir);
1735 if (filename)
1736 save_keymap_from_dialog (filename);
1737 g_free (keymapdir);
1738 }
1739
1740 /**
1741 * Wrapper function for saving the keymap to the standard place
1742 *
1743 */
1744 void
save_default_keymap_file_wrapper(GtkAction * action,DenemoScriptParam * param)1745 save_default_keymap_file_wrapper (GtkAction * action, DenemoScriptParam * param)
1746 {
1747 save_default_keymap_file ();
1748 }
1749
1750 /**
1751 * Saves the keymap as the user's default keymap
1752 *
1753 */
1754 void
save_default_keymap_file(void)1755 save_default_keymap_file (void)
1756 {
1757 gchar *localrc = NULL;
1758 const gchar *keymapdir = get_user_keymap_dir ();
1759 if (keymapdir)
1760 {
1761 gchar* default_keymap_file = g_strconcat (DEFAULT_KEYMAP, KEYMAP_EXT, NULL);
1762 localrc = g_build_filename (keymapdir, default_keymap_file, NULL);
1763 g_free(default_keymap_file);
1764 save_xml_keymap (localrc); //no longer saves keybindings
1765 g_free (localrc);
1766 localrc = g_build_filename (keymapdir, DEFAULT_KEYBINDINGS, NULL);
1767 save_xml_keybindings (localrc);
1768 g_free (localrc);
1769 }
1770
1771 }
1772
1773 /**
1774 * This function gets the caller a string useful for display
1775 */
1776 void
set_state(gint state,gchar ** value)1777 set_state (gint state, gchar ** value)
1778 {
1779 switch (state)
1780 {
1781 case 1:
1782 *value = "Shift+";
1783 break;
1784 case 4:
1785 *value = "Ctrl+";
1786 break;
1787 case 8:
1788 *value = "Alt+";
1789 break;
1790 case 5:
1791 *value = "Ctrl+Shift+";
1792 break;
1793 case 9:
1794 *value = "Alt+Shift+";
1795 break;
1796 case 12:
1797 *value = "Alt+Ctrl+";
1798 break;
1799 case 13:
1800 *value = "Alt+Ctrl+Shift+";
1801 break;
1802 }
1803 }
1804 /* UNUSED
1805 static void
1806 command_name_data_function (G_GNUC_UNUSED GtkTreeViewColumn * col, GtkCellRenderer * renderer, GtkTreeModel * model, GtkTreeIter * iter, G_GNUC_UNUSED gpointer user_data)
1807 {
1808 KeymapCommandType type;
1809 gpointer action;
1810 const gchar *name;
1811 gtk_tree_model_get (model, iter, COL_TYPE, &type, COL_ACTION, &action, -1);
1812 name = gtk_action_get_name (action);
1813
1814 g_object_set (renderer, "text", name, NULL);
1815 }
1816 */
1817 static void
label_data_function(G_GNUC_UNUSED GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,G_GNUC_UNUSED gpointer user_data)1818 label_data_function (G_GNUC_UNUSED GtkTreeViewColumn * col, GtkCellRenderer * renderer, GtkTreeModel * model, GtkTreeIter * iter, G_GNUC_UNUSED gpointer user_data)
1819 {
1820 const gchar *name;
1821 gtk_tree_model_get (model, iter, COL_LABEL, &name, -1);
1822 g_object_set (renderer, "text", name, NULL);
1823 }
1824
1825 static void
command_hidden_data_function(G_GNUC_UNUSED GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,G_GNUC_UNUSED gpointer user_data)1826 command_hidden_data_function (G_GNUC_UNUSED GtkTreeViewColumn * col, GtkCellRenderer * renderer, GtkTreeModel * model, GtkTreeIter * iter, G_GNUC_UNUSED gpointer user_data)
1827 {
1828 command_row* row = NULL;
1829 gchar* name = NULL;
1830 gtk_tree_model_get (model, iter, COL_NAME, &name, -1);
1831 gint id = lookup_command_from_name (Denemo.map, name);
1832 keymap_get_command_row (Denemo.map, &row, id);
1833 // ("command_hidden_data_function called");
1834 g_object_set (renderer, "active", row->hidden, NULL);
1835 }
1836
1837 static gint fuzzy = 0; // number of mis-matched words to allow
1838 static gint last_row = -1; // implemented as last found idx
1839 static gboolean
search_equal_func(GtkTreeModel * model,gint G_GNUC_UNUSED column,const gchar * key,GtkTreeIter * iter,G_GNUC_UNUSED gpointer search_data)1840 search_equal_func (GtkTreeModel * model, gint G_GNUC_UNUSED column, const gchar * key, GtkTreeIter * iter, G_GNUC_UNUSED gpointer search_data)
1841 {
1842 gchar *lookin;
1843 gboolean notfound;
1844 static gchar *last_key;
1845 static gchar **search_strings;
1846 static gint number_of_search_terms;
1847 gboolean backspace = FALSE;
1848 if (*key == 0)
1849 return TRUE;
1850 if ((!last_key) || strcmp (key, last_key))
1851 {
1852
1853 if(search_strings)
1854 g_strfreev (search_strings);
1855 search_strings = g_strsplit (key, " ", -1);
1856 gchar **p;
1857 number_of_search_terms = 0;
1858 for (p = search_strings;*p && **p;p++)
1859 {gchar *c = *p;
1860 *p = g_utf8_casefold (*p, -1);
1861 g_free (c);
1862 number_of_search_terms++;
1863 }
1864 backspace = last_key && (strlen(key)<strlen(last_key));
1865 g_free (last_key);
1866 last_key = g_strdup(key);
1867 }
1868 if (backspace)
1869 return FALSE;
1870 gchar *tooltip;
1871 gtk_tree_model_get (model, iter, COL_TOOLTIP, &tooltip, -1);
1872 gchar *label;
1873 gtk_tree_model_get (model, iter, COL_LABEL, &label, -1);
1874 gchar *name;
1875 gtk_tree_model_get (model, iter, COL_NAME, &name, -1);
1876
1877
1878 lookin = g_strconcat (tooltip, " ", label," ", name, NULL);
1879 gchar *this;
1880 this = g_utf8_casefold (lookin, -1);
1881
1882 GtkTreePath *path = gtk_tree_model_get_path (model, iter);
1883 gchar *thepath = path?gtk_tree_path_to_string (path):NULL;
1884 gint rownum = thepath?atoi (thepath):0;
1885 gtk_tree_path_free(path);
1886 g_free(thepath);
1887
1888 //gtk_tree_model_get (model, iter, COL_NAME, &name, -1);
1889 //const gint idx = rownum;//lookup_command_from_name(Denemo.map, name);//= lookup_action_from_name (name);
1890 gchar **p;
1891 gint matches = 0;
1892 for (p = search_strings;*p && **p;p++)
1893 {
1894 if (g_strstr_len (this, -1, *p))
1895 matches++;//g_print ("%s in %s matches %d\n", this, *p, matches);
1896 }
1897 //notfound = (matches <= number_of_search_terms/2);
1898 if (number_of_search_terms > 1)
1899 notfound = (matches + fuzzy < number_of_search_terms);
1900 else
1901 notfound = !matches;
1902
1903 if ((!notfound) && (rownum <= last_row))
1904 notfound = TRUE;
1905 if (!notfound)
1906 {
1907 last_row = rownum;
1908 if (Denemo.prefs.immediateplayback)
1909 play_note (DEFAULT_BACKEND, 0, 9, 67, 300, 127);
1910 }
1911 g_free(this);
1912
1913 g_free (lookin);
1914 return notfound;
1915 }
1916
1917 /*toggle hidden on action at row in command list */
1918 static void
toggle_hidden_on_action(G_GNUC_UNUSED GtkCellRendererToggle * cell_renderer,gchar * path,GtkTreeModel * model)1919 toggle_hidden_on_action (G_GNUC_UNUSED GtkCellRendererToggle * cell_renderer, gchar * path, GtkTreeModel* model)
1920 {
1921 gint command_id = -1;
1922 command_row* row = NULL;
1923 GtkTreeIter iter = { 0, NULL, NULL, NULL };
1924 const gchar *command_name;
1925 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(get_command_view()));
1926 if(selection) {
1927 if(gtk_tree_selection_get_selected (selection, NULL, &iter))
1928 {
1929 gtk_tree_model_get (model, &iter, COL_NAME, &command_name, -1);
1930 command_id = lookup_command_from_name(Denemo.map, command_name);
1931 }
1932
1933 keymap_get_command_row (Denemo.map, &row, command_id);
1934 const GtkAction *action = lookup_action_from_idx (Denemo.map, command_id);
1935 if (GTK_IS_ACTION (action))
1936 {
1937 set_visibility_for_action ((GtkAction *)action, row->hidden);
1938 }
1939 }
1940 }
1941
1942
1943 static void
search_next(GtkWidget * SearchEntry)1944 search_next (GtkWidget *SearchEntry)
1945 {
1946 if ( gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW(get_command_view())), NULL, NULL))
1947 last_row++;
1948 else
1949 last_row = -1;
1950 //FIXME issue some signal to cause a search to be made instead of this:
1951 g_signal_emit_by_name (SearchEntry, "insert-at-cursor", "");
1952 gtk_widget_grab_focus (SearchEntry);
1953 }
1954
toggle_fuzzy(void)1955 static void toggle_fuzzy (void)
1956 {
1957 fuzzy = !fuzzy;
1958 }
selection_changed(GtkTreeSelection * selection,GtkTreeModel * model)1959 static void selection_changed (GtkTreeSelection *selection, GtkTreeModel* model) {
1960 GtkTreeIter iter = { 0, NULL, NULL, NULL };
1961 const gchar *command_name;
1962 if(gtk_tree_selection_get_selected (selection, NULL, &iter))
1963 {
1964 gtk_tree_model_get (model, &iter, COL_NAME, &command_name, -1);
1965 last_row = lookup_command_from_name(Denemo.map, command_name);
1966 }
1967 }
1968
1969 static void
command_from_hashtable_to_treemodel(gpointer key,gpointer value,gpointer data)1970 command_from_hashtable_to_treemodel(gpointer key, gpointer value, gpointer data){
1971 GtkListStore* list = GTK_LIST_STORE(data);
1972 command_row* command = (command_row*) value;
1973
1974 GtkTreeIter iter = { 0, NULL, NULL, NULL };
1975 gtk_list_store_append (list, &iter);
1976
1977 gtk_list_store_set (list, &iter,
1978 COL_TYPE, command->type,
1979 COL_NAME, command->name,
1980 COL_LABEL, command->label,
1981 COL_TOOLTIP, command->tooltip,
1982 COL_CALLBACK, command->callback,
1983 COL_BINDINGS, command->bindings,
1984 COL_SCRIPTTYPE, command->script_type,
1985 COL_LOCATIONS, command->locations,
1986 COL_ROW, command,
1987 -1);
1988 }
1989
1990 static GtkTreeModel*
commands_treemodel(keymap * the_keymap)1991 commands_treemodel(keymap * the_keymap){
1992 GtkListStore* list = gtk_list_store_new (N_COLUMNS,
1993 G_TYPE_INT, //type
1994 G_TYPE_POINTER, //action
1995 G_TYPE_POINTER, //name
1996 G_TYPE_POINTER, //label
1997 G_TYPE_POINTER, //tooltip
1998 G_TYPE_POINTER, //callback
1999 G_TYPE_POINTER, //bindings
2000 G_TYPE_BOOLEAN, //hidden
2001 G_TYPE_BOOLEAN, //deleted
2002 G_TYPE_INT, //type
2003 G_TYPE_POINTER,
2004 G_TYPE_POINTER //row
2005 );
2006 g_hash_table_foreach(the_keymap->commands, command_from_hashtable_to_treemodel, list);
2007 return GTK_TREE_MODEL(list);
2008 }
2009
2010 GtkWidget *
keymap_get_command_view(keymap * the_keymap,GtkWidget * SearchEntry,GtkWidget * SearchNext)2011 keymap_get_command_view (keymap * the_keymap, GtkWidget *SearchEntry, GtkWidget *SearchNext)
2012 {
2013 GtkScrolledWindow *res2;
2014 GtkTreeView *res;
2015 GtkTreeViewColumn *col;
2016 GtkCellRenderer *renderer;
2017 GtkTreeSelection *selection;
2018 GtkTreeModel *model;
2019
2020 model = commands_treemodel(the_keymap);
2021 //setting up the tree view
2022 res = GTK_TREE_VIEW (gtk_tree_view_new ());
2023
2024
2025 gtk_tree_view_set_model (res, model);
2026
2027
2028 col = gtk_tree_view_column_new ();
2029 gtk_tree_view_column_set_title (col, _("Command"));
2030 gtk_tree_view_append_column (res, col);
2031
2032 renderer = gtk_cell_renderer_text_new ();
2033 gtk_tree_view_column_pack_start (col, renderer, TRUE);
2034 gtk_tree_view_column_set_cell_data_func (col, renderer, label_data_function, NULL, NULL);
2035
2036 #if 0
2037 /* including this column makes the command manager too wide */
2038 col = gtk_tree_view_column_new ();
2039 gtk_tree_view_column_set_title (col, _("Commands"));
2040 gtk_tree_view_append_column (res, col);
2041
2042 renderer = gtk_cell_renderer_text_new ();
2043 gtk_tree_view_column_pack_start (col, renderer, TRUE);
2044 gtk_tree_view_column_set_cell_data_func (col, renderer, command_name_data_function, NULL, NULL);
2045 #endif
2046 col = gtk_tree_view_column_new ();
2047 gtk_tree_view_column_set_title (col, _("Hidden"));
2048 gtk_tree_view_append_column (res, col);
2049
2050 renderer = gtk_cell_renderer_toggle_new ();
2051 gtk_tree_view_column_pack_start (col, renderer, TRUE);
2052 //gtk_tree_view_column_add_attribute (col, renderer, "active", COL_HIDDEN); this causes warnings from GLib-GObject
2053 gtk_tree_view_column_set_cell_data_func (col, renderer, command_hidden_data_function, NULL, NULL);
2054 g_signal_connect (renderer, "toggled", (GCallback) toggle_hidden_on_action, (gpointer) model);
2055
2056 #if 0
2057 col = gtk_tree_view_column_new ();
2058 gtk_tree_view_column_set_title (col, _("Deleted"));
2059 gtk_tree_view_append_column (res, col);
2060
2061 renderer = gtk_cell_renderer_toggle_new ();
2062 gtk_tree_view_column_pack_start (col, renderer, TRUE);
2063 gtk_tree_view_column_add_attribute (col, renderer, "active", COL_DELETED);
2064 gtk_tree_view_column_set_cell_data_func (col, renderer, command_deleted_data_function, NULL, NULL);
2065 g_signal_connect (renderer, "toggled", (GCallback) toggle_deleted_on_action, NULL);
2066 #endif
2067
2068
2069
2070 selection = gtk_tree_view_get_selection (res);
2071 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2072
2073 g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK(selection_changed), (gpointer) model);
2074
2075 gtk_tree_view_set_search_equal_func (res, search_equal_func, NULL, NULL);
2076 //gtk_tree_view_set_search_column (res, COL_LABEL);
2077 gtk_tree_view_set_enable_search (res, TRUE);
2078
2079
2080
2081 //setting up the scrolledwindow
2082 res2 = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0), gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0)));
2083 gtk_container_add (GTK_CONTAINER (res2), GTK_WIDGET (res));
2084 gtk_scrolled_window_set_policy (res2, GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
2085
2086 //FIXME adapt so that 10~15 rows are visible
2087 gtk_widget_set_size_request (GTK_WIDGET (res2), -1, 300);
2088
2089 GtkWidget *vbox = gtk_vbox_new (FALSE, 8);
2090 GtkWidget *hbox = gtk_hbox_new (FALSE, 8);
2091
2092 gtk_tree_view_set_search_entry (res, (GtkEntry*)SearchEntry);
2093 GtkWidget *label = gtk_label_new (_("Search"));
2094 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
2095 gtk_box_pack_start (GTK_BOX (hbox), SearchEntry, FALSE, TRUE, 0);
2096
2097 gtk_box_pack_start (GTK_BOX (hbox), SearchNext, FALSE, TRUE, 0);
2098
2099 GtkWidget *toggle = gtk_check_button_new_with_label (_("Fuzzy Search"));
2100 gtk_widget_set_tooltip_text (toggle, _("Allow one non-matching word in the search."));
2101
2102 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(toggle), FALSE);
2103 gtk_widget_set_can_focus (toggle, FALSE);
2104 g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(toggle_fuzzy), NULL);
2105 gtk_box_pack_end (GTK_BOX (hbox), toggle, FALSE, TRUE, 0);
2106 g_signal_connect_swapped (G_OBJECT (SearchNext), "clicked", G_CALLBACK (search_next), SearchEntry);
2107
2108 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
2109 gtk_box_pack_start (GTK_BOX (vbox), (GtkWidget*)res2, TRUE, TRUE, 0);
2110
2111
2112 return GTK_WIDGET (res2);
2113 }
2114
2115 void
row_inserted_handler(GtkTreeModel * model,GtkTreePath * arg1,GtkTreeIter * arg2,gpointer user_data)2116 row_inserted_handler (GtkTreeModel * model, GtkTreePath * arg1, GtkTreeIter * arg2, gpointer user_data)
2117 {
2118 keyboard_dialog_data *cbdata = (keyboard_dialog_data *) user_data;
2119 if (cbdata->command_id != -1)
2120 update_accel_labels (Denemo.map, cbdata->command_id);
2121 }
2122
2123 void
row_deleted_handler(GtkTreeModel * model,GtkTreePath * arg1,gpointer user_data)2124 row_deleted_handler (GtkTreeModel * model, GtkTreePath * arg1, gpointer user_data)
2125 {
2126 keyboard_dialog_data *cbdata = (keyboard_dialog_data *) user_data;
2127 if (cbdata->command_id != -1)
2128 update_accel_labels (Denemo.map, cbdata->command_id);
2129 }
2130
2131 //Performs cleanup on the keymap when a command view is closed
2132 gboolean
keymap_cleanup_command_view(keyboard_dialog_data * data)2133 keymap_cleanup_command_view (keyboard_dialog_data * data)
2134 {
2135 #if 0
2136 GtkTreeModel *model;
2137 model = gtk_tree_view_get_model (data->binding_view);
2138 if (model)
2139 {
2140 g_signal_handlers_disconnect_by_func (model, row_deleted_handler, data);
2141 }
2142
2143 Denemo.command_manager = NULL;
2144 return FALSE;//allow window to be destroyed
2145 #endif
2146 activate_action ("/MainMenu/ViewMenu/" "ToggleCommandManager");
2147 return TRUE;
2148
2149 }
2150 const gchar *
get_menu_label(gchar * name)2151 get_menu_label (gchar *name)
2152 {
2153 gint* idx = (gint*) g_hash_table_lookup(Denemo.map->idx_from_name, name);
2154 if(!idx)
2155 return name;
2156 command_row* row = g_hash_table_lookup(Denemo.map->commands, idx);
2157 return row->label ?: name;
2158 }
2159
2160 /* caller must free */
get_menu_position(gchar * menupath)2161 gchar *get_menu_position (gchar *menupath)
2162 {
2163 if(menupath==NULL)
2164 menupath = g_strdup_printf("%s", _("Not in menu system. You can create a palette button for it using the Add to Palette button."));
2165 GString *position = g_string_new("");
2166 gchar *path = g_strdup(menupath/* + 1 skip over the initial delimeter*/);
2167 gchar *element = strtok (path, "/");
2168 if(element) {
2169 g_string_append (position, get_menu_label(element));
2170 while ((element = strtok (NULL, "/"))) {
2171 if(*element)
2172 g_string_append_printf (position, "->%s", get_menu_label(element));
2173 else
2174 g_string_append (position, "**");
2175 }
2176 }
2177 g_free(path);
2178 return g_string_free (position, FALSE);
2179 }
2180
2181
update_bindings_model(GtkListStore * model,GList * bindings)2182 void update_bindings_model(GtkListStore * model, GList *bindings){
2183 GtkTreeIter iter = {0,0,0,0};
2184 GList* cur = bindings;
2185
2186 gtk_list_store_clear (model);
2187 while(cur){
2188 gtk_list_store_append (model, &iter);
2189 gtk_list_store_set (model, &iter, 0, cur->data, -1);
2190 cur = g_list_next(cur);
2191 }
2192 }
2193
2194 static GtkTreeModel*
get_bindings_model(GList * bindings)2195 get_bindings_model(GList *bindings)
2196 {
2197 GtkListStore* list = gtk_list_store_new (1, G_TYPE_STRING);
2198 update_bindings_model(list, bindings);
2199
2200 return GTK_TREE_MODEL(list);
2201 }
2202
2203 gboolean
keymap_change_binding_view_on_command_selection(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)2204 keymap_change_binding_view_on_command_selection (GtkTreeSelection * selection, GtkTreeModel * model, GtkTreePath * path, gboolean path_currently_selected, gpointer data)
2205 {
2206 GtkTreeView *binding_view;
2207 GtkTreeIter iter;
2208 GtkTreeModel *old_binding_model;
2209 GtkTextBuffer *text_buffer;
2210 KeymapCommandType type;
2211 gpointer action;
2212 gint *array;
2213 command_row* row;
2214 const gchar *tooltip;
2215 keyboard_dialog_data *cbdata = (keyboard_dialog_data *) data;
2216
2217 //if the same command is selected again, we do nothing
2218 if (path_currently_selected)
2219 return TRUE;
2220
2221 //getting the binding_view
2222 binding_view = cbdata->binding_view;
2223
2224 //disconnecting signals of the old binding view
2225 old_binding_model = gtk_tree_view_get_model (binding_view);
2226 if (old_binding_model)
2227 g_signal_handlers_disconnect_by_func (old_binding_model, row_deleted_handler, data);
2228
2229 //getting the new model
2230 gtk_tree_model_get_iter (model, &iter, path);
2231 gchar* name;
2232 gboolean hidden = FALSE;
2233 gtk_tree_model_get (model, &iter,
2234 COL_TYPE, &type,
2235 COL_TOOLTIP, &tooltip,
2236 COL_ROW, &row,
2237 COL_NAME, &name,
2238 -1);
2239 hidden = row->hidden;
2240 action = gtk_action_group_get_action(Denemo.action_group, name);
2241 //getting the new command_id
2242 array = gtk_tree_path_get_indices (path);
2243 cbdata->command_id = array[0];
2244
2245 //setting the model and releasing our reference
2246 GtkTreeModel* bindings_model = get_bindings_model (row->bindings);
2247 gtk_tree_view_set_model (binding_view, bindings_model);
2248
2249 g_signal_connect (bindings_model, "row-deleted", G_CALLBACK (row_deleted_handler), data);
2250 //changing the tooltip
2251 text_buffer = gtk_text_view_get_buffer (cbdata->text_view);
2252
2253 if (tooltip)
2254 {
2255 gchar *plain = NULL;
2256 gchar *menupath = get_menu_position (row->menupath);
2257 const gchar *label = get_menu_label (name);
2258 pango_parse_markup (tooltip, -1, 0, NULL, &plain, 0, NULL);//strip out any markup
2259 gchar *text = NULL;
2260 if (plain)
2261 text = g_strdup_printf (_( "%sCommand: %s\n%s\nLocation: %s\nInternal Name: %s"), hidden?_("WARNING!!:\nThis command is hidden.\nMost likely you want to continue your search for a better command.\nHidden commands are either for LilyPond users or low-level interfaces for more user-friendly versions.\n"):"", label, plain, menupath, gtk_action_get_name(action));
2262 g_free (plain);
2263
2264 if (text)
2265 gtk_text_buffer_set_text (text_buffer, text, -1);
2266 else
2267 gtk_text_buffer_set_text (text_buffer, "Internal Problem", -1);
2268
2269 g_free(text);
2270 g_free(menupath);
2271 }
2272 //perform the selection
2273 return TRUE;
2274 }
2275
2276 GtkWidget *
keymap_get_binding_view()2277 keymap_get_binding_view ()
2278 {
2279 GtkScrolledWindow *res2;
2280 GtkTreeView *res;
2281 GtkTreeViewColumn *col;
2282 GtkCellRenderer *renderer;
2283 GtkTreeSelection *selection;
2284
2285 //setting up the tree view
2286 res = GTK_TREE_VIEW (gtk_tree_view_new ());
2287
2288 col = gtk_tree_view_column_new ();
2289 gtk_tree_view_column_set_title (col, _("Shortcuts"));
2290 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2291 gtk_tree_view_append_column (res, col);
2292
2293 renderer = gtk_cell_renderer_text_new ();
2294 gtk_tree_view_column_pack_start (col, renderer, TRUE);
2295 gtk_tree_view_column_add_attribute (col, renderer, "text", 0);
2296
2297 selection = gtk_tree_view_get_selection (res);
2298 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
2299
2300 gtk_tree_view_set_enable_search (res, FALSE);
2301
2302 gtk_tree_view_set_reorderable (res, TRUE);
2303 //setting up the scrolledwindow
2304 res2 = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0), gtk_adjustment_new (1.0, 1.0, 2.0, 1.0, 4.0, 1.0)));
2305 gtk_container_add (GTK_CONTAINER (res2), GTK_WIDGET (res));
2306 gtk_scrolled_window_set_policy (res2, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2307
2308 //FIXME adapt so that 10~15 rows are visible
2309 gtk_widget_set_size_request (GTK_WIDGET (res2), -1, 300);
2310
2311 return GTK_WIDGET (res2);
2312 }
2313