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