1 /*
2 ** 1999-03-16 - A module to deal with various forms of user controls. Currently, this
3 ** will simply implement global keyboard shortcuts. In future, this might
4 ** be the place to add configuration of mouse buttons, and stuff.
5 ** 1999-05-08 - Minor changes due to the fact that command sequences are now best stored
6 ** as GStrings.
7 ** 2000-02-18 - Completed implementation of new NumLock-ignore support. Should help users.
8 */
9
10 #include "gentoo.h"
11
12 #include "strutil.h"
13
14 #include "controls.h"
15
16 /* ----------------------------------------------------------------------------------------- */
17
18 struct CtrlInfo {
19 MainInfo *min; /* So incredibly handy to have around. */
20
21 gboolean nonumlock; /* Ignore num lock for both keyboard and mouse bindings? */
22
23 GHashTable *keys; /* Contains all key-to-cmdseq bindings. */
24 GSList *keys_inst; /* List of KbdContexts into which we're currently installed. */
25
26 GList *mouse; /* List of CtrlMouse mouse button mappings. Why hurry? */
27
28 struct {
29 GString *cmdseq; /* Command assigned to Click-M-Click action gesture. */
30 gfloat delay; /* Max delay in seconds to trigger cmc. */
31 } clickmclick;
32
33 GHashTable *general; /* General bindings. */
34 };
35
36 struct CtrlKey {
37 gchar keyname[KEY_NAME_SIZE];
38 GString *cmdseq;
39 };
40
41 struct CtrlMouse {
42 guint button; /* Which button this is for (in range 1..5). */
43 guint state; /* Modifier bits that need to be set (see GdkModifierType). */
44 GString *cmdseq; /* The command we run when triggerec. */
45 };
46
47 #define CONTEXT_NAME_SIZE (32)
48
49 typedef struct {
50 gchar context[CONTEXT_NAME_SIZE];
51 GString *cmdseq;
52 } CtrlGen;
53
54 /* ----------------------------------------------------------------------------------------- */
55
56 static CtrlKey * key_new(const gchar *keyname, const gchar *cmdseq);
57 static void key_destroy(CtrlKey *key);
58 static void mouse_destroy(CtrlMouse *cm);
59
60 /* ----------------------------------------------------------------------------------------- */
61
62 /* 1999-03-16 - Create a new control info, the main representation of this module's work. */
ctrl_new(MainInfo * min)63 CtrlInfo * ctrl_new(MainInfo *min)
64 {
65 CtrlInfo *ctrl;
66
67 ctrl = g_malloc(sizeof *ctrl);
68 ctrl->min = min;
69 ctrl->nonumlock = TRUE;
70 ctrl->keys = g_hash_table_new(g_str_hash, g_str_equal);
71 ctrl->keys_inst = NULL;
72
73 ctrl->mouse = NULL;
74
75 ctrl->clickmclick.cmdseq = NULL;
76 ctrl->clickmclick.delay = 0.400f;
77
78 ctrl->general = NULL;
79
80 return ctrl;
81 }
82
83 /* 1999-03-16 - Return a control info with the default controls already configured into it.
84 ** These are simply the few keys that have had hardcoded functions since more
85 ** or less the beginning of gentoo. It's nice to see them reborn like this. :)
86 */
ctrl_new_default(MainInfo * min)87 CtrlInfo * ctrl_new_default(MainInfo *min)
88 {
89 CtrlInfo *ctrl;
90
91 ctrl = ctrl_new(min);
92
93 ctrl_key_add(ctrl, "BackSpace", "DirParent");
94 ctrl_key_add(ctrl, "Delete", "Delete");
95 ctrl_key_add(ctrl, "F2", "Rename");
96 ctrl_key_add(ctrl, "F5", "DirRescan");
97 ctrl_key_add(ctrl, "h", "DpHide");
98 ctrl_key_add(ctrl, "r", "DpRecenter");
99 ctrl_key_add(ctrl, "space", "ActivateOther");
100
101 /* These are the "legacy" default actions for mouse button presses, that used to be hardcoded. */
102 ctrl_mouse_add(ctrl, 1, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "SelectSuffix action=toggle");
103 ctrl_mouse_add(ctrl, 1, GDK_MOD1_MASK, "SelectType action=toggle");
104 ctrl_mouse_add(ctrl, 1, GDK_MOD1_MASK | GDK_SHIFT_MASK, "SelectType action=select");
105 ctrl_mouse_add(ctrl, 1, GDK_MOD1_MASK | GDK_CONTROL_MASK, "SelectType action=unselect");
106 ctrl_mouse_add(ctrl, 2, 0U, "DirParent");
107 ctrl_mouse_add(ctrl, 3, 0U, "MenuPopup");
108
109 return ctrl;
110 }
111
112 /* 1999-03-16 - Remove a key; this is a g_hash_table_foreach_remove() callback. */
key_remove(gpointer key,gpointer value,gpointer user)113 static gboolean key_remove(gpointer key, gpointer value, gpointer user)
114 {
115 key_destroy(value);
116 return TRUE;
117 }
118
119 /* 1999-03-16 - Copy a key. A callback for ctrl_copy() below. */
key_copy(gpointer key,gpointer value,gpointer user)120 static void key_copy(gpointer key, gpointer value, gpointer user)
121 {
122 if(((CtrlKey *) value)->cmdseq != NULL)
123 ctrl_key_add(user, ((CtrlKey *) value)->keyname, ((CtrlKey *) value)->cmdseq->str);
124 else
125 ctrl_key_add(user, ((CtrlKey *) value)->keyname, NULL);
126 }
127
128 /* 1999-06-13 - A g_list_foreach() callback to destroy a mouse mapping. */
mouse_remove(gpointer data,gpointer user)129 static void mouse_remove(gpointer data, gpointer user)
130 {
131 mouse_destroy(data);
132 }
133
134 /* 1999-06-13 - Copy a mouse button command mapping. */
mouse_copy(gpointer data,gpointer user)135 static void mouse_copy(gpointer data, gpointer user)
136 {
137 CtrlMouse *cm = data;
138
139 ctrl_mouse_add(user, ctrl_mouse_get_button(cm), ctrl_mouse_get_state(cm), ctrl_mouse_get_cmdseq(cm));
140 }
141
142 /* 2004-04-25 - Copy a general binding. */
general_copy(gpointer key,gpointer value,gpointer user)143 static void general_copy(gpointer key, gpointer value, gpointer user)
144 {
145 if(value != NULL && ((CtrlGen *) value)->cmdseq != NULL) /* Don't copy NULL cmdseq. */
146 ctrl_general_set_cmdseq(user, key, ((CtrlGen *) value)->cmdseq->str);
147 }
148
149 /* 2004-04-25 - Remove a general binding. */
general_remove(gpointer key,gpointer value,gpointer user)150 static gboolean general_remove(gpointer key, gpointer value, gpointer user)
151 {
152 CtrlGen *cg = value;
153
154 if(cg != NULL)
155 {
156 if(cg->cmdseq != NULL)
157 g_string_free(cg->cmdseq, TRUE);
158 g_free(cg);
159 }
160 return TRUE;
161 }
162
163 /* 1999-03-16 - Copy the entire control info structure. Very useful for config. Note that
164 ** the copy will NOT retain the installation status of the original; in fact,
165 ** it will not be installed anywhere (which is generally what you (should) want).
166 */
ctrl_copy(CtrlInfo * ctrl)167 CtrlInfo * ctrl_copy(CtrlInfo *ctrl)
168 {
169 CtrlInfo *copy = NULL;
170
171 if(ctrl != NULL)
172 {
173 copy = ctrl_new(ctrl->min);
174 g_hash_table_foreach(ctrl->keys, key_copy, copy);
175 g_list_foreach(ctrl->mouse, mouse_copy, copy);
176 ctrl_clickmclick_set_cmdseq(copy, ctrl_clickmclick_get_cmdseq(ctrl));
177 ctrl_clickmclick_set_delay(copy, ctrl_clickmclick_get_delay(ctrl));
178 if(ctrl->general != NULL)
179 g_hash_table_foreach(ctrl->general, general_copy, copy);
180 ctrl_numlock_ignore_set(copy, ctrl_numlock_ignore_get(ctrl));
181 }
182 return copy;
183 }
184
185 /* 1999-03-16 - Destroy a control info. */
ctrl_destroy(CtrlInfo * ctrl)186 void ctrl_destroy(CtrlInfo *ctrl)
187 {
188 if(ctrl != NULL)
189 {
190 ctrl_keys_uninstall_all(ctrl);
191 g_hash_table_foreach_remove(ctrl->keys, key_remove, NULL);
192 g_hash_table_destroy(ctrl->keys);
193 g_list_foreach(ctrl->mouse, mouse_remove, NULL);
194 if(ctrl->clickmclick.cmdseq)
195 g_string_free(ctrl->clickmclick.cmdseq, TRUE);
196 if(ctrl->general != NULL)
197 {
198 g_hash_table_foreach_remove(ctrl->general, general_remove, NULL);
199 g_hash_table_destroy(ctrl->general);
200 }
201 g_free(ctrl);
202 }
203 }
204
205 /* ----------------------------------------------------------------------------------------- */
206
ctrl_numlock_ignore_set(CtrlInfo * ctrl,gboolean ignore)207 void ctrl_numlock_ignore_set(CtrlInfo *ctrl, gboolean ignore)
208 {
209 if(ctrl != NULL)
210 ctrl->nonumlock = ignore;
211 }
212
ctrl_numlock_ignore_get(CtrlInfo * ctrl)213 gboolean ctrl_numlock_ignore_get(CtrlInfo *ctrl)
214 {
215 if(ctrl != NULL)
216 return ctrl->nonumlock;
217 return FALSE;
218 }
219
220 /* ----------------------------------------------------------------------------------------- */
221
222 /* 1999-03-16 - Create a new key control with the given mapping. */
key_new(const gchar * keyname,const gchar * cmdseq)223 static CtrlKey * key_new(const gchar *keyname, const gchar *cmdseq)
224 {
225 CtrlKey *key;
226
227 key = g_malloc(sizeof *key);
228 g_strlcpy(key->keyname, keyname, sizeof key->keyname);
229 if(cmdseq != NULL)
230 key->cmdseq = g_string_new(cmdseq);
231 else
232 key->cmdseq = NULL;
233
234 return key;
235 }
236
237 /* 1999-03-16 - Destroy a key mapping. */
key_destroy(CtrlKey * key)238 static void key_destroy(CtrlKey *key)
239 {
240 if(key != NULL)
241 {
242 if(key->cmdseq != NULL)
243 g_string_free(key->cmdseq, TRUE);
244 g_free(key);
245 }
246 }
247
248 /* 1999-03-16 - Add a keyboard mapping to the given <ctrl>. Note that if the ctrl has
249 ** already been installed, adding keys to it won't activate them until it
250 ** is reinstalled.
251 */
ctrl_key_add(CtrlInfo * ctrl,const gchar * keyname,const gchar * cmdseq)252 CtrlKey * ctrl_key_add(CtrlInfo *ctrl, const gchar *keyname, const gchar *cmdseq)
253 {
254 CtrlKey *key = NULL;
255
256 if((ctrl != NULL) && (keyname != NULL))
257 {
258 key = key_new(keyname, cmdseq);
259 ctrl_key_remove_by_name(ctrl, key->keyname);
260 g_hash_table_insert(ctrl->keys, key->keyname, key);
261 }
262 return key;
263 }
264
265 /* 1999-03-17 - Add a new key->command mapping, with the rather peculiar property that
266 ** the key name is in fact illegal (it is not an existing key), and the
267 ** command part is empty. The key name will, however, be unique among all
268 ** existing maps. Useful when adding keys interactively.
269 */
ctrl_key_add_unique(CtrlInfo * ctrl)270 CtrlKey * ctrl_key_add_unique(CtrlInfo *ctrl)
271 {
272 gchar buf[KEY_NAME_SIZE], *name = NULL;
273 gint i;
274
275 for(i = 0; (name == NULL) && (i < 1000); i++) /* Um, not quite unique, since we can give up. */
276 {
277 if(i == 0)
278 g_snprintf(buf, sizeof buf, "none");
279 else
280 g_snprintf(buf, sizeof buf, "none-%d", i + 1);
281 if(g_hash_table_lookup(ctrl->keys, buf) == NULL)
282 name = buf;
283 }
284 return ctrl_key_add(ctrl, name, NULL);
285 }
286
287 /* 1999-03-16 - Remove the given key mapping, and also destroy it. */
ctrl_key_remove(CtrlInfo * ctrl,CtrlKey * key)288 void ctrl_key_remove(CtrlInfo *ctrl, CtrlKey *key)
289 {
290 if((ctrl != NULL) && (key != NULL))
291 ctrl_key_remove_by_name(ctrl, key->keyname);
292 }
293
294 /* 1999-03-16 - Remove a named key mapping, and destroy it. */
ctrl_key_remove_by_name(CtrlInfo * ctrl,const gchar * keyname)295 void ctrl_key_remove_by_name(CtrlInfo *ctrl, const gchar *keyname)
296 {
297 if((ctrl != NULL) && (keyname != NULL))
298 {
299 CtrlKey *key;
300
301 if((key = g_hash_table_lookup(ctrl->keys, keyname)) != NULL)
302 {
303 g_hash_table_remove(ctrl->keys, key->keyname);
304 key_destroy(key);
305 }
306 }
307 }
308
309 /* 1999-03-17 - Remove all key mappings from <ctrl>. It is a very good idea to first
310 ** uninstall it from (all) keyboard contexts.
311 */
ctrl_key_remove_all(CtrlInfo * ctrl)312 void ctrl_key_remove_all(CtrlInfo *ctrl)
313 {
314 if(ctrl != NULL)
315 g_hash_table_foreach_remove(ctrl->keys, key_remove, NULL);
316 }
317
318 /* 1999-03-16 - Return the key part of given key->command mapping. */
ctrl_key_get_keyname(const CtrlKey * key)319 const gchar * ctrl_key_get_keyname(const CtrlKey *key)
320 {
321 if(key != NULL)
322 return key->keyname;
323 return NULL;
324 }
325
326 /* 1999-03-16 - Return the command part of a key-to-command mapping. */
ctrl_key_get_cmdseq(const CtrlKey * key)327 const gchar * ctrl_key_get_cmdseq(const CtrlKey *key)
328 {
329 if((key != NULL) && (key->cmdseq != NULL))
330 return key->cmdseq->str;
331 return NULL;
332 }
333
334 /* 1999-03-16 - Change the key part of the given mapping. Changes it in the
335 ** <ctrl> as well. Is basically equivalent to a remove followed
336 ** by an add, but slightly more efficient.
337 */
ctrl_key_set_keyname(CtrlInfo * ctrl,CtrlKey * key,const gchar * keyname)338 void ctrl_key_set_keyname(CtrlInfo *ctrl, CtrlKey *key, const gchar *keyname)
339 {
340 if((ctrl != NULL) && (key != NULL) && (keyname != NULL))
341 {
342 if(g_hash_table_lookup(ctrl->keys, key->keyname) == key)
343 {
344 g_hash_table_remove(ctrl->keys, key->keyname);
345 g_strlcpy(key->keyname, keyname, sizeof key->keyname);
346 g_hash_table_insert(ctrl->keys, key->keyname, key);
347 }
348 }
349 }
350
351 /* 1999-03-16 - Change command sequence part of given mapping. Equivalent to,
352 ** but far more efficient than, a remove+add pair.
353 */
ctrl_key_set_cmdseq(CtrlInfo * ctrl,CtrlKey * key,const gchar * cmdseq)354 void ctrl_key_set_cmdseq(CtrlInfo *ctrl, CtrlKey *key, const gchar *cmdseq)
355 {
356 if((ctrl != NULL) && (key != NULL))
357 {
358 if(g_hash_table_lookup(ctrl->keys, key->keyname) == key)
359 {
360 if(cmdseq != NULL)
361 {
362 if(key->cmdseq != NULL)
363 g_string_assign(key->cmdseq, cmdseq);
364 else
365 key->cmdseq = g_string_new(cmdseq);
366 }
367 else
368 {
369 if(key->cmdseq != NULL)
370 g_string_free(key->cmdseq, TRUE);
371 key->cmdseq = NULL;
372 }
373 }
374 }
375 }
376
377 /* 1999-05-08 - Return TRUE if given <key> already has a command sequence equal to <cmdseq>.
378 ** If not, FALSE is returned.
379 */
ctrl_key_has_cmdseq(CtrlInfo * ctrl,CtrlKey * key,const gchar * cmdseq)380 gboolean ctrl_key_has_cmdseq(CtrlInfo *ctrl, CtrlKey *key, const gchar *cmdseq)
381 {
382 if((ctrl != NULL) && (key != NULL) && (cmdseq != NULL))
383 {
384 if(key->cmdseq != NULL)
385 return strcmp(key->cmdseq->str, cmdseq) ? FALSE : TRUE;
386 return FALSE;
387 }
388 return FALSE;
389 }
390
391 /* ----------------------------------------------------------------------------------------- */
392
393 /* 1999-03-16 - Just a simple hash callback to install a CtrlKey into a keyboard context. */
key_install(gpointer key,gpointer value,gpointer user)394 static void key_install(gpointer key, gpointer value, gpointer user)
395 {
396 if(((CtrlKey *) value)->cmdseq != NULL)
397 kbd_context_entry_add(user, ((CtrlKey *) value)->keyname, KET_CMDSEQ, ((CtrlKey *) value)->cmdseq->str);
398 }
399
400 /* 1999-03-16 - Install all <ctrl>'s key mappings as command-type keyboard commands in <ctx>.
401 ** Note that the set of which keyboard contexts a CtrlInfo has been installed in
402 ** is a retained property in the CtrlInfo.
403 */
ctrl_keys_install(CtrlInfo * ctrl,KbdContext * ctx)404 void ctrl_keys_install(CtrlInfo *ctrl, KbdContext *ctx)
405 {
406 if((ctrl != NULL) && (ctx != NULL))
407 {
408 /* FIXME: This really assumes there is only one attachment. */
409 if(ctrl->nonumlock)
410 kbd_context_mask_set(ctx, GDK_MOD2_MASK);
411 else
412 kbd_context_mask_set(ctx, 0);
413
414 g_hash_table_foreach(ctrl->keys, key_install, ctx);
415 ctrl->keys_inst = g_slist_prepend(ctrl->keys_inst, ctx);
416 }
417 }
418
419 /* 1999-03-16 - Callback for ctrl_keys_uninstall() below. */
key_uninstall(gpointer key,gpointer value,gpointer user)420 static void key_uninstall(gpointer key, gpointer value, gpointer user)
421 {
422 kbd_context_entry_remove(user, ((CtrlKey *) value)->keyname);
423 }
424
425 /* 1999-03-16 - Uninstall all keys defined by <ctrl> from context <ctx>. If, in fact, the
426 ** ctrl has never been installed in the context, nothing happens. Does not
427 ** (ever) affect which mappings are contained in the ctrl.
428 */
ctrl_keys_uninstall(CtrlInfo * ctrl,KbdContext * ctx)429 void ctrl_keys_uninstall(CtrlInfo *ctrl, KbdContext *ctx)
430 {
431 if((ctrl != NULL) && (ctx != NULL))
432 {
433 if(g_slist_find(ctrl->keys_inst, ctx) != NULL)
434 {
435 g_hash_table_foreach(ctrl->keys, key_uninstall, ctx);
436 ctrl->keys_inst = g_slist_remove(ctrl->keys_inst, ctx);
437 }
438 }
439 }
440
441 /* 1999-03-17 - A foreach callback for ctrl_keys_uninstall_all() below. Does not just
442 ** call ctrl_keys_uninstall(), since that modofies the list which is a bad
443 ** idea when iterating it...
444 */
keys_uninstall(gpointer data,gpointer user)445 static void keys_uninstall(gpointer data, gpointer user)
446 {
447 g_hash_table_foreach(((CtrlInfo *) user)->keys, key_uninstall, data);
448 }
449
450 /* 1999-03-17 - Uninstall given ctrlinfo from all of its keyboard contexts. Does not alter
451 ** the actual collection of key mappings in the ctrlinfo.
452 */
ctrl_keys_uninstall_all(CtrlInfo * ctrl)453 void ctrl_keys_uninstall_all(CtrlInfo *ctrl)
454 {
455 if(ctrl != NULL)
456 {
457 g_slist_foreach(ctrl->keys_inst, keys_uninstall, ctrl);
458 g_slist_free(ctrl->keys_inst);
459 ctrl->keys_inst = NULL;
460 }
461 }
462
463 /* ----------------------------------------------------------------------------------------- */
464
cmp_string(gconstpointer a,gconstpointer b)465 static gint cmp_string(gconstpointer a, gconstpointer b)
466 {
467 return strcmp(a, b);
468 }
469
key_insert(gpointer key,gpointer value,gpointer user)470 static void key_insert(gpointer key, gpointer value, gpointer user)
471 {
472 GSList **list = user;
473
474 *list = g_slist_insert_sorted(*list, value, cmp_string);
475 }
476
477 /* 1999-03-16 - Return a list of all <ctrl>'s key mappings, sorted on the key names. */
ctrl_keys_get_list(CtrlInfo * ctrl)478 GSList * ctrl_keys_get_list(CtrlInfo *ctrl)
479 {
480 GSList *list = NULL;
481
482 if(ctrl != NULL)
483 g_hash_table_foreach(ctrl->keys, key_insert, &list);
484
485 return list;
486 }
487
488 /* ----------------------------------------------------------------------------------------- */
489
490 /* 1999-06-12 - Create a new mouse command mapping. Empty. */
mouse_new(void)491 static CtrlMouse * mouse_new(void)
492 {
493 CtrlMouse *cm;
494
495 cm = g_malloc(sizeof *cm);
496
497 cm->button = cm->state = 0U;
498 cm->cmdseq = NULL;
499
500 return cm;
501 }
502
503 /* 1999-06-12 - Destroy a mouse mapping which is no longer needed (or used). */
mouse_destroy(CtrlMouse * cm)504 static void mouse_destroy(CtrlMouse *cm)
505 {
506 if(cm != NULL)
507 {
508 if(cm->cmdseq != NULL)
509 g_string_free(cm->cmdseq, TRUE);
510 g_free(cm);
511 }
512 }
513
514 /* 1999-06-12 - Compare two mouse command definitions, ordering them in mouse button order. */
mouse_cmp(gconstpointer a,gconstpointer b)515 static gint mouse_cmp(gconstpointer a, gconstpointer b)
516 {
517 const CtrlMouse *ma = a, *mb = b;
518
519 if(ma->button == mb->button)
520 return ma->state - mb->state;
521 return ma->button - mb->button;
522 }
523
524 /* 1999-06-12 - Add a command mapping for a mouse button. Note that this will not prevent the
525 ** creation of multiple identical mappings, something that is silly at best, and
526 ** dangerous at worst. Beware.
527 */
ctrl_mouse_add(CtrlInfo * ctrl,guint button,guint state,const gchar * cmdseq)528 CtrlMouse * ctrl_mouse_add(CtrlInfo *ctrl, guint button, guint state, const gchar *cmdseq)
529 {
530 CtrlMouse *cm = NULL;
531
532 if(ctrl != NULL)
533 {
534 cm = mouse_new();
535
536 cm->button = button;
537 cm->state = state;
538 cm->cmdseq = g_string_new(cmdseq);
539
540 ctrl->mouse = g_list_insert_sorted(ctrl->mouse, cm, mouse_cmp);
541 }
542 return cm;
543 }
544
545 /* 1999-06-13 - Set a new button for a mapping. */
ctrl_mouse_set_button(CtrlMouse * mouse,guint button)546 void ctrl_mouse_set_button(CtrlMouse *mouse, guint button)
547 {
548 if(mouse != NULL)
549 mouse->button = button;
550 }
551
552 /* 1999-06-13 - Set a new modifier state for a mapping. */
ctrl_mouse_set_state(CtrlMouse * mouse,guint state)553 void ctrl_mouse_set_state(CtrlMouse *mouse, guint state)
554 {
555 if(mouse != NULL)
556 mouse->state = state;
557 }
558
559 /* 1999-06-13 - Set a new command sequence for the given mapping. */
ctrl_mouse_set_cmdseq(CtrlMouse * mouse,const gchar * cmdseq)560 void ctrl_mouse_set_cmdseq(CtrlMouse *mouse, const gchar *cmdseq)
561 {
562 if(mouse != NULL)
563 g_string_assign(mouse->cmdseq, cmdseq);
564 }
565
566 /* 1999-06-13 - Access the button part of a mouse command mapping. */
ctrl_mouse_get_button(const CtrlMouse * mouse)567 guint ctrl_mouse_get_button(const CtrlMouse *mouse)
568 {
569 if(mouse != NULL)
570 return mouse->button;
571 return 0;
572 }
573
574 /* 1999-06-13 - Get the state bits for a mouse button command mapping. */
ctrl_mouse_get_state(const CtrlMouse * mouse)575 guint ctrl_mouse_get_state(const CtrlMouse *mouse)
576 {
577 if(mouse != NULL)
578 return mouse->state;
579 return 0;
580 }
581
582 /* 1999-06-13 - Get the command sequence for a mouse mapping. */
ctrl_mouse_get_cmdseq(const CtrlMouse * mouse)583 const gchar * ctrl_mouse_get_cmdseq(const CtrlMouse *mouse)
584 {
585 if(mouse != NULL)
586 return mouse->cmdseq->str;
587 return NULL;
588 }
589
590 /* 1999-06-20 - Remove the mouse mapping <mouse> from <ctrl>. */
ctrl_mouse_remove(CtrlInfo * ctrl,CtrlMouse * mouse)591 void ctrl_mouse_remove(CtrlInfo *ctrl, CtrlMouse *mouse)
592 {
593 if((ctrl != NULL) && (mouse != NULL))
594 ctrl->mouse = g_list_remove(ctrl->mouse, mouse);
595 }
596
597 /* 1999-06-20 - Clear away all mouse mappings from <ctrl>. */
ctrl_mouse_remove_all(CtrlInfo * ctrl)598 void ctrl_mouse_remove_all(CtrlInfo *ctrl)
599 {
600 if(ctrl != NULL)
601 {
602 GList *iter;
603
604 for(iter = ctrl->mouse; iter != NULL; iter = g_list_next(iter))
605 mouse_destroy(iter->data);
606 g_list_free(ctrl->mouse);
607 ctrl->mouse = NULL;
608 }
609 }
610
611 /* 1999-06-12 - Check if we have a mapping for the mouse button event described by <evt>, and if we
612 ** do, return a pointer to the command definition for the caller to execute. Else
613 ** returns NULL.
614 */
ctrl_mouse_map(CtrlInfo * ctrl,GdkEventButton * evt)615 const gchar * ctrl_mouse_map(CtrlInfo *ctrl, GdkEventButton *evt)
616 {
617 if((ctrl != NULL) && (evt != NULL))
618 {
619 GList *iter;
620 CtrlMouse *cm;
621 guint state;
622
623 state = evt->state;
624 /* Allow only the modifiers known to be configurable for mouse buttons. */
625 state &= (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK);
626
627 for(iter = ctrl->mouse; iter != NULL; iter = g_list_next(iter))
628 {
629 cm = iter->data;
630 if((cm->button == evt->button) && (cm->state == state))
631 return cm->cmdseq->str;
632 }
633 }
634 return NULL;
635 }
636
637 /* 1999-06-13 - Get a list of all CtrlMouse mouse button command mappings. When done with the
638 ** list, the caller must call g_slist_free() on it.
639 */
ctrl_mouse_get_list(CtrlInfo * ctrl)640 GSList * ctrl_mouse_get_list(CtrlInfo *ctrl)
641 {
642 GSList *list = NULL;
643
644 if(ctrl != NULL)
645 {
646 GList *iter;
647
648 for(iter = ctrl->mouse; iter != NULL; iter = g_list_next(iter))
649 list = g_slist_append(list, iter->data);
650 }
651
652 return list;
653 }
654
655 /* 1999-06-20 - This (borderline silly) function answers whether there exists a "collision" in the
656 ** mouse command mappings, i.e. if the same button+state combo is used more than once.
657 ** Ultimately, it shouldn't be possible to create such a situation, but in the current
658 ** GUI it is. So this function can be used to at least warn the user.
659 */
ctrl_mouse_ambiguity_exists(const CtrlInfo * ctrl)660 gboolean ctrl_mouse_ambiguity_exists(const CtrlInfo *ctrl)
661 {
662 if(ctrl != NULL)
663 {
664 GList *i, *j;
665
666 for(i = ctrl->mouse; i != NULL; i = g_list_next(i))
667 {
668 for(j = ctrl->mouse; j != NULL; j = g_list_next(j))
669 {
670 if(j == i)
671 continue;
672 if(mouse_cmp(i->data, j->data) == 0)
673 return TRUE;
674 }
675 }
676 }
677 return FALSE;
678 }
679
680 /* ----------------------------------------------------------------------------------------- */
681
ctrl_clickmclick_set_cmdseq(CtrlInfo * ctrl,const gchar * cmdseq)682 void ctrl_clickmclick_set_cmdseq(CtrlInfo *ctrl, const gchar *cmdseq)
683 {
684 if(cmdseq)
685 {
686 if(ctrl->clickmclick.cmdseq)
687 g_string_assign(ctrl->clickmclick.cmdseq, cmdseq);
688 else
689 ctrl->clickmclick.cmdseq = g_string_new(cmdseq);
690 }
691 else if(ctrl->clickmclick.cmdseq)
692 {
693 g_string_free(ctrl->clickmclick.cmdseq, TRUE);
694 ctrl->clickmclick.cmdseq = NULL;
695 }
696 }
697
ctrl_clickmclick_get_cmdseq(const CtrlInfo * ctrl)698 const gchar * ctrl_clickmclick_get_cmdseq(const CtrlInfo *ctrl)
699 {
700 return ctrl ? ctrl->clickmclick.cmdseq ? ctrl->clickmclick.cmdseq->str : 0 : 0;
701 }
702
ctrl_clickmclick_set_delay(CtrlInfo * ctrl,gfloat delay)703 void ctrl_clickmclick_set_delay(CtrlInfo *ctrl, gfloat delay)
704 {
705 ctrl->clickmclick.delay = delay;
706 }
707
ctrl_clickmclick_get_delay(const CtrlInfo * ctrl)708 gfloat ctrl_clickmclick_get_delay(const CtrlInfo *ctrl)
709 {
710 return ctrl ? ctrl->clickmclick.delay : 0.0f;
711 }
712
713 /* ----------------------------------------------------------------------------------------- */
714
715 /* 2004-04-26 - Clear away all general command definitions. */
ctrl_general_clear(CtrlInfo * ctrl)716 void ctrl_general_clear(CtrlInfo *ctrl)
717 {
718 if(ctrl == NULL || ctrl->general == NULL)
719 return;
720 g_hash_table_foreach_remove(ctrl->general, general_remove, NULL);
721 }
722
723 /* 2004-04-19 - Store a new general command sequence. */
ctrl_general_set_cmdseq(CtrlInfo * ctrl,const gchar * context,const gchar * cmdseq)724 void ctrl_general_set_cmdseq(CtrlInfo *ctrl, const gchar *context, const gchar *cmdseq)
725 {
726 CtrlGen *cg;
727
728 if(ctrl == NULL)
729 return;
730 if(ctrl->general == NULL)
731 ctrl->general = g_hash_table_new(g_str_hash, g_str_equal);
732 cg = g_hash_table_lookup(ctrl->general, context);
733 if(cg == NULL)
734 {
735 cg = g_malloc(sizeof *cg);
736 g_snprintf(cg->context, sizeof cg->context, "%s", context);
737 cg->cmdseq = g_string_new("");
738 g_hash_table_insert(ctrl->general, cg->context, cg);
739 }
740 if(cmdseq != NULL)
741 g_string_assign(cg->cmdseq, cmdseq);
742 else
743 general_remove(cg->context, cg, ctrl);
744 }
745
general_prepend(gpointer key,gpointer data,gpointer user)746 static void general_prepend(gpointer key, gpointer data, gpointer user)
747 {
748 GSList **list = user;
749
750 *list = g_slist_prepend(*list, key);
751 }
752
753 /* 2004-04-25 - Return list of contexts, i.e. char pointers. They are constants. */
ctrl_general_get_contexts(const CtrlInfo * ctrl)754 GSList * ctrl_general_get_contexts(const CtrlInfo *ctrl)
755 {
756 GSList *list = NULL;
757
758 if(ctrl != NULL && ctrl->general != NULL)
759 g_hash_table_foreach(ctrl->general, general_prepend, &list);
760 return list;
761 }
762
763 /* 2004-04-19 - Retrieve general command sequence. */
ctrl_general_get_cmdseq(const CtrlInfo * ctrl,const gchar * context)764 const gchar * ctrl_general_get_cmdseq(const CtrlInfo *ctrl, const gchar *context)
765 {
766 const CtrlGen *cg;
767
768 if(ctrl == NULL || ctrl->general == NULL)
769 return NULL;
770 cg = g_hash_table_lookup(ctrl->general, context);
771 if(cg == NULL)
772 return NULL;
773 return cg->cmdseq != NULL ? cg->cmdseq->str : NULL;
774 }
775