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