1 /*
2 ** 1999-03-16 -	Configure controls. Initially, this will only have global keyboard shortcuts
3 **		to deal with, but it may get more in the future. This module relies heavily
4 **		on the services provided by the 'controls' module, of course.
5 */
6 
7 #include "gentoo.h"
8 
9 #include "cmdseq_dialog.h"
10 
11 #include "cmdseq.h"
12 #include "controls.h"
13 #include "dialog.h"
14 #include "guiutil.h"
15 #include "strutil.h"
16 
17 #include "configure.h"
18 #include "cfg_module.h"
19 #include "cfg_cmdseq.h"				/* For ccs_current(). */
20 #include "cfg_controls.h"
21 
22 #define	NODE	"Controls"
23 
24 /* ----------------------------------------------------------------------------------------- */
25 
26 #include "graphics/icon_mouse1.xpm"
27 #include "graphics/icon_mouse2.xpm"
28 #include "graphics/icon_mouse3.xpm"
29 #include "graphics/icon_mouse4.xpm"
30 #include "graphics/icon_mouse5.xpm"
31 
32 /* ----------------------------------------------------------------------------------------- */
33 
34 typedef struct {
35 	GtkWidget	*vbox;			/* Root page container. Very standard. */
36 
37 	GtkWidget	*kframe;		/* Keyboard frame. */
38 	GtkListStore	*kstore;		/* Stores keyboard shortcuts. */
39 	GtkWidget	*kview;			/* A tree view for the keyboard shortcuts. */
40 	GtkWidget	*kdtable;		/* Key definition table. */
41 	GtkWidget	*kdkey;			/* Definition key name (entry). */
42 	GtkWidget	*kdcmd;			/* Definition cmdsequence (entry). */
43 	GtkWidget	*kdcmdpick;		/* Definition command pick button. */
44 	GtkWidget	*kadd;			/* Keyboard add button. */
45 	GtkWidget	*kdel;			/* Keyboard delete button. */
46 
47 	GtkWidget	*mframe;		/* Dirpane mouse button config frame. */
48 	GtkListStore	*mstore;		/* Stores mouse button configs. */
49 	GtkWidget	*mview;			/* A tree view for mouse button settings. */
50 	GtkWidget	*mscwin;		/* Scrolling window (obsolete). */
51 	GtkWidget	*mdtable;		/* Definition table. */
52 	GtkWidget	*mdcombo;		/* Combobox showing mouse button for mapping. */
53 	GtkWidget	*mdcmd;
54 	GtkWidget	*mdcmdpick;
55 	GtkWidget	*mhbox;
56 	GtkWidget	*madd, *mdel;
57 
58 	GdkPixbuf	*mpbuf[5];		/* Mouse icon pixbufs. */
59 
60 	GtkWidget	*cmccmd;		/* Entry widget for Click-M-Click command. */
61 	GtkWidget	*cmcdelay;		/* HScale widget for cmc delay. */
62 
63 	GtkWidget	*nonumlock;		/* Ignore numlock check button. */
64 
65 	MainInfo	*min;			/* This is handy sometimes. */
66 	CtrlInfo	*ctrlinfo;		/* The control info we're editing. */
67 
68 	gboolean	modified;		/* Indicates that a change has been made. */
69 } P_Controls;
70 
71 enum {
72 	KEY_COLUMN_KEYNAME = 0,
73 	KEY_COLUMN_CMDSEQ,
74 	KEY_COLUMN_KEY,
75 
76 	KEY_COLUMN_COUNT
77 };
78 
79 enum {
80 	MOUSE_COLUMN_ICON = 0,
81 	MOUSE_COLUMN_BUTTON,
82 	MOUSE_COLUMN_STATE,
83 	MOUSE_COLUMN_CMDSEQ,
84 	MOUSE_COLUMN_MOUSE,
85 
86 	MOUSE_COLUMN_COUNT
87 };
88 
89 static P_Controls	the_page;
90 
91 /* ----------------------------------------------------------------------------------------- */
92 
93 /* 1999-03-16 -	Reset the key definition widgets to their idle state. */
reset_key_widgets(P_Controls * page)94 static void reset_key_widgets(P_Controls *page)
95 {
96 	gtk_entry_set_text(GTK_ENTRY(page->kdkey), "");
97 	gtk_entry_set_text(GTK_ENTRY(page->kdcmd), "");
98 	gtk_widget_set_sensitive(page->kdtable, FALSE);
99 	gtk_widget_set_sensitive(page->kadd, TRUE);
100 	gtk_widget_set_sensitive(page->kdel, FALSE);
101 }
102 
103 /* 1999-06-13 -	Reset mouse editing widgets. */
reset_mouse_widgets(P_Controls * page)104 static void reset_mouse_widgets(P_Controls *page)
105 {
106 	gtk_combo_box_set_active(GTK_COMBO_BOX(page->mdcombo), -1);
107 	gtk_entry_set_text(GTK_ENTRY(page->mdcmd), "");
108 	gtk_widget_set_sensitive(page->mdtable, FALSE);
109 	gtk_widget_set_sensitive(page->madd, TRUE);
110 	gtk_widget_set_sensitive(page->mdel, FALSE);
111 }
112 
113 /* 1999-03-16 -	Reset all widgets on page to their most idle state. */
reset_widgets(P_Controls * page)114 static void reset_widgets(P_Controls *page)
115 {
116 	const gchar	*cmd;
117 
118 	reset_key_widgets(page);
119 	reset_mouse_widgets(page);
120 	cmd = ctrl_clickmclick_get_cmdseq(page->ctrlinfo);
121 	gtk_entry_set_text(GTK_ENTRY(page->cmccmd), cmd ? cmd : "");
122 	gtk_adjustment_set_value(gtk_range_get_adjustment(GTK_RANGE(page->cmcdelay)), ctrl_clickmclick_get_delay(page->ctrlinfo));
123 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->nonumlock), ctrl_numlock_ignore_get(page->ctrlinfo));
124 }
125 
126 /* ----------------------------------------------------------------------------------------- */
127 
list_store_append_key(P_Controls * page,const CtrlKey * key,GtkTreeIter * iter)128 static void list_store_append_key(P_Controls *page, const CtrlKey *key, GtkTreeIter *iter)
129 {
130 	GtkTreeIter	myiter;
131 
132 	/* If caller didn't provide an iter to use, use our local. */
133 	if(iter == NULL)
134 		iter = &myiter;
135 	gtk_list_store_insert_with_values(page->kstore, iter, -1,
136 			KEY_COLUMN_KEYNAME, ctrl_key_get_keyname(key),
137 			KEY_COLUMN_CMDSEQ,  ctrl_key_get_cmdseq(key),
138 			KEY_COLUMN_KEY,     key,
139 			-1);
140 }
141 
142 /* 1999-03-16 -	Populate keyboard clist. */
populate_key_list(P_Controls * page)143 static void populate_key_list(P_Controls *page)
144 {
145 	const GSList	*iter;
146 
147 	gtk_list_store_clear(page->kstore);
148 	for(iter = ctrl_keys_get_list(page->ctrlinfo); iter != NULL; iter = g_slist_next(iter))
149 		list_store_append_key(page, iter->data, NULL);
150 }
151 
list_store_append_mouse(P_Controls * page,const CtrlMouse * mouse,GtkTreeIter * iter)152 static void list_store_append_mouse(P_Controls *page, const CtrlMouse *mouse, GtkTreeIter *iter)
153 {
154 	gchar   	*bname[] = { N_("Left"), N_("Middle"), N_("Right"), N_("Wheel Up"), N_("Wheel Down") };
155 	guint		btn;
156 	GtkTreeIter	myiter;
157 
158 	/* If caller didn't provide an iter to use, use our local. */
159 	if(iter == NULL)
160 		iter = &myiter;
161 	btn = ctrl_mouse_get_button(mouse) - 1;
162 	gtk_list_store_insert_with_values(page->mstore, iter, -1,
163 			MOUSE_COLUMN_ICON,   page->mpbuf[btn],
164 			MOUSE_COLUMN_BUTTON, _(bname[btn]),
165 			MOUSE_COLUMN_STATE,  gtk_accelerator_name(0U, ctrl_mouse_get_state(mouse)),
166 			MOUSE_COLUMN_CMDSEQ, ctrl_mouse_get_cmdseq(mouse),
167 			MOUSE_COLUMN_MOUSE,  mouse,
168 			-1);
169 }
170 
171 /* 1999-06-13 -	Populate the mouse command mapping list. */
populate_mouse_list(P_Controls * page)172 static void populate_mouse_list(P_Controls *page)
173 {
174 	const GSList	*iter;
175 
176 	gtk_list_store_clear(page->mstore);
177 	for(iter = ctrl_mouse_get_list(page->ctrlinfo); iter != NULL; iter = g_slist_next(iter))
178 		list_store_append_mouse(page, iter->data, NULL);
179 }
180 
181 /* ----------------------------------------------------------------------------------------- */
182 
183 /* 2009-03-14 -	Keyboard list selection changed. Updated widgetry for editing. */
evt_kselection_changed(GtkTreeSelection * ts,gpointer user)184 static void evt_kselection_changed(GtkTreeSelection *ts, gpointer user)
185 {
186 	P_Controls	*page = user;
187 	GtkTreeModel	*model;
188 	GtkTreeIter	iter;
189 	CtrlKey		*key = NULL;
190 
191 	if(gtk_tree_selection_get_selected(ts, &model, &iter))
192 		gtk_tree_model_get(model, &iter, KEY_COLUMN_KEY, &key, -1);
193 	gtk_entry_set_text(GTK_ENTRY(page->kdkey), key != NULL ? ctrl_key_get_keyname(key) : "");
194 	gtk_entry_set_text(GTK_ENTRY(page->kdcmd), key != NULL ? ctrl_key_get_cmdseq(key) : "");
195 	gtk_widget_set_sensitive(page->kdtable, key != NULL);
196 	gtk_widget_set_sensitive(page->kdel, key != NULL);
197 }
198 
199 /* 1999-03-16 -	User pressed something in the key definition entry. Convert to name and set it.
200 ** 2009-03-14 -	Adapted for new GTK+ 2.0 list selection management. 10 years is a good interval. :)
201  */
evt_kkey_press(GtkWidget * wid,GdkEventKey * evt,gpointer user)202 static gint evt_kkey_press(GtkWidget *wid, GdkEventKey *evt, gpointer user)
203 {
204 	P_Controls	*page = user;
205 	GtkTreeModel	*model;
206 	GtkTreeIter	iter;
207 	CtrlKey		*key = NULL;
208 
209 	if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(page->kview)), &model, &iter))
210 	{
211 		gchar	*keyname = gtk_accelerator_name(evt->keyval, evt->state);
212 
213 		gtk_tree_model_get(model, &iter, 2, &key, -1);
214 		ctrl_key_set_keyname(page->ctrlinfo, key, keyname);
215 		gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, keyname, -1);
216 		gtk_entry_set_text(GTK_ENTRY(page->kdkey), keyname);
217 		g_signal_stop_emission_by_name(G_OBJECT(wid), "key_press_event");
218 		page->modified = TRUE;
219 	}
220 	return TRUE;
221 }
222 
223 /* 1999-03-16 -	User edited the command sequence, so store the new one. */
evt_kcmd_changed(GtkWidget * wid,gpointer user)224 static gint evt_kcmd_changed(GtkWidget *wid, gpointer user)
225 {
226 	P_Controls	*page = user;
227 	GtkTreeModel	*model;
228 	GtkTreeIter	iter;
229 	CtrlKey		*key = NULL;
230 
231 	if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(page->kview)), &model, &iter))
232 	{
233 		const gchar	*cmdseq = gtk_entry_get_text(GTK_ENTRY(wid));
234 
235 		gtk_tree_model_get(model, &iter, 2, &key, -1);
236 		if(!ctrl_key_has_cmdseq(page->ctrlinfo, key, cmdseq))
237 		{
238 			ctrl_key_set_cmdseq(page->ctrlinfo, key, cmdseq);
239 			gtk_list_store_set(GTK_LIST_STORE(model), &iter, 1, cmdseq, -1);
240 			page->modified = TRUE;
241 		}
242 	}
243 	return TRUE;
244 }
245 
246 /* ----------------------------------------------------------------------------------------- */
247 
248 /* 1999-03-16 -	User clicked the "pick" button for key command sequence.
249 ** 1999-03-29 -	Rewritten for new, simpler, csq_dialog() semantics.
250 */
evt_kcmdpick_clicked(GtkWidget * wid,gpointer user)251 static gint evt_kcmdpick_clicked(GtkWidget *wid, gpointer user)
252 {
253 	P_Controls	*page = user;
254 	const gchar	*cmd;
255 
256 	if((cmd = csq_dialog_sync_new_wait(page->min, ccs_get_current())) != NULL)
257 		gtk_entry_set_text(GTK_ENTRY(page->kdcmd), cmd);
258 	return TRUE;
259 }
260 
261 /* ----------------------------------------------------------------------------------------- */
262 
263 /* 1999-03-16 -	User just hit the "Add" key button so, er, add a key. */
evt_kadd_clicked(GtkWidget * wid,gpointer user)264 static gint evt_kadd_clicked(GtkWidget *wid, gpointer user)
265 {
266 	P_Controls	*page = user;
267 	CtrlKey		*key;
268 	GtkTreeIter	iter;
269 	GtkTreePath	*path;
270 
271 	key = ctrl_key_add_unique(page->ctrlinfo);
272 	ctrl_key_set_cmdseq(page->ctrlinfo, key, "");
273 	gtk_list_store_insert_with_values(page->kstore, &iter, -1,
274 				0, ctrl_key_get_keyname(key),
275 				1, ctrl_key_get_cmdseq(key),
276 				2, key,
277 				-1);
278 	gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(page->kview)), &iter);
279 	path = gtk_tree_model_get_path(GTK_TREE_MODEL(page->kstore), &iter);
280 	gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->kview), path, NULL, TRUE, 0.5f, 0.0f);
281 	gtk_tree_path_free(path);
282 	gtk_widget_grab_focus(page->kdkey);
283 	page->modified = TRUE;
284 
285 	return TRUE;
286 }
287 
288 /* 1999-03-16 -	User hit the "Delete" button for keys. */
evt_kdel_clicked(GtkWidget * wid,gpointer user)289 static gint evt_kdel_clicked(GtkWidget *wid, gpointer user)
290 {
291 	P_Controls	*page = user;
292 	GtkTreeModel	*model;
293 	GtkTreeIter	iter;
294 	CtrlKey		*key = NULL;
295 
296 	if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(page->kview)), &model, &iter))
297 	{
298 		gtk_tree_model_get(model, &iter, 2, &key, -1);
299 		ctrl_key_remove(page->ctrlinfo, key);
300 		gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
301 		page->modified = TRUE;
302 	}
303 	return TRUE;
304 }
305 
306 /* ----------------------------------------------------------------------------------------- */
307 
308 /* 2009-03-14 -	The selection in the mouse binding list has changed. Deal with it. */
evt_mselection_changed(GtkTreeSelection * ts,gpointer user)309 static void evt_mselection_changed(GtkTreeSelection *ts, gpointer user)
310 {
311 	P_Controls	*page = user;
312 	GtkTreeModel	*model;
313 	GtkTreeIter	iter;
314 	CtrlMouse	*mouse = NULL;
315 
316 	if(gtk_tree_selection_get_selected(ts, &model, &iter))
317 		gtk_tree_model_get(model, &iter, MOUSE_COLUMN_MOUSE, &mouse, -1);
318 	gtk_entry_set_text(GTK_ENTRY(page->mdcmd), mouse != NULL ? ctrl_mouse_get_cmdseq(mouse) : "");
319 	gtk_combo_box_set_active(GTK_COMBO_BOX(page->mdcombo), ctrl_mouse_get_button(mouse) - 1);
320 	gtk_widget_set_sensitive(page->mdtable, mouse != NULL);
321 	gtk_widget_set_sensitive(page->mdel, mouse != NULL);
322 }
323 
evt_mbutton_changed(GtkWidget * wid,gpointer user)324 static void evt_mbutton_changed(GtkWidget *wid, gpointer user)
325 {
326 	P_Controls	*page = user;
327 	GtkTreeSelection *ts;
328 	GtkTreeIter	ti, biter;
329 	CtrlMouse	*mouse;
330 	guint		btn;
331 
332 	ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->mview));
333 	if(ts == NULL || !gtk_tree_selection_get_selected(ts, NULL, &ti))
334 		return;
335 	gtk_tree_model_get(GTK_TREE_MODEL(page->mstore), &ti, MOUSE_COLUMN_MOUSE, &mouse, -1);
336 	btn = gtk_combo_box_get_active(GTK_COMBO_BOX(page->mdcombo));
337 	ctrl_mouse_set_button(mouse, btn + 1);
338 
339 	/* Almost-too-clever: grab the translated mouse button label from the GtkComboBox's model. */
340 	if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(page->mdcombo), &biter))
341 	{
342 		gchar	*button;
343 
344 		gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(page->mdcombo)), &biter, 1, &button, -1);
345 
346 		gtk_list_store_set(GTK_LIST_STORE(page->mstore), &ti,
347 					MOUSE_COLUMN_ICON, page->mpbuf[btn],
348 					MOUSE_COLUMN_BUTTON, button,
349 					-1);
350 		g_free(button);
351 	}
352 }
353 
354 /* 1999-06-13 -	User just hit the "Edit Modifiers..." button. Bring up a little dialog. */
evt_mstate_clicked(GtkWidget * wid,gpointer user)355 static void evt_mstate_clicked(GtkWidget *wid, gpointer user)
356 {
357 	P_Controls	*page = user;
358 	GtkTreeSelection *ts;
359 	GtkTreeIter	ti;
360 	CtrlMouse	*mouse;
361 	Dialog		*dlg;
362 	guint		i, x, y,
363 			mask[] = { GDK_SHIFT_MASK, GDK_CONTROL_MASK, GDK_MOD1_MASK, GDK_MOD2_MASK,
364 				   GDK_MOD3_MASK, GDK_MOD4_MASK, GDK_MOD5_MASK };
365 	GtkWidget	*grid, *label, *check[sizeof mask / sizeof mask[0]];
366 
367 	ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->mview));
368 	if(ts == NULL || !gtk_tree_selection_get_selected(ts, NULL, &ti))
369 		return;
370 	gtk_tree_model_get(GTK_TREE_MODEL(page->mstore), &ti, MOUSE_COLUMN_MOUSE, &mouse, -1);
371 
372 	grid = gtk_grid_new();
373 	label = gtk_label_new(_("The following modifier key(s) must\n"
374 				"be held down when the mouse button\n"
375 				"is clicked to trigger the command"));
376 	gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 2, 1);
377 	for(i = 0, x = 0, y = 1; i < sizeof mask / sizeof mask[0]; i++, y++)
378 	{
379 		check[i] = gtk_check_button_new_with_label(gtk_accelerator_name(0, mask[i]));
380 		if(ctrl_mouse_get_state(mouse) & mask[i])
381 			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check[i]), TRUE);
382 		gtk_grid_attach(GTK_GRID(grid), check[i], x, y, 1, 1);
383 		if(i == (sizeof mask / sizeof *mask) / 2)
384 			x++, y = 0;
385 	}
386 	dlg = dlg_dialog_sync_new(grid, _("Edit Modifiers"), NULL);
387 	if(dlg_dialog_sync_wait(dlg) == DLG_POSITIVE)
388 	{
389 		guint	ns = 0U;
390 
391 		for(i = 0; i < sizeof mask / sizeof mask[0]; i++)
392 		{
393 			if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check[i])))
394 				ns |= mask[i];
395 		}
396 		ctrl_mouse_set_state(mouse, ns);
397 		gtk_list_store_set(GTK_LIST_STORE(page->mstore), &ti, MOUSE_COLUMN_STATE, gtk_accelerator_name(0, ns), -1);
398 		page->modified = TRUE;
399 	}
400 	dlg_dialog_sync_destroy(dlg);
401 }
402 
403 /* 1999-06-13 -	User is editing the command name. Update mapping. */
evt_mcmd_changed(GtkWidget * wid,gpointer user)404 static void evt_mcmd_changed(GtkWidget *wid, gpointer user)
405 {
406 	P_Controls	*page = user;
407 	GtkTreeSelection *ts;
408 	GtkTreeIter	ti;
409 	CtrlMouse	*mouse;
410 	const gchar	*cmdseq;
411 
412 	ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->mview));
413 	if(ts == NULL || !gtk_tree_selection_get_selected(ts, NULL, &ti))
414 		return;
415 	gtk_tree_model_get(GTK_TREE_MODEL(page->mstore), &ti, MOUSE_COLUMN_MOUSE, &mouse, -1);
416 	if((cmdseq = gtk_entry_get_text(GTK_ENTRY(page->mdcmd))) != NULL)
417 	{
418 		ctrl_mouse_set_cmdseq(mouse, cmdseq);
419 		gtk_list_store_set(page->mstore, &ti, MOUSE_COLUMN_CMDSEQ, cmdseq, -1);
420 		page->modified = TRUE;
421 	}
422 }
423 
424 /* 1999-06-13 -	User clicked details button for mouse mapping command. Pop up dialog. */
evt_mcmdpick_clicked(GtkWidget * wid,gpointer user)425 static void evt_mcmdpick_clicked(GtkWidget *wid, gpointer user)
426 {
427 	P_Controls	*page = user;
428 	const gchar	*cmd;
429 
430 	if((cmd = csq_dialog_sync_new_wait(page->min, ccs_get_current())) != NULL)
431 		gtk_entry_set_text(GTK_ENTRY(page->mdcmd), cmd);
432 }
433 
434 /* 1999-06-20 -	User clicked the "Add" button for mouse bindings, so add one. */
evt_madd_clicked(GtkWidget * wid,gpointer user)435 static void evt_madd_clicked(GtkWidget *wid, gpointer user)
436 {
437 	P_Controls	*page = user;
438 	CtrlMouse	*mouse;
439 	GtkTreeIter	iter;
440 	GtkTreePath	*path;
441 
442 	mouse = ctrl_mouse_add(page->ctrlinfo, 1U, 0U, "");
443 	if(mouse != NULL)
444 	{
445 		list_store_append_mouse(page, mouse, &iter);
446 		gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(page->mview)), &iter);
447 		path = gtk_tree_model_get_path(GTK_TREE_MODEL(page->mstore), &iter);
448 		gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->mview), path, NULL, TRUE, 0.5f, 0.0f);
449 		gtk_tree_path_free(path);
450 		page->modified = TRUE;
451 	}
452 }
453 
454 /* 1999-06-21 -	The Delete-button has been clicked, remove current mouse command mapping. */
evt_mdel_clicked(GtkWidget * wid,gpointer user)455 static void evt_mdel_clicked(GtkWidget *wid, gpointer user)
456 {
457 	P_Controls	*page = user;
458 	GtkTreeModel	*model;
459 	GtkTreeIter	iter;
460 	CtrlMouse	*mouse;
461 
462 	if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(page->mview)), &model, &iter))
463 	{
464 		gtk_tree_model_get(model, &iter, MOUSE_COLUMN_MOUSE, &mouse, -1);
465 		ctrl_mouse_remove(page->ctrlinfo, mouse);
466 		gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
467 		page->modified = TRUE;
468 	}
469 }
470 
471 /* 2003-10-09 -	User set a new Click-M-Click command. */
evt_clickmclick_changed(GtkWidget * wid,gpointer user)472 static void evt_clickmclick_changed(GtkWidget *wid, gpointer user)
473 {
474 	P_Controls	*page = user;
475 
476 	if(page)
477 	{
478 		const gchar *text = gtk_entry_get_text(GTK_ENTRY(wid));
479 
480 		ctrl_clickmclick_set_cmdseq(page->ctrlinfo, text);
481 		gtk_widget_set_sensitive(page->cmcdelay, *text);
482 		page->modified = TRUE;
483 	}
484 }
485 
486 /* 2003-10-09 -	User wants help in picking a command for Click-M-Click. */
evt_clickmclick_pick_clicked(GtkWidget * wid,gpointer user)487 static void evt_clickmclick_pick_clicked(GtkWidget *wid, gpointer user)
488 {
489 	P_Controls	*page = user;
490 
491 	if(page)
492 	{
493 		const gchar	*cmd = csq_dialog_sync_new_wait(page->min, ccs_get_current());
494 		if(cmd)
495 			gtk_entry_set_text(GTK_ENTRY(page->cmccmd), cmd);
496 	}
497 }
498 
499 /* 2003-10-09 -	Maximum allowed delay for Click-M-Click recognition changed. */
evt_clickmclick_delay_changed(GtkAdjustment * adj,gpointer user)500 static void evt_clickmclick_delay_changed(GtkAdjustment *adj, gpointer user)
501 {
502 	P_Controls	*page = user;
503 
504 	if(page)
505 		ctrl_clickmclick_set_delay(page->ctrlinfo, gtk_adjustment_get_value(adj));
506 }
507 
508 /* ----------------------------------------------------------------------------------------- */
509 
510 /* 2000-02-18 -	User hit the "Ignore Num Lock?" toggle. Update editing state. */
evt_nonumlock_clicked(GtkWidget * wid,gpointer user)511 static void evt_nonumlock_clicked(GtkWidget *wid, gpointer user)
512 {
513 	P_Controls	*page = user;
514 
515 	if(page != NULL)
516 	{
517 		ctrl_numlock_ignore_set(page->ctrlinfo, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wid)));
518 		page->modified = TRUE;
519 	}
520 }
521 
522 /* ----------------------------------------------------------------------------------------- */
523 
build_mouse_menu(P_Controls * page)524 static void build_mouse_menu(P_Controls *page)
525 {
526 	gchar		**gfx[]  = { icon_mouse1_xpm, icon_mouse2_xpm, icon_mouse3_xpm, icon_mouse4_xpm, icon_mouse5_xpm },
527 			*label[] = { N_("Left"), N_("Middle"), N_("Right"), N_("Wheel Up"), N_("Wheel Down") };
528 	guint		i;
529 	GtkListStore	*mmodel;
530 	GtkCellRenderer	*cr;
531 
532 	for(i = 0; i < sizeof gfx / sizeof *gfx; i++)
533 		page->mpbuf[i] = gdk_pixbuf_new_from_xpm_data((const gchar **) gfx[i]);
534 
535 	/* Now build a full TreeModel for the GtkOptionMenu replacement. Pfew!
536 	** NOTE: Unlike key and mouse true models, we don't store the button index
537 	** in the model; instead we just use get/set active() on the GtkComboBox.
538 	 */
539 	mmodel = gtk_list_store_new(2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
540 	for(i = 0; i < sizeof gfx / sizeof *gfx; ++i)
541 	{
542 		GtkTreeIter	iter;
543 		gtk_list_store_insert_with_values(mmodel, &iter, -1, 0, page->mpbuf[i], 1, _(label[i]), -1);
544 	}
545 	page->mdcombo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(mmodel));
546 	cr = gtk_cell_renderer_pixbuf_new();
547 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(page->mdcombo), cr, FALSE);
548 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(page->mdcombo), cr,
549 	                                "pixbuf", 0,
550 	                                NULL);
551 
552 	cr = gtk_cell_renderer_text_new();
553 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(page->mdcombo), cr, TRUE);
554 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT(page->mdcombo), cr,
555 	                                "text", 1,
556 	                                NULL);
557 
558 	g_signal_connect(G_OBJECT(page->mdcombo), "changed", G_CALLBACK(evt_mbutton_changed), page);
559 	gtk_grid_attach(GTK_GRID(page->mdtable), page->mdcombo, 1, 0, 1, 1);
560 }
561 
cct_init(MainInfo * min,gchar ** name)562 static GtkWidget * cct_init(MainInfo *min, gchar **name)
563 {
564 	P_Controls	*page = &the_page;
565 	GtkWidget	*vbox, *frame, *label, *wid, *hbox, *scwin;
566 	GtkAdjustment	*adj;
567 	GtkCellRenderer	*cr;
568 	GtkTreeViewColumn *vc;
569 	GtkTreeSelection *ts;
570 
571 	if(name == NULL)
572 		return NULL;
573 	*name = _("Controls");
574 
575 	page->min = min;
576 	page->modified = FALSE;
577 
578 	page->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
579 
580 	page->kframe = gtk_frame_new(_("Global Keyboard Shortcuts"));
581 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
582 	page->kstore = gtk_list_store_new(KEY_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
583 	page->kview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(page->kstore));
584 	cr = gtk_cell_renderer_text_new();
585 	vc = gtk_tree_view_column_new_with_attributes("(Key)", cr, "text", 0, NULL);
586 	gtk_tree_view_append_column(GTK_TREE_VIEW(page->kview), vc);
587 	vc = gtk_tree_view_column_new_with_attributes("(Command)", cr, "text", 1, NULL);
588 	gtk_tree_view_append_column(GTK_TREE_VIEW(page->kview), vc);
589 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(page->kview), FALSE);
590 	ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->kview));
591 	gtk_tree_selection_set_mode(ts, GTK_SELECTION_SINGLE);
592 	g_signal_connect(G_OBJECT(ts), "changed", G_CALLBACK(evt_kselection_changed), page);
593 	scwin = gtk_scrolled_window_new(NULL, NULL);
594 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
595 	gtk_container_add(GTK_CONTAINER(scwin), page->kview);
596 	gtk_box_pack_start(GTK_BOX(vbox), scwin, TRUE, TRUE, 0);
597 
598 	page->kdtable = gtk_grid_new();
599 	label = gtk_label_new(_("Key"));
600 	gtk_grid_attach(GTK_GRID(page->kdtable), label, 0, 0, 1, 1);
601 	page->kdkey = gtk_entry_new();
602 	g_signal_connect(G_OBJECT(page->kdkey), "key_press_event", G_CALLBACK(evt_kkey_press), page);
603 	gtk_editable_set_editable(GTK_EDITABLE(page->kdkey), FALSE);
604 	gtk_grid_attach(GTK_GRID(page->kdtable), page->kdkey, 1, 0, 3, 1);
605 	label = gtk_label_new(_("Command"));
606 	gtk_grid_attach(GTK_GRID(page->kdtable), label, 0, 1, 1, 1);
607 	page->kdcmd = gtk_entry_new();
608 	gtk_widget_set_hexpand(page->kdcmd, TRUE);
609 	gtk_widget_set_halign(page->kdcmd, GTK_ALIGN_FILL);
610 	g_signal_connect(G_OBJECT(page->kdcmd), "changed", G_CALLBACK(evt_kcmd_changed), page);
611 	gtk_grid_attach(GTK_GRID(page->kdtable), page->kdcmd, 1, 1, 1, 1);
612 	page->kdcmdpick = gui_details_button_new();
613 	g_signal_connect(G_OBJECT(page->kdcmdpick), "clicked", G_CALLBACK(evt_kcmdpick_clicked), page);
614 	gtk_grid_attach(GTK_GRID(page->kdtable), page->kdcmdpick, 2, 1, 1, 1);
615 	gtk_box_pack_start(GTK_BOX(vbox), page->kdtable, FALSE, FALSE, 0);
616 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
617 	page->kadd = gtk_button_new_with_label(_("Add"));
618 	g_signal_connect(G_OBJECT(page->kadd), "clicked", G_CALLBACK(evt_kadd_clicked), page);
619 	gtk_box_pack_start(GTK_BOX(hbox), page->kadd, TRUE, TRUE, 5);
620 	page->kdel = gtk_button_new_with_label(_("Delete"));
621 	g_signal_connect(G_OBJECT(page->kdel), "clicked", G_CALLBACK(evt_kdel_clicked), page);
622 	gtk_box_pack_start(GTK_BOX(hbox), page->kdel, TRUE, TRUE, 5);
623 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
624 	gtk_container_add(GTK_CONTAINER(page->kframe), vbox);
625 	gtk_box_pack_start(GTK_BOX(page->vbox), page->kframe, TRUE, TRUE, 0);
626 
627 	page->mframe = gtk_frame_new(_("Dirpane Mouse Buttons"));
628 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
629 
630 	page->mstore = gtk_list_store_new(MOUSE_COLUMN_COUNT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
631 	page->mview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(page->mstore));
632 	cr = gtk_cell_renderer_pixbuf_new();
633 	vc = gtk_tree_view_column_new_with_attributes("(BIcon)", cr, "pixbuf", 0, NULL);
634 	gtk_tree_view_append_column(GTK_TREE_VIEW(page->mview), vc);
635 	cr = gtk_cell_renderer_text_new();
636 	vc = gtk_tree_view_column_new_with_attributes("(Button)", cr, "text", 1, NULL);
637 	gtk_tree_view_append_column(GTK_TREE_VIEW(page->mview), vc);
638 	vc = gtk_tree_view_column_new_with_attributes("(Modifier)", cr, "text", 2, NULL);
639 	gtk_tree_view_append_column(GTK_TREE_VIEW(page->mview), vc);
640 	vc = gtk_tree_view_column_new_with_attributes("(Command)", cr, "text", 3, NULL);
641 	gtk_tree_view_append_column(GTK_TREE_VIEW(page->mview), vc);
642 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(page->mview), FALSE);
643 	ts = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->mview));
644 	gtk_tree_selection_set_mode(ts, GTK_SELECTION_SINGLE);
645 	g_signal_connect(G_OBJECT(ts), "changed", G_CALLBACK(evt_mselection_changed), page);
646 	scwin = gtk_scrolled_window_new(NULL, NULL);
647 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
648 	gtk_container_add(GTK_CONTAINER(scwin), page->mview);
649 	gtk_box_pack_start(GTK_BOX(vbox), scwin, TRUE, TRUE, 0);
650 
651 	page->mdtable = gtk_grid_new();
652 	label = gtk_label_new(_("Button"));
653 	gtk_grid_attach(GTK_GRID(page->mdtable), label, 0, 0, 1, 1);
654 	build_mouse_menu(page);
655 	wid = gtk_button_new_with_label(_("Edit Modifiers..."));
656 	g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(evt_mstate_clicked), page);
657 	gtk_grid_attach(GTK_GRID(page->mdtable), wid, 2, 0, 2, 1);
658 	label = gtk_label_new(_("Command"));
659 	gtk_grid_attach(GTK_GRID(page->mdtable), label, 0, 1, 1, 1);
660 	page->mdcmd = gtk_entry_new();
661 	gtk_widget_set_hexpand(page->mdcmd, TRUE);
662 	gtk_widget_set_halign(page->mdcmd, GTK_ALIGN_FILL);
663 	g_signal_connect(G_OBJECT(page->mdcmd), "changed", G_CALLBACK(evt_mcmd_changed), page);
664 	gtk_grid_attach(GTK_GRID(page->mdtable), page->mdcmd, 1, 1, 2, 1);
665 	page->mdcmdpick = gui_details_button_new();
666 	g_signal_connect(G_OBJECT(page->mdcmdpick), "clicked", G_CALLBACK(evt_mcmdpick_clicked), page);
667 	gtk_grid_attach(GTK_GRID(page->mdtable), page->mdcmdpick, 3, 1, 1, 1);
668 	gtk_box_pack_start(GTK_BOX(vbox), page->mdtable, FALSE, FALSE, 0);
669 	page->mhbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
670 	page->madd = gtk_button_new_with_label(_("Add"));
671 	g_signal_connect(G_OBJECT(page->madd), "clicked", G_CALLBACK(evt_madd_clicked), page);
672 	gtk_box_pack_start(GTK_BOX(page->mhbox), page->madd, TRUE, TRUE, 5);
673 	page->mdel = gtk_button_new_with_label(_("Delete"));
674 	g_signal_connect(G_OBJECT(page->mdel), "clicked", G_CALLBACK(evt_mdel_clicked), page);
675 	gtk_box_pack_start(GTK_BOX(page->mhbox), page->mdel, TRUE, TRUE, 5);
676 	gtk_box_pack_start(GTK_BOX(vbox), page->mhbox, FALSE, FALSE, 5);
677 	gtk_container_add(GTK_CONTAINER(page->mframe), vbox);
678 	gtk_box_pack_start(GTK_BOX(page->vbox), page->mframe, TRUE, TRUE, 0);
679 
680 	frame = gtk_frame_new(_("Click-M-Click Gesture"));
681 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
682 	label = gtk_label_new(_("Command"));
683 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
684 	page->cmccmd = gtk_entry_new();
685 	g_signal_connect(G_OBJECT(page->cmccmd), "changed", G_CALLBACK(evt_clickmclick_changed), page);
686 	gtk_box_pack_start(GTK_BOX(hbox), page->cmccmd, TRUE, TRUE, 0);
687 	wid = gui_details_button_new();
688 	g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(evt_clickmclick_pick_clicked), page);
689 	gtk_box_pack_start(GTK_BOX(hbox), wid, FALSE, FALSE, 0);
690 	label = gtk_label_new(_("Time Limit"));
691 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
692 	adj = gtk_adjustment_new(0.400f, 0.100f, 1.5f, 0.1f, 0.1f, 0.0f);
693 	g_signal_connect(G_OBJECT(adj), "value_changed", G_CALLBACK(evt_clickmclick_delay_changed), page);
694 	page->cmcdelay = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT(adj));
695 	gtk_scale_set_value_pos(GTK_SCALE(page->cmcdelay), GTK_POS_RIGHT);
696 	gtk_scale_set_digits(GTK_SCALE(page->cmcdelay), 3);
697 	gtk_box_pack_start(GTK_BOX(hbox), page->cmcdelay, TRUE, TRUE, 0);
698 	gtk_container_add(GTK_CONTAINER(frame), hbox);
699 	gtk_box_pack_start(GTK_BOX(page->vbox), frame, FALSE, FALSE, 0);
700 
701 	page->nonumlock = gtk_check_button_new_with_label(_("Ignore Num Lock For All Bindings?"));
702 	g_signal_connect(G_OBJECT(page->nonumlock), "clicked", G_CALLBACK(evt_nonumlock_clicked), page);
703 	gtk_box_pack_start(GTK_BOX(page->vbox), page->nonumlock, FALSE, FALSE, 0);
704 
705 	gtk_widget_show_all(page->vbox);
706 	return page->vbox;
707 }
708 
709 /* ----------------------------------------------------------------------------------------- */
710 
711 /* 1999-03-16 -	Update the controls config page by grabbing current settings. */
cct_update(MainInfo * min)712 static void cct_update(MainInfo *min)
713 {
714 	the_page.ctrlinfo = ctrl_copy(min->cfg.ctrlinfo);
715 	populate_key_list(&the_page);
716 	populate_mouse_list(&the_page);
717 	reset_widgets(&the_page);
718 }
719 
720 /* ----------------------------------------------------------------------------------------- */
721 
722 /* 1999-03-17 -	The user just accepted the settings, so make them current. */
cct_accept(MainInfo * min)723 static void cct_accept(MainInfo *min)
724 {
725 	P_Controls	*page = &the_page;
726 
727 	if(page->modified)
728 	{
729 		if(ctrl_mouse_ambiguity_exists(page->ctrlinfo))
730 		{
731 			dlg_dialog_async_new_simple(_("Note: The mouse button control settings\n"
732 						    "are ambiguous: the same button+modifier\n"
733 						    "is used for more than one function. This\n"
734 						    "might make their behaviour pretty weird..."),
735 							_("Warning"), _("_OK"), NULL, NULL);
736 		}
737 		ctrl_destroy(min->cfg.ctrlinfo);
738 		min->cfg.ctrlinfo = page->ctrlinfo;
739 		page->ctrlinfo = NULL;
740 		cfg_set_flags(CFLG_RESET_KEYBOARD);
741 		page->modified = FALSE;
742 	}
743 }
744 
745 /* ----------------------------------------------------------------------------------------- */
746 
keys_save(CtrlInfo * ctrl,FILE * out)747 static void keys_save(CtrlInfo *ctrl, FILE *out)
748 {
749 	GSList	*keys, *iter;
750 
751 	xml_put_node_open(out, "Keys");
752 	if((keys = ctrl_keys_get_list(ctrl)) != NULL)
753 	{
754 		for(iter = keys; iter != NULL; iter = g_slist_next(iter))
755 		{
756 			const gchar	*key, *cmd;
757 
758 			if((key = ctrl_key_get_keyname(iter->data)) && (cmd = ctrl_key_get_cmdseq(iter->data)))
759 			{
760 				xml_put_node_open(out, "Key");
761 				xml_put_text(out, "keyname", key);
762 				xml_put_text(out, "cmdseq", cmd);
763 				xml_put_node_close(out, "Key");
764 			}
765 		}
766 		g_slist_free(keys);
767 	}
768 	xml_put_node_close(out, "Keys");
769 }
770 
771 /* 1999-06-20 -	Save out all mouse command bindings. */
mouse_save(CtrlInfo * ctrl,FILE * out)772 static void mouse_save(CtrlInfo *ctrl, FILE *out)
773 {
774 	GSList	*mouse, *iter;
775 
776 	xml_put_node_open(out, "MouseButtons");
777 	if((mouse = ctrl_mouse_get_list(ctrl)) != NULL)
778 	{
779 		for(iter = mouse; iter != NULL; iter = g_slist_next(iter))
780 		{
781 			xml_put_node_open(out, "MouseButton");
782 			xml_put_uinteger(out, "button", ctrl_mouse_get_button(iter->data));
783 			xml_put_uinteger(out, "state", ctrl_mouse_get_state(iter->data));
784 			xml_put_text(out, "cmdseq", ctrl_mouse_get_cmdseq(iter->data));
785 			xml_put_node_close(out, "MouseButton");
786 		}
787 		g_slist_free(mouse);
788 	}
789 	xml_put_node_close(out, "MouseButtons");
790 }
791 
clickmclick_save(const CtrlInfo * ctrl,FILE * out)792 static void clickmclick_save(const CtrlInfo *ctrl, FILE *out)
793 {
794 	xml_put_node_open(out, "ClickMClick");
795 	xml_put_text(out, "cmdseq", ctrl_clickmclick_get_cmdseq(ctrl));
796 	xml_put_real(out, "delay",  ctrl_clickmclick_get_delay(ctrl));
797 	xml_put_node_close(out, "ClickMClick");
798 }
799 
800 /* 2004-04-25 -	Save general bindings. */
general_save(const CtrlInfo * ctrl,FILE * out)801 static void general_save(const CtrlInfo *ctrl, FILE *out)
802 {
803 	GSList	*list;
804 
805 	if((list = ctrl_general_get_contexts(ctrl)) != NULL)	/* Only emit toplevel if there are bindings. */
806 	{
807 		const GSList	*iter;
808 
809 		xml_put_node_open(out, "Generals");
810 		for(iter = list; iter != NULL; iter = g_slist_next(iter))
811 		{
812 			xml_put_node_open(out, "General");
813 			xml_put_text(out, "context", (const gchar *) iter->data);
814 			xml_put_text(out, "cmdseq", ctrl_general_get_cmdseq(ctrl, (const gchar *) iter->data));
815 			xml_put_node_close(out, "General");
816 		}
817 		g_slist_free(list);
818 		xml_put_node_close(out, "Generals");
819 	}
820 }
821 
822 /* 1999-03-17 -	Save the current control settings right out in given <file>. */
cct_save(MainInfo * min,FILE * out)823 static gint cct_save(MainInfo *min, FILE *out)
824 {
825 	xml_put_node_open(out, "Controls");
826 	keys_save(min->cfg.ctrlinfo, out);
827 	mouse_save(min->cfg.ctrlinfo, out);
828 	clickmclick_save(min->cfg.ctrlinfo, out);
829 	general_save(min->cfg.ctrlinfo, out);
830 	xml_put_boolean(out, "ignore_numlock", ctrl_numlock_ignore_get(min->cfg.ctrlinfo));
831 	xml_put_node_close(out, "Controls");
832 
833 	return TRUE;
834 }
835 
836 /* ----------------------------------------------------------------------------------------- */
837 
838 /* 1999-03-17 -	Load in and install a single key mapping. */
key_load(const XmlNode * node,gpointer user)839 static void key_load(const XmlNode *node, gpointer user)
840 {
841 	const gchar	*name, *cmd;
842 
843 	if(xml_get_text(node, "keyname", &name) && xml_get_text(node, "cmdseq", &cmd))
844 		ctrl_key_add(((MainInfo *) user)->cfg.ctrlinfo, name, csq_cmdseq_map_name(cmd, "keyboard shortcut"));
845 }
846 
847 /* 1999-03-17 -	Load in all key mappings. */
keys_load(MainInfo * min,const XmlNode * node)848 static void keys_load(MainInfo *min, const XmlNode *node)
849 {
850 	xml_node_visit_children(node, key_load, min);
851 }
852 
853 /* 1999-06-20 -	Load and install a single mouse command mapping. */
mousebutton_load(const XmlNode * node,gpointer user)854 static void mousebutton_load(const XmlNode *node, gpointer user)
855 {
856 	const gchar	*cmd;
857 	guint		button, state;
858 
859 	if(xml_get_uinteger(node, "button", &button) && xml_get_uinteger(node, "state", &state) && xml_get_text(node, "cmdseq", &cmd))
860 		ctrl_mouse_add(((MainInfo *) user)->cfg.ctrlinfo, button, state, csq_cmdseq_map_name(cmd, "dirpane mouse button"));
861 }
862 
863 /* 1999-06-20 -	Load in the mouse command bindings. */
mousebuttons_load(MainInfo * min,const XmlNode * node)864 static void mousebuttons_load(MainInfo *min, const XmlNode *node)
865 {
866 	xml_node_visit_children(node, mousebutton_load, min);
867 }
868 
869 /* 2004-04-26 -	Load a general command binding. */
general_load(const XmlNode * node,gpointer user)870 static void general_load(const XmlNode *node, gpointer user)
871 {
872 	const gchar	*ctx, *cmd;
873 
874 	if(xml_get_text(node, "context", &ctx) && xml_get_text(node, "cmdseq", &cmd))
875 		ctrl_general_set_cmdseq(((MainInfo *) user)->cfg.ctrlinfo, ctx, cmd);
876 }
877 
878 /* 2004-04-26 -	Load a bunch of general command bindings. */
generals_load(MainInfo * min,const XmlNode * node)879 static void generals_load(MainInfo *min, const XmlNode *node)
880 {
881 	xml_node_visit_children(node, general_load, min);
882 }
883 
884 /* 1999-03-17 -	Load in the control config information. Replaces current. */
cct_load(MainInfo * min,const XmlNode * node)885 static void cct_load(MainInfo *min, const XmlNode *node)
886 {
887 	const XmlNode	*data;
888 
889 	if((node = xml_tree_search(node, "Controls")) != NULL)
890 	{
891 		gboolean	tmp;
892 
893 		if((data = xml_tree_search(node, "Keys")) != NULL)
894 		{
895 			ctrl_keys_uninstall_all(min->cfg.ctrlinfo);
896 			ctrl_key_remove_all(min->cfg.ctrlinfo);
897 			keys_load(min, data);
898 		}
899 		if((data = xml_tree_search(node, "MouseButtons")) != NULL)
900 		{
901 			ctrl_mouse_remove_all(min->cfg.ctrlinfo);
902 			mousebuttons_load(min, data);
903 		}
904 		if((data = xml_tree_search(node, "ClickMClick")) != NULL)
905 		{
906 			const gchar	*cmd;
907 			gfloat		delay;
908 			if(xml_get_text(data, "cmdseq", &cmd) && xml_get_real(data, "delay", &delay))
909 			{
910 				ctrl_clickmclick_set_cmdseq(min->cfg.ctrlinfo, cmd);
911 				ctrl_clickmclick_set_delay(min->cfg.ctrlinfo, delay);
912 			}
913 		}
914 		if((data = xml_tree_search(node, "Generals")) != NULL)
915 		{
916 			ctrl_general_clear(min->cfg.ctrlinfo);
917 			generals_load(min, data);
918 		}
919 		if(xml_get_boolean(node, "ignore_numlock", &tmp))
920 			ctrl_numlock_ignore_set(min->cfg.ctrlinfo, tmp);
921 	}
922 }
923 
924 /* ----------------------------------------------------------------------------------------- */
925 
cct_describe(MainInfo * min)926 const CfgModule * cct_describe(MainInfo *min)
927 {
928 	static const CfgModule	desc = { NODE, cct_init, cct_update, cct_accept, cct_save, cct_load, NULL };
929 
930 	return &desc;
931 }
932