]> icculus.org git repositories - taylor/freespace2.git/blob - src/controlconfig/controlsconfig.cpp
SDL2 port - stage 2
[taylor/freespace2.git] / src / controlconfig / controlsconfig.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module for keyboard, joystick and mouse configuration
16  *
17  * $Log$
18  * Revision 1.8  2006/04/26 19:37:47  taylor
19  * some text position fixes for FS1
20  *
21  * Revision 1.7  2005/03/29 02:18:47  taylor
22  * Various 64-bit platform fixes
23  * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
24  * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
25  * Streaming audio support (big thanks to Pierre Willenbrock!!)
26  * Removed dependance on strings.tbl for FS1 since we don't actually need it now
27  *
28  * Revision 1.6  2004/09/20 01:31:44  theoddone33
29  * GCC 3.4 fixes.
30  *
31  * Revision 1.5  2004/07/04 11:31:43  taylor
32  * amd64 support, compiler warning fixes, don't use software rendering
33  *
34  * Revision 1.4  2003/05/25 02:30:42  taylor
35  * Freespace 1 support
36  *
37  * Revision 1.3  2002/06/09 04:41:15  relnev
38  * added copyright header
39  *
40  * Revision 1.2  2002/05/07 03:16:43  theoddone33
41  * The Great Newline Fix
42  *
43  * Revision 1.1.1.1  2002/05/03 03:28:08  root
44  * Initial import.
45  *
46  * 
47  * 13    10/14/99 2:50p Jefff
48  * localization fixes
49  * 
50  * 12    8/16/99 9:50a Jefff
51  * fixed loading of tab bitmaps
52  * 
53  * 11    8/11/99 3:21p Jefff
54  * added tab highlights on conflict
55  * 
56  * 10    7/26/99 5:25p Jefff
57  * removed invalidation of key binding for demo builds
58  * 
59  * 9     7/19/99 2:13p Dave
60  * Added some new strings for Heiko.
61  * 
62  * 8     7/15/99 9:20a Andsager
63  * FS2_DEMO initial checkin
64  * 
65  * 7     6/19/99 2:46p Dave
66  * New control config screen.
67  * 
68  * 6     1/30/99 5:08p Dave
69  * More new hi-res stuff.Support for nice D3D textures.
70  * 
71  * 5     1/15/99 11:29a Neilk
72  * Fixed D3D screen/texture pixel formatting problem. 
73  * 
74  * 4     11/05/98 4:18p Dave
75  * First run nebula support. Beefed up localization a bit. Removed all
76  * conditional compiles for foreign versions. Modified mission file
77  * format.
78  * 
79  * 3     10/13/98 9:28a Dave
80  * Started neatening up freespace.h. Many variables renamed and
81  * reorganized. Added AlphaColors.[h,cpp]
82  * 
83  * 2     10/07/98 10:52a Dave
84  * Initial checkin.
85  * 
86  * 1     10/07/98 10:48a Dave
87  * 
88  * 61    8/09/98 11:55a Lawrance
89  * if GRAVIS_OEM is defined, map the throttle axis by default
90  * 
91  * 60    6/19/98 3:51p Lawrance
92  * localize control text
93  * 
94  * 59    6/17/98 11:04a Lawrance
95  * localize the control config strings
96  * 
97  * 58    6/13/98 5:19p Hoffoss
98  * externalized control config texts.
99  * 
100  * 57    6/09/98 5:15p Lawrance
101  * French/German localization
102  * 
103  * 56    6/09/98 10:31a Hoffoss
104  * Created index numbers for all xstr() references.  Any new xstr() stuff
105  * added from here on out should be added to the end if the list.  The
106  * current list count can be found in FreeSpace.cpp (search for
107  * XSTR_SIZE).
108  * 
109  * 55    6/01/98 11:43a John
110  * JAS & MK:  Classified all strings for localization.
111  * 
112  * 54    5/26/98 11:10a Lawrance
113  * Fix bug where window controls get disabled when F1 pressed twice
114  * 
115  * 53    5/20/98 10:35p Hoffoss
116  * Fixed bug with mouse buttons not working when action isn't continuous.
117  * 
118  * 52    5/19/98 4:08p Allender
119  * kill default binding for Z axis
120  * 
121  * 51    5/19/98 12:56p Hoffoss
122  * Added some code to help prevent triple-clicking for binding the mouse
123  * button to an action (why the hell people triple click is beyond me,
124  * though).
125  * 
126  * 50    5/19/98 11:11a Lawrance
127  * Ensure X and Y axis have defaults!
128  * 
129  * 49    5/18/98 4:53p Hoffoss
130  * Some force feedback tweaks and pilot initializations there should have
131  * been happening, but weren't, and not are!
132  * 
133  * 48    5/18/98 10:15a Lawrance
134  * Only do hud squad msg key check when necessary
135  * 
136  * 47    5/18/98 10:08a Lawrance
137  * deal with overlap between hud squad msg number keys and CC_CONTINUOUS
138  * keys
139  * 
140  * 46    5/17/98 5:44p Hoffoss
141  * Made throttle never bound by default (ask Sandeep why if interested).
142  * 
143  * 45    5/14/98 5:32p Hoffoss
144  * Improved axis binding code some more.
145  * 
146  * 44    5/13/98 7:15p Hoffoss
147  * Fixed remaining bugs with axis binding.
148  * 
149  * 43    5/13/98 1:17a Hoffoss
150  * Added joystick axes configurability.
151  * 
152  * 42    5/12/98 3:49p Hoffoss
153  * Fixed bug where double mouse click would bind mouse button right away.
154  * 
155  * 41    5/11/98 5:43p Hoffoss
156  * Made num lock not bindable.
157  * 
158  * 40    5/11/98 5:29p Hoffoss
159  * Added mouse button mapped to joystick button support.
160  * 
161  * 39    5/07/98 6:25p Dave
162  * Fix strange boundary conditions which arise when players die/respawn
163  * while the game is being ended. Spiff up the chatbox doskey thing a bit.
164  * 
165  * 38    5/05/98 1:48a Lawrance
166  * Add in missing help overlays
167  * 
168  * 37    4/27/98 10:11a Lawrance
169  * Add in disabled beep for missing buttons
170  * 
171  * 36    4/25/98 2:59p Hoffoss
172  * Fixed typo that was causing a bug.
173  * 
174  * 35    4/22/98 1:51a Lawrance
175  * Take out multiplayer key from demo key config
176  * 
177  * 34    4/16/98 4:29p Hoffoss
178  * Fixed bank_when_pressed functionality when using alt or shift for it.
179  * 
180  * 33    4/15/98 11:06a Lawrance
181  * fix bug with a multi key showing up in demo, remove obsolete bindings
182  * from demo and full version
183  * 
184  * 32    4/14/98 2:45p Hoffoss
185  * Made hitting escape to exit screen not play failed sound.
186  * 
187  * 31    4/14/98 2:27p Hoffoss
188  * Made certain actions be hidden in demo build.
189  * 
190  * 30    4/13/98 2:38p Hoffoss
191  * Added a tooltip handler and make binding attempts with illegal keys
192  * show a popup.
193  * 
194  * 29    4/11/98 7:59p Lawrance
195  * Add support for help overlays
196  * 
197  * 28    4/09/98 4:12p Hoffoss
198  * Changed check_control() to automatically register a control as used if
199  * it detects it being used.
200  * 
201  * 27    4/08/98 11:11a Hoffoss
202  * Fixed some bugs that showed up due to fixing other bugs the other day
203  * with controls.
204  * 
205  * 26    4/07/98 3:47p Hoffoss
206  * Fixed continuous controls checking with respect to modifiers.
207  * 
208  * 25    4/06/98 11:17a Hoffoss
209  * Fixed num lock/pause interplay bug.
210  * 
211  * 24    4/03/98 3:51p Hoffoss
212  * Fixed some bugs, and made changed Interplay requested regarding search
213  * mode.
214  * 
215  * 23    3/31/98 4:12p Hoffoss
216  * Made control used status clear at mission init time.
217  * 
218  * 22    3/23/98 11:28a Hoffoss
219  * Fixed flashing question mark bug.
220  * 
221  * 21    3/21/98 11:30a John
222  * Fixed bug where joymouse caused you to stay in binding mode when
223  * binding joystick button 1 to something.
224  * 
225  * 20    3/20/98 3:37p Hoffoss
226  * Tried to fix mitri's bug, failed miserably.
227  * 
228  * 19    3/19/98 5:04p Dave
229  * Put in support for targeted multiplayer text and voice messaging (all,
230  * friendly, hostile, individual).
231  * 
232  * 18    3/18/98 12:03p John
233  * Marked all the new strings as externalized or not.
234  * 
235  * 17    3/18/98 10:16a Hoffoss
236  * Fixed warning.
237  * 
238  * 16    3/17/98 11:15a Hoffoss
239  * Made question mark that appears when you are in bind mode flash.
240  * 
241  * 15    3/17/98 10:48a Hoffoss
242  * Allowed a special hack for "bank while pressed" action to use alt and
243  * shift keys standalone.
244  * 
245  * 14    3/12/98 3:22p Hoffoss
246  * Fixed 2 bugs with one solution!  Yay!  Failed sound on bind fixed and
247  * pad enter now not translated to enter.
248  * 
249  * 13    3/11/98 5:28p Hoffoss
250  * Added control config debug display info to possibly aid in tracking
251  * down a bug.
252  * 
253  * 12    2/26/98 10:07p Hoffoss
254  * Rewrote state saving and restoring to fix bugs and simplify the code.
255  * 
256  * 11    2/22/98 12:19p John
257  * Externalized some strings
258  * 
259  * 10    2/20/98 3:39p Hoffoss
260  * Updated code for new control config screen artwork.
261  * 
262  * 9     2/09/98 2:50p Hoffoss
263  * Made 'none' show up as gray instead of normal color, to distinguish it
264  * from actions with bound keys.
265  * 
266  * 8     2/07/98 10:04p Hoffoss
267  * Changed color and placement of "more" indicator.
268  * 
269  * 7     2/05/98 10:42a Hoffoss
270  * Fixed bug where while in bind mode, you could change the line selected
271  * using the mouse, and binding would work on the new line instead.
272  * 
273  * 6     2/03/98 5:05p Hoffoss
274  * Added "clear other" button to clear all conflicting bindings with
275  * selected action.
276  * 
277  * 5     1/22/98 4:53p Hoffoss
278  * Made training messages/directives display a joystick button in place of
279  * a keypress if there is no keypress bound to the action.
280  * 
281  * 4     1/20/98 4:20p Hoffoss
282  * Removed confusing behavior of clear button clearing out the other
283  * binding in a conflict.
284  * 
285  * 3     1/08/98 12:11p Hoffoss
286  * Changed Rudder axis to Roll axis, added new function we can use to
287  * check what joystick axes are valid with.
288  * 
289  * 2     12/24/97 3:37p Hoffoss
290  * Moved control config stuff to seperate library to Fred can access it as
291  * well.
292  * 
293  * 1     12/24/97 11:58a Hoffoss
294  * 
295  * 98    12/22/97 2:15p Hoffoss
296  * Fixed bug where joystick axis lines weren't being displayed.
297  * 
298  * 97    12/16/97 2:44p Hoffoss
299  * Added clear button to control config screen.
300  * 
301  * 96    12/12/97 3:07p Hoffoss
302  * Changed how deleting bindings work.  Each control of an action can be
303  * deleted independently or both at once.
304  * 
305  * 95    12/07/97 2:36p John
306  * Made warp out be Alt+J instead of J
307  * 
308  * 94    12/03/97 4:59p Hoffoss
309  * Added reset sound and change control config sounds around.
310  * 
311  * 93    12/03/97 4:16p Hoffoss
312  * Changed sound stuff used in interface screens for interface purposes.
313  *
314  * $NoKeywords: $
315  *
316 */
317
318 #include "freespace.h"
319 #include "controlsconfig.h"
320 #include "gamesequence.h"
321 #include "player.h"
322 #include "2d.h"
323 #include "hudsquadmsg.h"
324 #include "key.h"
325 #include "timer.h"
326 #include "math.h"
327 #include "mouse.h"
328 #include "ui.h"
329 #include "joy.h"
330 #include "bmpman.h"
331 #include "sound.h"
332 #include "gamesnd.h"
333 #include "missionscreencommon.h"
334 #include "font.h"
335 #include "hud.h"
336 #include "managepilot.h"
337 #include "multi_pmsg.h"
338 #include "contexthelp.h"
339 #include "popup.h"
340 #include "uidefs.h"
341 #include "multiutil.h"
342 #include "alphacolors.h"
343
344 #define NUM_SYSTEM_KEYS                 14
345 #define NUM_BUTTONS                             19
346 #define NUM_TABS                                        4
347
348 // coordinate indicies
349 #define CONTROL_X_COORD 0
350 #define CONTROL_Y_COORD 1
351 #define CONTROL_W_COORD 2
352 #define CONTROL_H_COORD 3
353
354 const char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = {
355         "ControlConfig",                // GR_640
356         "2_ControlConfig"               // GR_1024
357 };
358
359 const char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = {
360         "ControlConfig-m",              // GR_640
361         "2_ControlConfig-m"             // GR_1024
362 };
363
364 // control list area
365 int Control_list_coords[GR_NUM_RESOLUTIONS][4] = {
366         {
367 #ifdef MAKE_FS1
368                 34, 85, 198, 229
369 #else
370                 32, 58, 198, 259                        // GR_640
371 #endif
372         },
373         {
374                 32, 94, 904, 424                        // GR_1024
375         }
376 };
377
378 // width of the control name section of the list
379 int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = {
380         350,            // GR_640
381         600             // GR_1024
382 };
383
384 // x start position of the binding area section of the list
385 int Control_list_key_x[GR_NUM_RESOLUTIONS] = {
386         397,            // GR_640
387         712             // GR_1024
388 };
389
390 // width of the binding area section of the list
391 int Control_list_key_w[GR_NUM_RESOLUTIONS] = {
392         198,            // GR_640
393         230             // GR_1024
394 };
395
396 // display the "more..." text under the control list
397 int Control_more_coords[GR_NUM_RESOLUTIONS][2] = {
398         {
399 #ifdef MAKE_FS1
400                 320, 326
401 #else
402                 320, 326                        // GR_640
403 #endif
404         },
405         {
406                 500, 542                        // GR_1024
407         }
408 };
409
410 // area to display "conflicts with..." text
411 int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
412         {
413 #ifdef MAKE_FS1
414                 34, 364, 175, 19
415 #else
416                 32, 313, 250, 32        // GR_640
417 #endif
418         },
419         {
420                 48, 508, 354, 46        // GR_1024
421         }
422 };
423
424 // conflict warning anim coords
425 int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = {
426         {
427 #ifdef MAKE_FS1
428                 274, 352
429 #else
430                 -1, 420                 // GR_640
431 #endif
432         },
433         {
434                 -1, 669                 // GR_1024
435         }
436 };
437
438 // for flashing the conflict text
439 #define CONFLICT_FLASH_TIME     250
440 int Conflict_stamp = -1;
441 int Conflict_bright = 0;
442
443 #define LIST_BUTTONS_MAX        40
444 #define JOY_AXIS                                0x80000
445
446 static int Num_cc_lines;
447 static struct {
448         const char *label;
449         int cc_index;  // index into Control_config of item
450         int y;  // Y coordinate of line
451         int kx, kw, jx, jw;  // x start and width of keyboard and joystick bound text
452 } Cc_lines[CCFG_MAX];
453
454 // struct to hold backup config_item elements so we can undo them
455 struct config_item_undo {
456         int size;
457         int *index;  // array (size) of Control_config indices of replaced elements
458         config_item *list;  // array (size) of original elements
459         config_item_undo *next;
460 };
461
462 config_item Control_config_backup[CCFG_MAX];
463
464 #ifdef GRAVIS_OEM
465 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
466 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 };
467 #else
468 int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
469 int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 };
470 #endif
471
472 // all this stuff is localized/externalized
473 #define NUM_AXIS_TEXT                   6
474 #define NUM_MOUSE_TEXT                  5
475 #define NUM_MOUSE_AXIS_TEXT     2
476 #define NUM_INVERT_TEXT                 2       
477 char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS];
478 char *Joy_axis_text[NUM_AXIS_TEXT];
479 char *Mouse_button_text[NUM_MOUSE_TEXT];
480 char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT];
481 char *Invert_text[NUM_INVERT_TEXT];
482
483 //ubyte System_keys[NUM_SYSTEM_KEYS] = {
484 //      SDLK_ESCAPE, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_F9, SDLK_F10,
485 //      SDLK_F11, SDLK_F12, SDLK_PRINTSCREEN
486 //};
487
488 int Control_check_count = 0;
489
490 static int Tab;  // which tab we are currently in
491 static int Binding_mode = 0;  // are we waiting for a key to bind it?
492 static int Bind_time = 0;
493 static int Search_mode = 0;  // are we waiting for a key to bind it?
494 static int Last_key = -1;
495 static int Selected_line = 0;  // line that is currently selected for binding
496 static int Selected_item = -1;  // -1 = none, 0 = key, 1 = button
497 static int Scroll_offset;
498 static int Axis_override = -1;
499 static int Background_bitmap;
500 static int Conflicts_tabs[NUM_TABS];
501 static UI_BUTTON List_buttons[LIST_BUTTONS_MAX];  // buttons for each line of text in list
502 static UI_WINDOW Ui_window;
503
504 static struct {
505         int key;  // index of other control in conflict with this one
506         int joy;  // index of other control in conflict with this one
507 } Conflicts[CCFG_MAX];
508
509 int Conflicts_axes[NUM_JOY_AXIS_ACTIONS];
510
511 #ifdef MAKE_FS1
512 static hud_anim Conflict_warning_anim;
513 #endif
514
515 #define TARGET_TAB                              0
516 #define SHIP_TAB                                        1
517 #define WEAPON_TAB                              2
518 #define COMPUTER_TAB                            3
519 #define SCROLL_UP_BUTTON                4
520 #define SCROLL_DOWN_BUTTON              5
521 #define ALT_TOGGLE                              6
522 #define SHIFT_TOGGLE                            7
523 #define INVERT_AXIS                             8
524 #define CANCEL_BUTTON                   9
525 #define UNDO_BUTTON                             10
526 #define RESET_BUTTON                            11
527 #define SEARCH_MODE                             12
528 #define BIND_BUTTON                             13
529 #define HELP_BUTTON                             14
530 #define ACCEPT_BUTTON                   15
531 #define CLEAR_OTHER_BUTTON              16
532 #define CLEAR_ALL_BUTTON                17
533 #define CLEAR_BUTTON                            18
534
535 ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
536         { // GR_640
537 #ifdef MAKE_FS1
538                 ui_button_info("CCB_00",        35,             30,             -1,     -1,     0),             // target tab
539                 ui_button_info("CCB_01",        121,    30,             -1,     -1,     1),             // ship tab
540                 ui_button_info("CCB_02",        176,    30,             -1,     -1,     2),             // weapon tab
541                 ui_button_info("CCB_03",        254,    30,             -1,     -1,     3),             // computer/misc tab
542                 ui_button_info("CCB_04",        598,    168,    -1,     -1, 4),         // scroll up
543                 ui_button_info("CCB_05",        598,    219,    -1,     -1, 5),         // scroll down
544                 ui_button_info("CCB_06",        13,     394,    -1,     -1,     6),             // alt toggle
545                 ui_button_info("CCB_07",        58,     394,    -1,     -1,     7),             // shift toggle
546                 ui_button_info("CCB_09",        168,    394,    -1,     -1,     9),             // invert
547                 ui_button_info("CCB_10",        456,    341,    -1,     -1, 10),        // cancel
548                 ui_button_info("CCB_11",        394,    404,    -1,     -1,     11),    // undo
549                 ui_button_info("CCB_12",        501,    26,             -1,     -1, 12),        // default
550                 ui_button_info("CCB_13",        513,    331,    -1,     -1, 13),        // search
551                 ui_button_info("CCB_14",        572,    331,    -1,     -1, 14),        // bind
552                 ui_button_info("CCB_15",        469,    429,    -1,     -1,     15),    // help
553                 ui_button_info("CCB_16",        562,    411,    -1,     -1,     16),    // accept
554                 ui_button_info("CCB_18",        223,    404,    -1,     -1,     18),    // clear other
555                 ui_button_info("CCB_19",        289,    404,    -1,     -1,     19),    // clear all
556                 ui_button_info("CCB_20",        333,    404,    -1,     -1,     20),    // clear button
557 #else
558                 ui_button_info("CCB_00",        32,     348,    17,     384,    0),     // target tab
559                 ui_button_info("CCB_01",        101,    348,    103,    384,    1),     // ship tab
560                 ui_button_info("CCB_02",        173,    352,    154,    384,    2),     // weapon tab
561                 ui_button_info("CCB_03",        242,    347,    244,    384,    3),     // computer/misc tab
562                 ui_button_info("CCB_04",        614,    73,     -1,     -1,     4),     // scroll up
563                 ui_button_info("CCB_05",        614,    296,    -1,     -1,     5),     // scroll down
564                 ui_button_info("CCB_06",        17,     452,    12,     440,    6),     // alt toggle
565                 ui_button_info("CCB_07",        56,     452,    50,     440,    7),     // shift toggle
566                 ui_button_info("CCB_09",        162,    452,    155,    440,    9),     // invert
567                 ui_button_info("CCB_10",        404,    1,              397,    45,     10),    // cancel
568                 ui_button_info("CCB_11",        582,    347,    586,    386,    11),    // undo
569                 ui_button_info("CCB_12",        576,    1,              578,    45,     12),    // default
570                 ui_button_info("CCB_13",        457,    4,              453,    45,     13),    // search
571                 ui_button_info("CCB_14",        516,    4,              519,    45,     14),    // bind
572                 ui_button_info("CCB_15",        540,    428,    500,    440,    15),    // help
573                 ui_button_info("CCB_16",        574,    432,    571,    412,    16),    // accept
574                 ui_button_info("CCB_18",        420,    346,    417,    386,    18),    // clear other 
575                 ui_button_info("CCB_19",        476,    346,    474,    386,    19),    // clear all
576                 ui_button_info("CCB_20",        524,    346,    529,    386,    20),    // clear button
577 #endif
578         },
579         { // GR_1024
580                 ui_button_info("2_CCB_00",      51,     557,    27,     615,    0),     // target tab
581                 ui_button_info("2_CCB_01",      162,    557,    166,    615,    1),     // ship tab
582                 ui_button_info("2_CCB_02",      277,    563,    246,    615,    2),     // weapon tab
583                 ui_button_info("2_CCB_03",      388,    555,    391,    615,    3),     // computer/misc tab
584                 ui_button_info("2_CCB_04",      982,    117,    -1,     -1,     4),     // scroll up
585                 ui_button_info("2_CCB_05",      982,    474,    -1,     -1,     5),     // scroll down
586                 ui_button_info("2_CCB_06",      28,     723,    24,     704,    6),     // alt toggle
587                 ui_button_info("2_CCB_07",      89,     723,    80,     704,    7),     // shift toggle
588                 ui_button_info("2_CCB_09",      260,    723,    249,    704,    9),     // invert
589                 ui_button_info("2_CCB_10",      646,    2,              635,    71,     10),    // cancel
590                 ui_button_info("2_CCB_11",      932,    555,    938,    619,    11),    // undo
591                 ui_button_info("2_CCB_12",      921,    1,              923,    71,     12),    // default
592                 ui_button_info("2_CCB_13",      732,    6,              726,    71,     13),    // search
593                 ui_button_info("2_CCB_14",      825,    6,              831,    71,     14),    // bind
594                 ui_button_info("2_CCB_15",      864,    685,    800,    704,    15),    // help
595                 ui_button_info("2_CCB_16",      919,    692,    914,    660,    16),    // accept
596                 ui_button_info("2_CCB_18",      672,    553,    668,    619,    18),    // clear other 
597                 ui_button_info("2_CCB_19",      761,    553,    749,    619,    19),    // clear all
598                 ui_button_info("2_CCB_20",      838,    553,    846,    619,    20),    // clear button
599         }
600 };
601
602 // strings
603 #ifndef MAKE_FS1
604 #define CC_NUM_TEXT             20
605
606 UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = {
607         // nothing needed for FS1
608         { // GR_640
609                 { "Targeting",          1340,           17,     384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button },
610                 { "Ship",                       1341,           103,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button },
611                 { "Weapons",            1065,           154,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button },
612                 { "Misc",                       1411,           244,    384,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button },         
613                 { "Alt",                                1510,           12,     440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button },
614                 { "Shift",                      1511,           50,     440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button },
615                 { "Invert",                     1342,           155,    440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button },
616                 { "Cancel",                     641,            397,    45,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button },
617                 { "Undo",                       1343,           586,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button },
618                 { "Defaults",           1344,           568,    45,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button },
619                 { "Search",                     1345,           453,    45,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button },
620                 { "Bind",                       1346,           519,    45,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button },
621                 { "Help",                       928,            500,    440,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button },
622                 { "Accept",                     1035,           571,    412,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button },
623                 { "Clear",                      1347,           417,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
624                 { "Conflict",           1348,           406,    396,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button },
625                 { "Clear",                      1413,           474,    386,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
626                 { "All",                                1349,           483,    396,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button },
627                 { "Clear",                      1414,           529,    388,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
628                 { "Selected",           1350,           517,    396,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button },
629         },
630         { // GR_1024
631                 { "Targeting",          1340,           47,     615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button },
632                 { "Ship",                       1341,           176,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button },
633                 { "Weapons",            1065,           266,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button },
634                 { "Misc",                       1411,           401,    615,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button },         
635                 { "Alt",                                1510,           29,     704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button },
636                 { "Shift",                      1511,           85,     704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button },
637                 { "Invert",                     1342,           254,    704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button },
638                 { "Cancel",                     641,            655,    71,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button },
639                 { "Undo",                       1343,           938,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button },
640                 { "Defaults",           1344,           923,    71,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button },
641                 { "Search",                     1345,           746,    71,     UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button },
642                 { "Bind",                       1346,           846,    71,     UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button },
643                 { "Help",                       928,            800,    704,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button },
644                 { "Accept",                     1035,           914,    660,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button },
645                 { "Clear",                      1347,           683,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
646                 { "Conflict",           1348,           666,    634,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button },
647                 { "Clear",                      1413,           759,    619,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
648                 { "All",                                1349,           772,    634,    UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button },
649                 { "Clear",                      1414,           871,    619,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
650                 { "Selected",           1350,           852,    634,    UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button },
651         }
652 };
653 #endif
654
655 // linked list head of undo items
656 config_item_undo *Config_item_undo;
657
658 // same indices as Scan_code_text[].  Indicates if a scancode is allowed to be bound.
659 int Config_allowed[] = {
660         0, 0, 1, 1, 1, 1, 1, 1,
661         1, 1, 1, 1, 1, 1, 1, 1,
662         1, 1, 1, 1, 1, 1, 1, 1,
663         1, 1, 1, 1, 1, 1, 1, 1,
664
665         1, 1, 1, 1, 1, 1, 1, 1,
666         1, 0, 1, 1, 1, 1, 1, 1,
667         1, 1, 1, 1, 1, 1, 1, 1,
668         1, 1, 1, 0, 0, 0, 0, 0,
669
670         0, 0, 0, 0, 0, 0, 1, 1,
671         1, 1, 1, 1, 1, 1, 1, 1,
672         1, 1, 1, 1, 0, 0, 0, 0,
673         0, 0, 0, 0, 0, 0, 0, 0,
674
675         0, 0, 0, 0, 0, 0, 0, 0,
676         0, 0, 0, 0, 0, 0, 0, 0,
677         0, 0, 0, 0, 0, 0, 0, 0,
678         0, 0, 0, 0, 0, 0, 0, 0,
679
680         0, 0, 0, 0, 0, 0, 0, 0,
681         0, 0, 0, 0, 0, 0, 0, 0,
682         0, 0, 0, 0, 0, 0, 0, 0,
683         0, 0, 0, 0, 1, 1, 0, 0,
684
685         0, 0, 0, 0, 0, 0, 0, 0,
686         0, 0, 0, 0, 0, 0, 0, 0,
687         0, 0, 0, 0, 0, 1, 0, 0,
688         1, 0, 0, 0, 0, 0, 0, 0,
689
690         0, 0, 0, 0, 0, 0, 0, 1,
691         1, 1, 0, 1, 0, 1, 0, 1,
692         1, 1, 1, 1, 0, 0, 0, 0,
693         0, 0, 0, 0, 0, 0, 0, 0,
694
695         0, 0, 0, 0, 0, 0, 0, 0,
696         0, 0, 0, 0, 0, 0, 0, 0,
697         0, 0, 0, 0, 0, 0, 0, 0,
698         0, 0, 0, 0, 0, 0, 0, 0,
699 };
700
701
702 #ifdef FS1_DEMO
703 // old invalid demo keys
704 #define INVALID_DEMO_KEYS_MAX   14
705 int Invalid_demo_keys[] = {
706         INCREASE_SHIELD,
707         DECREASE_SHIELD,
708         SHIELD_EQUALIZE,
709         SHIELD_XFER_TOP,
710         SHIELD_XFER_BOTTOM,
711         SHIELD_XFER_LEFT,
712         SHIELD_XFER_RIGHT,
713         XFER_SHIELD,
714         XFER_LASER,
715         MULTI_MESSAGE_ALL,
716         MULTI_MESSAGE_FRIENDLY,
717         MULTI_MESSAGE_HOSTILE,
718         MULTI_MESSAGE_TARGET,
719         MULTI_OBSERVER_ZOOM_TO
720 };
721 #else
722 #define INVALID_DEMO_KEYS_MAX   0
723 int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1];         // +1 is only to prevent a 0-size array;
724 #endif
725
726 #ifndef NDEBUG
727 int Show_controls_info = 0;
728
729 DCF_BOOL(show_controls_info, Show_controls_info)
730 #endif
731
732 static int Axes_origin[JOY_NUM_AXES];
733
734 void control_config_detect_axis_reset()
735 {
736         joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin);
737 }
738
739 int control_config_detect_axis()
740 {
741         int i, d, axis = -1, delta = 16384;
742         int axes_values[JOY_NUM_AXES];
743
744         joystick_read_raw_axis(JOY_NUM_AXES, axes_values);
745         for (i=0; i<JOY_NUM_AXES; i++) {
746                 d = abs(axes_values[i] - Axes_origin[i]);
747                 if (d > delta) {
748                         axis = i;
749                         delta = d;
750                 }
751         }
752
753         return axis;
754 }
755
756 int control_config_valid_action(int n)
757 {
758 #if defined(FS2_DEMO) || defined(FS1_DEMO)
759         int i;
760
761         for (i=0; i<INVALID_DEMO_KEYS_MAX; i++)
762                 if (n == Invalid_demo_keys[i])
763                         return 0;
764 #endif
765
766         return 1;
767 }
768
769 void control_config_conflict_check()
770 {
771         int i, j, a, b, c, shift = -1, alt = -1;
772
773         for (i=0; i<CCFG_MAX; i++) {
774                 Conflicts[i].key = Conflicts[i].joy = -1;
775                 switch (Control_config[i].key_id) {
776                         case SDLK_LSHIFT:
777                         case SDLK_RSHIFT:
778                                 shift = i;
779                                 break;
780
781                         case SDLK_LALT:
782                         case SDLK_RALT:
783                                 alt = i;
784                                 break;
785                 }
786         }
787
788         for (i=0; i<NUM_TABS; i++)
789                 Conflicts_tabs[i] = 0;
790
791         for (i=0; i<CCFG_MAX-1; i++) {
792                 if (control_config_valid_action(i)) {
793                         for (j=i+1; j<CCFG_MAX; j++) {
794                                 if (control_config_valid_action(j)) {
795                                         if (Control_config[i].key_id >= 0) {
796                                                 c = 0;
797                                                 a = Control_config[i].key_id;
798                                                 b = Control_config[j].key_id;
799                                                 if (a == b) {
800                                                         Conflicts[i].key = j;
801                                                         Conflicts[j].key = i;
802                                                         Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
803                                                         Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
804                                                 }
805
806                 /*                              if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
807                                                         Conflicts[i].key = shift;
808                                                         Conflicts[shift].key = i;
809                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
810                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
811                                                 }
812
813                                                 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
814                                                         Conflicts[j].key = shift;
815                                                         Conflicts[shift].key = j;
816                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
817                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
818                                                 }
819
820                                                 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
821                                                         Conflicts[i].key = alt;
822                                                         Conflicts[alt].key = i;
823                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
824                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
825                                                 }
826
827                                                 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
828                                                         Conflicts[j].key = alt;
829                                                         Conflicts[alt].key = j;
830                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
831                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
832                                                 }*/
833                                         }
834
835                                         if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
836                                                 Conflicts[i].joy = j;
837                                                 Conflicts[j].joy = i;
838                                                 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
839                                                 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
840                                         }
841                                 }
842                         }
843                 }
844         }
845
846         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
847                 Conflicts_axes[i] = -1;
848
849         for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
850                 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
851                         if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
852                                 Conflicts_axes[i] = j;
853                                 Conflicts_axes[j] = i;
854                                 Conflicts_tabs[SHIP_TAB] = 1;
855                         }
856                 }
857         }
858 }
859
860 // do list setup required prior to rendering and checking for the controls listing.  Called when list changes
861 void control_config_list_prepare()
862 {
863         int j, k, y, z;
864         int font_height = gr_get_font_height();
865
866         Num_cc_lines = y = z = 0;
867         while (z < CCFG_MAX) {
868                 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
869                         k = Control_config[z].key_id;
870                         j = Control_config[z].joy_id;
871                         Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z);
872                         Cc_lines[Num_cc_lines].cc_index = z;
873                         Cc_lines[Num_cc_lines++].y = y;
874                         y += font_height + 2;
875                 }
876
877                 z++;
878         }
879
880         if (Tab == SHIP_TAB) {
881                 for (j=0; j<NUM_JOY_AXIS_ACTIONS; j++) {
882                         Cc_lines[Num_cc_lines].label = Joy_axis_action_text[j];
883                         Cc_lines[Num_cc_lines].cc_index = j | JOY_AXIS;
884                         Cc_lines[Num_cc_lines++].y = y;
885                         y += font_height + 2;
886                 }
887         }
888 }
889
890 int cc_line_query_visible(int n)
891 {
892         int y;
893
894         if ((n < 0) || (n >= Num_cc_lines))
895                 return 0;
896         
897         y = Cc_lines[n].y - Cc_lines[Scroll_offset].y;
898         if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){
899                 return 0;
900         }
901
902         return 1;
903 }
904
905 // allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack).
906 // Returns a pointer to this newly allocated block
907 config_item_undo *get_undo_block(int size)
908 {
909         config_item_undo *ptr;
910
911         ptr = (config_item_undo *) malloc( sizeof(config_item_undo) );
912         Assert(ptr);
913         ptr->next = Config_item_undo;
914         Config_item_undo = ptr;
915
916         ptr->size = size;
917         if (size) {
918                 ptr->index = (int *) malloc( sizeof(int) * size );
919                 Assert(ptr->index);
920                 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
921                 Assert(ptr->list);
922
923         } else {
924                 ptr->index = NULL;
925                 ptr->list = NULL;
926         }
927
928         return ptr;
929 }
930
931 // frees one undo block.  The first one in the list (top of the stack) to be precise.
932 void free_undo_block()
933 {
934         config_item_undo *ptr;
935
936         ptr = Config_item_undo;
937         if (!ptr)
938                 return;
939
940         Config_item_undo = ptr->next;
941         if (ptr->size) {
942                 free(ptr->list);
943                 free(ptr->index);
944         }
945
946         free(ptr);
947 }
948
949 // undo the most recent binding changes
950 int control_config_undo_last()
951 {
952         int i, z, tab;
953
954         if (!Config_item_undo) {
955                 gamesnd_play_iface(SND_GENERAL_FAIL);
956                 return -1;
957         }
958
959         if (Config_item_undo->index[0] & JOY_AXIS)
960                 tab = SHIP_TAB;
961         else
962                 tab = Control_config[Config_item_undo->index[0]].tab;
963
964         for (i=1; i<Config_item_undo->size; i++) {
965                 if (Config_item_undo->index[i] & JOY_AXIS) {
966                         if (tab != SHIP_TAB)
967                                 tab = -1;
968
969                 } else {
970                         if (Control_config[Config_item_undo->index[i]].tab != tab)
971                                 tab = -1;
972                 }
973         }
974
975         if (tab >= 0)
976                 Tab = tab;
977
978         for (i=0; i<Config_item_undo->size; i++) {
979                 z = Config_item_undo->index[i];
980                 if (z & JOY_AXIS) {
981                         config_item *ptr;
982
983                         z &= ~JOY_AXIS;
984                         ptr = &Config_item_undo->list[i];
985                         Axis_map_to[z] = ptr->joy_id;
986                         Invert_axis[z] = ptr->used;
987
988                 } else {
989                         Control_config[z] = Config_item_undo->list[i];
990                 }
991         }
992
993         free_undo_block();
994         control_config_conflict_check();
995         control_config_list_prepare();
996         gamesnd_play_iface(SND_USER_SELECT);
997         return 0;
998 }
999
1000 void control_config_save_axis_undo(int axis)
1001 {
1002         config_item_undo *ptr;
1003         config_item item;
1004
1005         item.joy_id = (short) Axis_map_to[axis];
1006         item.used = 0;
1007         item.used = Invert_axis[axis];
1008
1009         ptr = get_undo_block(1);
1010         ptr->index[0] = axis | JOY_AXIS;
1011         ptr->list[0] = item;
1012 }
1013
1014 void control_config_bind_key(int i, int key)
1015 {
1016         config_item_undo *ptr;
1017
1018         ptr = get_undo_block(1);
1019         ptr->index[0] = i;
1020         ptr->list[0] = Control_config[i];
1021         Control_config[i].key_id = key;
1022 }
1023
1024 void control_config_bind_joy(int i, int joy)
1025 {
1026         config_item_undo *ptr;
1027
1028         ptr = get_undo_block(1);
1029         ptr->index[0] = i;
1030         ptr->list[0] = Control_config[i];
1031         Control_config[i].joy_id = (short) joy;
1032 }
1033
1034 void control_config_bind_axis(int i, int axis)
1035 {
1036         control_config_save_axis_undo(i);
1037         Axis_map_to[i] = axis;
1038 }
1039
1040 int control_config_remove_binding()
1041 {
1042         int z, j, k;
1043         config_item_undo *ptr;
1044
1045         if (Selected_line < 0) {
1046                 gamesnd_play_iface(SND_GENERAL_FAIL);
1047                 return -1;
1048         }
1049
1050         z = Cc_lines[Selected_line].cc_index;
1051         if (z & JOY_AXIS) {
1052                 z &= ~JOY_AXIS;
1053                 if (Axis_map_to[z] < 0) {
1054                         gamesnd_play_iface(SND_GENERAL_FAIL);
1055                         return -1;
1056                 }
1057
1058                 control_config_save_axis_undo(z);
1059                 Axis_map_to[z] = -1;
1060                 control_config_conflict_check();
1061                 control_config_list_prepare();
1062                 gamesnd_play_iface(SND_USER_SELECT);
1063                 Selected_item = -1;
1064                 return 0;
1065         }
1066
1067         if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1068                 gamesnd_play_iface(SND_GENERAL_FAIL);
1069                 return -1;
1070         }
1071
1072         j = k = -1;
1073         ptr = get_undo_block(1);
1074         ptr->index[0] = z;
1075         ptr->list[0] = Control_config[z];
1076
1077         if (Selected_item && (Control_config[z].joy_id >= 0))  // if not just key selected (which would be 0)
1078                 Control_config[z].joy_id = (short) -1;
1079
1080         if ((Selected_item != 1) && (Control_config[z].key_id >= 0))  // if not just joy button selected (1)
1081                 Control_config[z].key_id = -1;
1082
1083         control_config_conflict_check();
1084         control_config_list_prepare();
1085         gamesnd_play_iface(SND_USER_SELECT);
1086         Selected_item = -1;
1087         return 0;
1088 }
1089
1090 int control_config_clear_other()
1091 {
1092         int z, i, j, total = 0;
1093         config_item_undo *ptr;
1094
1095         if (Selected_line < 0) {
1096                 gamesnd_play_iface(SND_GENERAL_FAIL);
1097                 return -1;
1098         }
1099
1100         z = Cc_lines[Selected_line].cc_index;
1101         if (z & JOY_AXIS) {
1102                 config_item item;
1103
1104                 z &= ~JOY_AXIS;
1105                 if (Axis_map_to[z] < 0) {
1106                         gamesnd_play_iface(SND_GENERAL_FAIL);
1107                         return -1;
1108                 }
1109
1110                 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1111                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1112                                 total++;
1113
1114                 if (!total) {
1115                         gamesnd_play_iface(SND_GENERAL_FAIL);
1116                         return -1;
1117                 }
1118
1119                 ptr = get_undo_block(total);
1120                 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1121                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1122                                 item.joy_id = (short) Axis_map_to[i];
1123                                 item.used = Invert_axis[i];
1124
1125                                 ptr->index[j] = i | JOY_AXIS;
1126                                 ptr->list[j] = item;
1127                                 j++;
1128
1129                                 Axis_map_to[i] = -1;
1130                         }
1131
1132                 control_config_conflict_check();
1133                 control_config_list_prepare();
1134                 gamesnd_play_iface(SND_USER_SELECT);
1135                 return 0;
1136         }
1137
1138         for (i=0; i<CCFG_MAX; i++)
1139                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1140                         if (i != z)
1141                                 total++;
1142
1143         if (!total) {
1144                 gamesnd_play_iface(SND_GENERAL_FAIL);
1145                 return -1;
1146         }
1147
1148         if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1149                 gamesnd_play_iface(SND_GENERAL_FAIL);
1150                 return -1;
1151         }
1152
1153         // now, back up the old bindings so we can undo if we want to
1154         ptr = get_undo_block(total);
1155         for (i=j=0; i<CCFG_MAX; i++)
1156                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1157                         if (i != z) {
1158                                 ptr->index[j] = i;
1159                                 ptr->list[j] = Control_config[i];
1160                                 j++;
1161
1162                                 if (Control_config[i].key_id == Control_config[z].key_id)
1163                                         Control_config[i].key_id = -1;
1164                                 if (Control_config[i].joy_id == Control_config[z].joy_id)
1165                                         Control_config[i].joy_id = (short) -1;
1166                         }
1167
1168         control_config_conflict_check();
1169         control_config_list_prepare();
1170         gamesnd_play_iface(SND_USER_SELECT);
1171         return 0;
1172 }
1173
1174 int control_config_clear_all()
1175 {
1176         int i, j, total = 0;
1177         config_item_undo *ptr;
1178
1179         // first, determine how many bindings need to be changed
1180         for (i=0; i<CCFG_MAX; i++)
1181                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1182                         total++;
1183
1184         if (!total) {
1185                 gamesnd_play_iface(SND_GENERAL_FAIL);
1186                 return -1;
1187         }
1188
1189         // now, back up the old bindings so we can undo if we want to
1190         ptr = get_undo_block(total);
1191         for (i=j=0; i<CCFG_MAX; i++) {
1192                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1193                         ptr->index[j] = i;
1194                         ptr->list[j] = Control_config[i];
1195                         j++;
1196                 }
1197         }
1198
1199         Assert(j == total);
1200         for (i=0; i<CCFG_MAX; i++) {
1201                 Control_config[i].key_id = Control_config[i].joy_id = -1;
1202         }
1203
1204         control_config_conflict_check();
1205         control_config_list_prepare();
1206         gamesnd_play_iface(SND_RESET_PRESSED);
1207         return 0;
1208 }
1209
1210 int control_config_axis_default(int axis)
1211 {
1212         Assert(axis >= 0);
1213
1214         if ( axis > 1 ) {
1215                 if (Axis_map_to_defaults[axis] < 0)
1216                         return -1;
1217
1218                 if ( !joy_axis_valid(Axis_map_to_defaults[axis]) )
1219                         return -1;
1220         }
1221
1222         return Axis_map_to_defaults[axis];
1223 }
1224
1225 int control_config_do_reset()
1226 {
1227         int i, j, total = 0;
1228         config_item_undo *ptr;
1229         config_item item;
1230
1231         // first, determine how many bindings need to be changed
1232         for (i=0; i<CCFG_MAX; i++)
1233                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1234                         total++;
1235
1236         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1237                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1238                         total++;
1239
1240         if (!total) {
1241                 gamesnd_play_iface(SND_GENERAL_FAIL);
1242                 return -1;
1243         }
1244
1245         // now, back up the old bindings so we can undo if we want to
1246         ptr = get_undo_block(total);
1247         for (i=j=0; i<CCFG_MAX; i++) {
1248                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1249                         ptr->index[j] = i;
1250                         ptr->list[j] = Control_config[i];
1251                         j++;
1252                 }
1253         }
1254
1255         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1256                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1257                         item.joy_id = (short) Axis_map_to[i];
1258                         item.used = Invert_axis[i];
1259
1260                         ptr->index[j] = i | JOY_AXIS;
1261                         ptr->list[j] = item;
1262                         j++;
1263                 }
1264
1265         Assert(j == total);
1266         control_config_reset_defaults();
1267         control_config_conflict_check();
1268         control_config_list_prepare();
1269         gamesnd_play_iface(SND_RESET_PRESSED);
1270         return 0;
1271 }
1272
1273 // This sets all the controls to their default values
1274 void control_config_reset_defaults()
1275 {
1276         int i;
1277
1278         // Reset keyboard defaults
1279         for (i=0; i<CCFG_MAX; i++) {
1280                 Control_config[i].key_id = Control_config[i].key_default;
1281                 Control_config[i].joy_id = Control_config[i].joy_default;
1282         }
1283
1284         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1285                 Axis_map_to[i] = control_config_axis_default(i);
1286                 Invert_axis[i] = Invert_axis_defaults[i];
1287         }
1288 }
1289
1290 void control_config_scroll_screen_up()
1291 {
1292         if (Scroll_offset) {
1293                 Scroll_offset--;
1294                 Assert(Selected_line > Scroll_offset);
1295                 while (!cc_line_query_visible(Selected_line))
1296                         Selected_line--;
1297
1298                 Selected_item = -1;
1299                 gamesnd_play_iface(SND_SCROLL);
1300
1301         } else
1302                 gamesnd_play_iface(SND_GENERAL_FAIL);
1303 }
1304
1305 void control_config_scroll_line_up()
1306 {
1307         if (Selected_line) {
1308                 Selected_line--;
1309                 if (Selected_line < Scroll_offset)
1310                         Scroll_offset = Selected_line;
1311
1312                 Selected_item = -1;
1313                 gamesnd_play_iface(SND_SCROLL);
1314
1315         } else
1316                 gamesnd_play_iface(SND_GENERAL_FAIL);
1317 }
1318
1319 void control_config_scroll_screen_down()
1320 {
1321         if (Cc_lines[Num_cc_lines - 1].y + gr_get_font_height() > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
1322                 Scroll_offset++;
1323                 while (!cc_line_query_visible(Selected_line)) {
1324                         Selected_line++;
1325                         Assert(Selected_line < Num_cc_lines);
1326                 }
1327
1328                 Selected_item = -1;
1329                 gamesnd_play_iface(SND_SCROLL);
1330
1331         } else
1332                 gamesnd_play_iface(SND_GENERAL_FAIL);
1333 }
1334
1335 void control_config_scroll_line_down()
1336 {
1337         if (Selected_line < Num_cc_lines - 1) {
1338                 Selected_line++;
1339                 Assert(Selected_line > Scroll_offset);
1340                 while (!cc_line_query_visible(Selected_line))
1341                         Scroll_offset++;
1342
1343                 Selected_item = -1;
1344                 gamesnd_play_iface(SND_SCROLL);
1345
1346         } else
1347                 gamesnd_play_iface(SND_GENERAL_FAIL);
1348 }
1349
1350 void control_config_toggle_modifier(int bit)
1351 {
1352         int k, z;
1353
1354         z = Cc_lines[Selected_line].cc_index;
1355         Assert(!(z & JOY_AXIS));
1356         k = Control_config[z].key_id;
1357         if (k < 0) {
1358                 gamesnd_play_iface(SND_GENERAL_FAIL);
1359                 return;
1360         }
1361
1362         control_config_bind_key(z, k ^ bit);
1363         control_config_conflict_check();
1364         gamesnd_play_iface(SND_USER_SELECT);
1365 }
1366
1367 void control_config_toggle_invert()
1368 {
1369         int z;
1370
1371         z = Cc_lines[Selected_line].cc_index;
1372         Assert(z & JOY_AXIS);
1373         z &= ~JOY_AXIS;
1374         control_config_save_axis_undo(z);
1375         Invert_axis[z] = !Invert_axis[z];
1376 }
1377
1378 void control_config_do_bind()
1379 {
1380         int i;
1381
1382         game_flush();
1383 //      if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1384         if (Selected_line < 0) {
1385                 gamesnd_play_iface(SND_GENERAL_FAIL);
1386                 return;
1387         }
1388
1389         for (i=0; i<NUM_BUTTONS; i++)
1390                 if (i != CANCEL_BUTTON) {
1391                         CC_Buttons[gr_screen.res][i].button.reset_status();
1392                         CC_Buttons[gr_screen.res][i].button.disable();
1393                 }
1394
1395         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1396         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1397
1398         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1399                 joy_down_count(i);  // clear checking status of all joystick buttons
1400         }
1401
1402         control_config_detect_axis_reset();
1403
1404         Binding_mode = 1;
1405         Bind_time = timer_get_milliseconds();
1406         Search_mode = 0;
1407         Last_key = -1;
1408         Axis_override = -1;
1409         gamesnd_play_iface(SND_USER_SELECT);
1410 }
1411
1412 void control_config_do_search()
1413 {
1414         int i;
1415
1416         for (i=0; i<NUM_BUTTONS; i++){
1417                 if (i != CANCEL_BUTTON) {
1418                         CC_Buttons[gr_screen.res][i].button.reset_status();
1419                         CC_Buttons[gr_screen.res][i].button.disable();
1420                 }
1421         }
1422
1423         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1424         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1425
1426         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1427                 joy_down_count(i);  // clear checking status of all joystick buttons
1428         }
1429
1430         Binding_mode = 0;
1431         Search_mode = 1;
1432         Last_key = -1;
1433         gamesnd_play_iface(SND_USER_SELECT);
1434 }
1435
1436 void control_config_do_cancel(int fail = 0)
1437 {
1438         int i;
1439
1440         game_flush();
1441
1442         for (i=0; i<NUM_BUTTONS; i++){
1443                 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1444                         CC_Buttons[gr_screen.res][i].button.enable();
1445                 }
1446         }
1447
1448         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1449         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1450         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1451         CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1452         CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1453
1454         Binding_mode = Search_mode = 0;
1455         if (fail){
1456                 gamesnd_play_iface(SND_GENERAL_FAIL);
1457         } else {
1458                 gamesnd_play_iface(SND_USER_SELECT);
1459         }
1460 }
1461
1462 int control_config_accept()
1463 {
1464         int i;
1465
1466         for (i=0; i<NUM_TABS; i++)
1467                 if (Conflicts_tabs[i])
1468                         break;
1469
1470         if (i < NUM_TABS) {
1471                 gamesnd_play_iface(SND_GENERAL_FAIL);
1472                 return -1;
1473         }
1474
1475         hud_squadmsg_save_keys();  // rebuild map for saving/restoring keys in squadmsg mode
1476         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1477         gamesnd_play_iface(SND_COMMIT_PRESSED);
1478         return 0;
1479 }
1480
1481 void control_config_cancel_exit()
1482 {
1483         int i;
1484
1485         for (i=0; i<CCFG_MAX; i++)
1486                 Control_config[i] = Control_config_backup[i];
1487
1488         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1489 }
1490
1491 void control_config_button_pressed(int n)
1492 {
1493         switch (n) {
1494                 case TARGET_TAB:
1495                 case SHIP_TAB:
1496                 case WEAPON_TAB:
1497                 case COMPUTER_TAB:
1498                         Tab = n;
1499                         Scroll_offset = Selected_line = 0;
1500                         control_config_list_prepare();
1501                         gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1502                         break;
1503
1504                 case BIND_BUTTON:
1505                         control_config_do_bind();
1506                         break;
1507
1508                 case SEARCH_MODE:
1509                         control_config_do_search();
1510                         break;
1511
1512                 case SHIFT_TOGGLE:
1513                         control_config_toggle_modifier(KEY_SHIFTED);
1514                         gamesnd_play_iface(SND_USER_SELECT);
1515                         break;
1516
1517                 case ALT_TOGGLE:
1518                         control_config_toggle_modifier(KEY_ALTED);
1519                         gamesnd_play_iface(SND_USER_SELECT);
1520                         break;
1521
1522                 case INVERT_AXIS:
1523                         control_config_toggle_invert();
1524                         gamesnd_play_iface(SND_USER_SELECT);
1525                         break;
1526
1527                 case SCROLL_UP_BUTTON:
1528                         control_config_scroll_screen_up();
1529                         break;
1530
1531                 case SCROLL_DOWN_BUTTON:
1532                         control_config_scroll_screen_down();
1533                         break;
1534
1535                 case ACCEPT_BUTTON:
1536                         control_config_accept();
1537                         break;
1538
1539                 case CLEAR_BUTTON:
1540                         control_config_remove_binding();
1541                         break;
1542
1543                 case HELP_BUTTON:
1544                         launch_context_help();
1545                         gamesnd_play_iface(SND_HELP_PRESSED);
1546                         break;
1547
1548                 case RESET_BUTTON:
1549                         control_config_do_reset();
1550                         break;
1551
1552                 case UNDO_BUTTON:
1553                         control_config_undo_last();
1554                         break;
1555
1556                 case CANCEL_BUTTON:
1557                         control_config_do_cancel();
1558                         break;
1559
1560                 case CLEAR_OTHER_BUTTON:
1561                         control_config_clear_other();
1562                         break;
1563
1564                 case CLEAR_ALL_BUTTON:
1565                         control_config_clear_all();
1566                         break;          
1567         }
1568 }
1569
1570 const char *control_config_tooltip_handler(const char *str)
1571 {
1572         int i;
1573
1574         if (!stricmp(str, NOX("@conflict"))) {
1575                 for (i=0; i<NUM_TABS; i++) {
1576                         if (Conflicts_tabs[i])
1577                                 return XSTR( "Conflict!", 205);
1578                 }
1579         }
1580
1581         return NULL;
1582 }
1583
1584 void control_config_init()
1585 {
1586         int i;
1587         ui_button_info *b;
1588
1589         // make backup of all controls
1590         for (i=0; i<CCFG_MAX; i++)
1591                 Control_config_backup[i] = Control_config[i];
1592
1593         common_set_interface_palette(NOX("ControlConfigPalette"));  // set the interface palette
1594         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1595         Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1596         Ui_window.tooltip_handler = control_config_tooltip_handler;
1597
1598         // load in help overlay bitmap  
1599         help_overlay_load(CONTROL_CONFIG_OVERLAY);
1600         help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1601
1602         // reset conflict flashing
1603         Conflict_stamp = -1;
1604
1605 #ifdef MAKE_FS1
1606         hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1607         hud_anim_load(&Conflict_warning_anim);
1608 #endif
1609
1610         for (i=0; i<NUM_BUTTONS; i++) {
1611                 b = &CC_Buttons[gr_screen.res][i];
1612
1613                 if (b->hotspot < 0) {  // temporary
1614                         b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1);  // temporary
1615                         b->button.set_highlight_action(common_play_highlight_sound);
1616                         continue;
1617                 }
1618
1619                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1620
1621                 // set up callback for when a mouse first goes over a button
1622                 b->button.set_highlight_action(common_play_highlight_sound);            
1623                 if (i<4) {
1624                         b->button.set_bmaps(b->filename, 5, 1);         // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1625                 } else {
1626                         b->button.set_bmaps(b->filename);
1627                 }
1628                 b->button.link_hotspot(b->hotspot);
1629         }       
1630
1631 #ifndef MAKE_FS1
1632         // create all text
1633         for(i=0; i<CC_NUM_TEXT; i++){
1634                 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1635         }
1636 #endif
1637
1638         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1639                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1640                 List_buttons[i].hide();
1641                 List_buttons[i].disable();
1642         }
1643
1644         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1645         CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1646         CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1647         CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(SDLK_RETURN);
1648         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_DELETE);
1649         CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_z);
1650         CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(SDLK_DELETE);
1651         CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
1652         CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(SDLK_F1);
1653         CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_r);
1654         CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(SDLK_i);
1655
1656         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1657         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1658
1659         Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);   
1660
1661         Scroll_offset = Selected_line = 0;
1662         Config_item_undo = NULL;
1663         control_config_conflict_check();
1664
1665         // setup strings                                        
1666         Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1667         Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1668         Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1669         Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1670         Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1671         Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1672         Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1673         Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1674         Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1675         Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1676         Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1677         Mouse_button_text[0] = strdup("");
1678         Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1679         Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1680         Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1681         Mouse_button_text[4] = strdup("");
1682         Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1683         Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1684         Invert_text[0] = strdup(XSTR("N", 1032));
1685         Invert_text[1] = strdup(XSTR("Y", 1033));
1686
1687         control_config_list_prepare();
1688 }
1689
1690 void control_config_close()
1691 {
1692         int idx;
1693         
1694         while (Config_item_undo){
1695                 free_undo_block();
1696         }
1697
1698         // unload the overlay bitmap
1699         help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1700         
1701         if (Background_bitmap){
1702                 bm_unload(Background_bitmap);
1703         }
1704
1705 #ifdef MAKE_FS1
1706         hud_anim_release(&Conflict_warning_anim);
1707 #endif
1708
1709         Ui_window.destroy();
1710         common_free_interface_palette();                // restore game palette
1711         hud_squadmsg_save_keys();                               // rebuild map for saving/restoring keys in squadmsg mode
1712         game_flush();
1713         write_pilot_file();
1714
1715         // free strings 
1716         for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1717                 if(Joy_axis_action_text[idx] != NULL){
1718                         free(Joy_axis_action_text[idx]);
1719                         Joy_axis_action_text[idx] = NULL;
1720                 }
1721         }
1722         for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1723                 if(Joy_axis_text[idx] != NULL){
1724                         free(Joy_axis_text[idx]);
1725                         Joy_axis_text[idx] = NULL;
1726                 }
1727         }
1728         for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1729                 if(Mouse_button_text[idx] != NULL){
1730                         free(Mouse_button_text[idx]);
1731                         Mouse_button_text[idx] = NULL;
1732                 }
1733         }
1734         for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1735                 if(Mouse_axis_text[idx] != NULL){
1736                         free(Mouse_axis_text[idx]);
1737                         Mouse_axis_text[idx] = NULL;
1738                 }
1739         }
1740         for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1741                 if(Invert_text[idx] != NULL){
1742                         free(Invert_text[idx]);
1743                         Invert_text[idx] = NULL;
1744                 }
1745         }
1746 }
1747
1748 void control_config_do_frame(float frametime)
1749 {
1750         char buf[256], *jptr;
1751         int i, j, k, w, x, y, z, len, line, conflict;
1752         int font_height = gr_get_font_height();
1753         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1754         static float timer = 0.0f;
1755         color *c;
1756         static int bound_timestamp = 0;
1757         static char bound_string[40];
1758         
1759         timer += frametime;
1760
1761         if (Binding_mode) {
1762                 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1763                         int bind = 0;
1764
1765                         z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1766                         i = control_config_detect_axis();
1767                         if (i >= 0) {
1768                                 Axis_override = i;
1769                                 bind = 1;
1770                         }
1771
1772                         k = game_poll();
1773                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1774                         Ui_window.process(0);
1775
1776                         if (k == SDLK_ESCAPE) {
1777                                 strcpy(bound_string, XSTR( "Canceled", 206));
1778                                 bound_timestamp = timestamp(2500);
1779                                 control_config_do_cancel();
1780
1781                         } else {
1782                                 if (k == SDLK_RETURN)
1783                                         bind = 1;
1784
1785                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1786                                         if (joy_down_count(i))
1787                                                 bind = 1;
1788
1789                                 if (bind) {
1790                                         if (Axis_override >= 0) {
1791                                                 control_config_bind_axis(z, Axis_override);
1792                                                 strcpy(bound_string, Joy_axis_text[Axis_override]);
1793                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1794                                                 bound_timestamp = timestamp(2500);
1795                                                 control_config_conflict_check();
1796                                                 control_config_list_prepare();
1797                                                 control_config_do_cancel();
1798
1799                                         } else {
1800                                                 control_config_do_cancel(1);
1801                                         }
1802                                 }
1803                         }
1804
1805                 } else {
1806                         if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1807                                 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1808                                 Ui_window.set_ignore_gadgets(1);
1809                         }
1810
1811                         k = game_poll();
1812                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1813                         Ui_window.process(0);
1814
1815                         if ( (k > 0) || B1_JUST_RELEASED ) {
1816                                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1817                                         help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1818                                         Ui_window.set_ignore_gadgets(0);
1819                                         k = 0;
1820                                 }
1821                         }
1822
1823                         if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1824                                 Ui_window.set_ignore_gadgets(0);
1825                         }
1826
1827                         if (k == SDLK_ESCAPE) {
1828                                 strcpy(bound_string, XSTR( "Canceled", 206));
1829                                 bound_timestamp = timestamp(2500);
1830                                 control_config_do_cancel();
1831
1832                         } else {
1833                                 switch (k & KEY_MASK) {
1834                                         case SDLK_LSHIFT:
1835                                         case SDLK_RSHIFT:
1836                                         case SDLK_LALT:
1837                                         case SDLK_RALT:
1838                                                 Last_key = k & KEY_MASK;
1839                                                 k = 0;
1840                                                 break;
1841                                 }
1842
1843                                 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED)  // a special hack just for Mike K.
1844                                         if ( (Last_key >= 0) && (k <= 0) && !key_pressed(Last_key) )
1845                                                 k = Last_key;
1846
1847                                 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1848                                         popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key.  Please try again.", 207));
1849                                         k = 0;
1850                                 }
1851
1852                                 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1853                                 if (k > 0) {
1854                                         z = Cc_lines[Selected_line].cc_index;
1855                                         Assert(!(z & JOY_AXIS));
1856                                         control_config_bind_key(z, k);
1857
1858                                         strcpy(bound_string, textify_scancode(k));
1859                                         gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1860                                         bound_timestamp = timestamp(2500);
1861                                         control_config_conflict_check();
1862                                         control_config_list_prepare();
1863                                         control_config_do_cancel();
1864                                 }
1865
1866                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1867                                         if (joy_down_count(i)) {
1868                                                 z = Cc_lines[Selected_line].cc_index;
1869                                                 Assert(!(z & JOY_AXIS));
1870                                                 control_config_bind_joy(z, i);
1871
1872                                                 strcpy(bound_string, Joy_button_text[i]);
1873                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1874                                                 bound_timestamp = timestamp(2500);
1875                                                 control_config_conflict_check();
1876                                                 control_config_list_prepare();
1877                                                 control_config_do_cancel();
1878                                                 break;
1879                                         }
1880
1881                                 if (Bind_time + 375 < timer_get_milliseconds()) {
1882                                         for (i=0; i<NUM_BUTTONS; i++){
1883                                                 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1884                                                         break;
1885                                                 }
1886                                         }
1887
1888                                         if (i == NUM_BUTTONS) {  // no buttons pressed
1889                                                 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1890                                                         if (mouse_down(1 << i)) {
1891                                                                 z = Cc_lines[Selected_line].cc_index;
1892                                                                 Assert(!(z & JOY_AXIS));
1893                                                                 control_config_bind_joy(z, i);
1894
1895                                                                 strcpy(bound_string, Joy_button_text[i]);
1896                                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1897                                                                 bound_timestamp = timestamp(2500);
1898                                                                 control_config_conflict_check();
1899                                                                 control_config_list_prepare();
1900                                                                 control_config_do_cancel();
1901                                                                 for (j=0; j<NUM_BUTTONS; j++){
1902                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1903                                                                 }
1904
1905                                                                 break;
1906                                                         }
1907                                         }
1908                                 }
1909                         }
1910                 }
1911
1912         } else if (Search_mode) {
1913                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1914                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1915                         Ui_window.set_ignore_gadgets(1);
1916                 }
1917
1918                 k = game_poll();
1919                 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1920                 Ui_window.process(0);
1921
1922                 if ( (k > 0) || B1_JUST_RELEASED ) {
1923                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1924                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1925                                 Ui_window.set_ignore_gadgets(0);
1926                                 k = 0;
1927                         }
1928                 }
1929
1930                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1931                         Ui_window.set_ignore_gadgets(0);
1932                 }
1933
1934                 if (k == SDLK_ESCAPE) {
1935                         control_config_do_cancel();
1936
1937                 } else {
1938                         if ((k > 0) && !Config_allowed[k & KEY_MASK])
1939                                 k = 0;
1940
1941                         k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1942                         z = -1;
1943                         if (k > 0) {
1944                                 for (i=0; i<CCFG_MAX; i++)
1945                                         if (Control_config[i].key_id == k) {
1946                                                 z = i;
1947                                                 break;
1948                                         }
1949                         }
1950
1951                         for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1952                                 if (joy_down_count(i)) {
1953                                         j = i;
1954                                         for (i=0; i<CCFG_MAX; i++)
1955                                                 if (Control_config[i].joy_id == j) {
1956                                                         z = i;
1957                                                         break;
1958                                                 }
1959
1960                                         break;
1961                                 }
1962
1963                         // check if not on enabled button
1964                         for (j=0; j<NUM_BUTTONS; j++){
1965                                 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1966                                         break;
1967                                 }
1968                         }
1969
1970                         if (j == NUM_BUTTONS) {  // no buttons pressed
1971                                 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1972                                         if (mouse_down(1 << j)) {
1973                                                 for (i=0; i<CCFG_MAX; i++)
1974                                                         if (Control_config[i].joy_id == j) {
1975                                                                 z = i;
1976                                                                 for (j=0; j<NUM_BUTTONS; j++){
1977                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1978                                                                 }
1979                                                                 break;
1980                                                         }
1981
1982                                                 break;
1983                                         }
1984                         }
1985
1986                         if (z >= 0) {
1987                                 Tab = Control_config[z].tab;
1988                                 control_config_list_prepare();
1989                                 Selected_line = Scroll_offset = 0;
1990                                 for (i=0; i<Num_cc_lines; i++)
1991                                         if (Cc_lines[i].cc_index == z) {
1992                                                 Selected_line = i;
1993                                                 break;
1994                                         }
1995
1996                                 while (!cc_line_query_visible(Selected_line)) {
1997                                         Scroll_offset++;
1998                                         Assert(Scroll_offset < Num_cc_lines);
1999                                 }
2000                         }
2001                 }
2002
2003         } else {
2004                 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2005                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2006                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2007                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2008
2009                 if (!z) {
2010                         z = Cc_lines[Selected_line].cc_index;
2011                         k = Control_config[z].key_id;
2012                         if ( (k == SDLK_LALT) || (k == SDLK_RALT) || (k == SDLK_LSHIFT) || (k == SDLK_RSHIFT) ) {
2013                                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2014                                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2015                         }
2016                 }
2017
2018                 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2019
2020                 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2021                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2022                         Ui_window.set_ignore_gadgets(1);
2023                 }
2024
2025                 k = Ui_window.process();
2026
2027                 if ( (k > 0) || B1_JUST_RELEASED ) {
2028                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2029                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2030                                 Ui_window.set_ignore_gadgets(0);
2031                                 k = 0;
2032                         }
2033                 }
2034
2035                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2036                         Ui_window.set_ignore_gadgets(0);
2037                 }
2038
2039                 switch (k) {
2040                         case SDLK_DOWN:  // select next line
2041                                 control_config_scroll_line_down();
2042                                 break;
2043
2044                         case SDLK_UP:  // select previous line
2045                                 control_config_scroll_line_up();
2046                                 break;
2047
2048                         case KEY_SHIFTED | SDLK_TAB:  // activate previous tab
2049                                 Tab--;
2050                                 if (Tab < 0)
2051                                         Tab = NUM_TABS - 1;
2052
2053                                 Scroll_offset = Selected_line = 0;
2054                                 control_config_list_prepare();
2055                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2056                                 break;
2057
2058                         case SDLK_TAB:  // activate next tab
2059                                 Tab++;
2060                                 if (Tab >= NUM_TABS)
2061                                         Tab = 0;
2062
2063                                 Scroll_offset = Selected_line = 0;
2064                                 control_config_list_prepare();
2065                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2066                                 break;
2067
2068                         case SDLK_LEFT:
2069                                 Selected_item--;
2070                                 if (Selected_item == -2) {
2071                                         Selected_item = 1;
2072                                         if (Cc_lines[Selected_line].jw < 1) {
2073                                                 Selected_item = 0;
2074                                                 if (Cc_lines[Selected_line].kw < 1)
2075                                                         Selected_item = -1;
2076                                         }
2077                                 }
2078
2079                                 gamesnd_play_iface(SND_SCROLL);
2080                                 break;
2081
2082                         case SDLK_RIGHT:
2083                                 Selected_item++;
2084                                 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2085                                         Selected_item = -1;
2086                                 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2087                                         Selected_item = -1;
2088                                 else if (Selected_item > 1)
2089                                         Selected_item = -1;
2090
2091                                 gamesnd_play_iface(SND_SCROLL);
2092                                 break;
2093
2094                         case SDLK_BACKSPACE:  // undo
2095                                 control_config_undo_last();
2096                                 break;
2097
2098                         case SDLK_ESCAPE:
2099                                 control_config_cancel_exit();
2100                                 break;
2101                 }       // end switch
2102         }
2103
2104         for (i=0; i<NUM_BUTTONS; i++){
2105                 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2106                         control_config_button_pressed(i);
2107                 }
2108         }
2109
2110         for (i=0; i<LIST_BUTTONS_MAX; i++) {
2111                 if (List_buttons[i].is_mouse_on())
2112                         select_tease_line = i + Scroll_offset;
2113         
2114                 if (List_buttons[i].pressed()) {
2115                         Selected_line = i + Scroll_offset;
2116                         Selected_item = -1;
2117                         List_buttons[i].get_mouse_pos(&x, &y);
2118                         if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2119                                 Selected_item = 0;
2120
2121                         if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2122                                 Selected_item = 1;
2123
2124                         gamesnd_play_iface(SND_USER_SELECT);
2125                 }
2126
2127                 if (List_buttons[i].double_clicked())
2128                         control_config_do_bind();
2129         }
2130
2131         GR_MAYBE_CLEAR_RES(Background_bitmap);
2132         if (Background_bitmap >= 0) {
2133                 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2134                 gr_bitmap(0, 0);
2135         } 
2136
2137         // highlight tab with conflict
2138         Ui_window.draw();
2139         for (i=z=0; i<NUM_TABS; i++) {
2140                 if (Conflicts_tabs[i]) {
2141                         CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2142                         z++;
2143                 }
2144         }
2145
2146         if (z) {
2147                 // maybe switch from bright to normal
2148                 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2149                         Conflict_bright = !Conflict_bright;
2150
2151                         Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2152                 }
2153
2154 #ifndef MAKE_FS1
2155                 // set color and font
2156                 gr_set_font(FONT2);
2157                 if(Conflict_bright){
2158                         gr_set_color_fast(&Color_bright_red);
2159                 } else {
2160                         gr_set_color_fast(&Color_red);
2161                 }
2162
2163                 // setup the conflict string
2164                 char conflict_str[512] = "";
2165                 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2166                 int sw, sh;
2167                 gr_get_string_size(&sw, &sh, conflict_str);
2168
2169                 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2170
2171                 gr_set_font(FONT1);
2172 #else
2173                 hud_anim_render(&Conflict_warning_anim, frametime);
2174 #endif
2175         } else {
2176                 // might as well always reset the conflict stamp
2177                 Conflict_stamp = -1;
2178         }
2179
2180         for (i=0; i<NUM_TABS; i++) {
2181                 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2182                         break;
2183                 }
2184         }
2185
2186         if (i == NUM_TABS) {
2187                 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2188         }
2189
2190         if (Search_mode) {
2191                 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2192         }
2193
2194         if (Selected_line >= 0) {
2195                 z = Cc_lines[Selected_line].cc_index;
2196                 if (z & JOY_AXIS) {
2197                         if (Invert_axis[z & ~JOY_AXIS]) {
2198                                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2199                         }
2200
2201                 } else {
2202                         z = Control_config[z].key_id;
2203                         if (z >= 0) {
2204                                 if (z & KEY_SHIFTED) {
2205                                         CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2206                                 }
2207                                 if (z & KEY_ALTED) {
2208                                         CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2209                                 }
2210                         }
2211                 }
2212         }
2213
2214         if (Binding_mode) {
2215                 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2216         }
2217
2218         z = Cc_lines[Selected_line].cc_index;
2219         x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2220         y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2221         if (Binding_mode) {
2222                 int t;
2223
2224                 t = (int) (timer * 3);
2225                 if (t % 2) {
2226                         gr_set_color_fast(&Color_text_normal);
2227                         gr_get_string_size(&w, NULL, XSTR( "?", 208));
2228                         gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2229                 }
2230
2231         } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2232                 i = Conflicts[z].key;
2233                 if (i < 0)
2234                         i = Conflicts[z].joy;
2235
2236                 gr_set_color_fast(&Color_text_normal);
2237                 const char *str = XSTR( "Control conflicts with:", 209);
2238                 gr_get_string_size(&w, NULL, str);
2239                 gr_printf(x - w / 2, y - font_height, str);
2240
2241                 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2242                 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2243                 gr_get_string_size(&w, NULL, buf);
2244                 gr_printf(x - w / 2, y, buf);
2245
2246         } else if (*bound_string) {
2247                 gr_set_color_fast(&Color_text_normal);
2248                 gr_get_string_size(&w, NULL, bound_string);
2249                 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2250                 if (timestamp_elapsed(bound_timestamp))
2251                         *bound_string = 0;
2252         }
2253
2254 //      gr_set_color_fast(&Color_text_heading);
2255 //      gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2256
2257 //      gr_get_string_size(&w, &h, Heading[Tab]);
2258 //      y = HEADING_Y + h / 2 - 1;
2259 //      gr_line(LIST_X, y, LIST_X + 18, y);
2260 //      gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2261
2262         if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2263                 gr_set_color_fast(&Color_white);
2264                 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2265         }
2266
2267         conflict = 0;
2268         line = Scroll_offset;
2269         while (cc_line_query_visible(line)) {
2270                 z = Cc_lines[line].cc_index;
2271                 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2272
2273                 List_buttons[line - Scroll_offset].update_dimensions(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, Control_list_coords[gr_screen.res][CONTROL_W_COORD], font_height);
2274                 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2275
2276                 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2277
2278                 if (line == Selected_line){
2279                         c = &Color_text_selected;
2280                 } else if (line == select_tease_line) {
2281                         c = &Color_text_subselected;
2282                 } else {
2283                         c = &Color_text_normal;
2284                 }
2285
2286                 gr_set_color_fast(c);
2287                 if (Cc_lines[line].label) {
2288                         strcpy(buf, Cc_lines[line].label);
2289                         gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2290                         gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2291                 }
2292
2293                 if (!(z & JOY_AXIS)) {
2294                         k = Control_config[z].key_id;
2295                         j = Control_config[z].joy_id;
2296                         x = Control_list_key_x[gr_screen.res];
2297                         jptr = NULL;
2298                         *buf = 0;
2299
2300                         if ((k < 0) && (j < 0)) {
2301                                 gr_set_color_fast(&Color_grey);
2302                                 gr_printf(x, y, XSTR( "None", 211));
2303
2304                         } else {
2305                                 if (k >= 0) {
2306                                         strcpy(buf, textify_scancode(k));
2307                                         if (Conflicts[z].key >= 0) {
2308                                                 if (c == &Color_text_normal)
2309                                                         gr_set_color_fast(&Color_text_error);
2310                                                 else {
2311                                                         gr_set_color_fast(&Color_text_error_hi);
2312                                                         conflict++;
2313                                                 }
2314
2315                                         } else if (Selected_item == 1) {
2316                                                 gr_set_color_fast(&Color_text_normal);
2317
2318                                         } else
2319                                                 gr_set_color_fast(c);
2320
2321                                         gr_printf(x, y, buf);
2322
2323                                         len = strlen(buf);
2324                                         Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2325                                         gr_get_string_size(&w, NULL, buf);
2326                                         Cc_lines[line].kw = w;
2327                                         x += w;
2328
2329                                         if (j >= 0) {
2330                                                 gr_set_color_fast(&Color_text_normal);
2331                                                 gr_printf(x, y, XSTR( ", ", 212));
2332                                                 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2333                                                 x += w;
2334                                         }
2335                                 }
2336
2337                                 if (j >= 0) {
2338                                         strcpy(buf, Joy_button_text[j]);
2339                                         if (Conflicts[z].joy >= 0) {
2340                                                 if (c == &Color_text_normal)
2341                                                         gr_set_color_fast(&Color_text_error);
2342                                                 else {
2343                                                         gr_set_color_fast(&Color_text_error_hi);
2344                                                         conflict++;
2345                                                 }
2346
2347                                         } else if (!Selected_item) {
2348                                                 gr_set_color_fast(&Color_text_normal);
2349
2350                                         } else
2351                                                 gr_set_color_fast(c);
2352
2353                                         gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2354                                         gr_printf(x, y, buf);
2355
2356                                         Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2357                                         gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2358                                 }
2359                         }
2360
2361                 } else {
2362                         x = Control_list_key_x[gr_screen.res];
2363                         j = Axis_map_to[z & ~JOY_AXIS];
2364                         if (Binding_mode && (line == Selected_line))
2365                                 j = Axis_override;
2366
2367                         if (j < 0) {
2368                                 gr_set_color_fast(&Color_grey);
2369                                 gr_printf(x, y, XSTR( "None", 211));
2370
2371                         } else {
2372                                 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2373                                         if (c == &Color_text_normal)
2374                                                 gr_set_color_fast(&Color_text_error);
2375
2376                                         else {
2377                                                 gr_set_color_fast(&Color_text_error_hi);
2378                                                 conflict++;
2379                                         }
2380
2381                                 } else if (!Selected_item) {
2382                                         gr_set_color_fast(&Color_text_normal);
2383
2384                                 } else
2385                                         gr_set_color_fast(c);
2386
2387                                 gr_string(x, y, Joy_axis_text[j]);
2388                         }
2389                 }
2390
2391                 line++;
2392         }
2393
2394         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2395
2396         i = line - Scroll_offset;
2397         while (i < LIST_BUTTONS_MAX)
2398                 List_buttons[i++].disable();
2399
2400         // blit help overlay if active
2401         help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2402
2403         gr_flip();
2404 }
2405
2406 void clear_key_binding(short key)
2407 {
2408         int i;
2409
2410         for (i=0; i<CCFG_MAX; i++) {
2411                 if (Control_config[i].key_id == key)
2412                         Control_config[i].key_id = -1;
2413         }
2414 }
2415
2416 float check_control_timef(int id)
2417 {
2418         float t1, t2;
2419
2420         // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2421         Assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2422
2423         // first, see if control actually used (makes sure modifiers match as well)
2424         if (!check_control(id))
2425                 return 0.0f;
2426
2427         t1 = key_down_timef(Control_config[id].key_id);
2428         if (t1)
2429                 control_used(id);
2430
2431         t2 = joy_down_time(Control_config[id].joy_id);
2432         if (t2)
2433                 control_used(id);
2434
2435         if (t1 + t2)
2436                 return t1 + t2;
2437
2438         return 1.0f;
2439 }
2440
2441 void control_check_indicate()
2442 {
2443 #ifndef NDEBUG
2444         if (Show_controls_info) {
2445                 gr_set_color_fast(&HUD_color_debug);
2446                 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2447         }
2448 #endif
2449
2450         Control_check_count = 0;
2451 }
2452
2453 int check_control(int id, int key)
2454 {
2455         int z, mask;
2456         static int last_key = 0;
2457
2458         Control_check_count++;
2459         if (key < 0)
2460                 key = last_key;
2461
2462         last_key = key;
2463
2464         // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2465         if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2466                 return 0;
2467         }
2468
2469         if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2470                 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2471                         control_used(id);
2472                         return 1;
2473                 }
2474
2475                 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2476                         if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2477                                 control_used(id);
2478                                 return 1;
2479                         }
2480
2481                 // check what current modifiers are pressed
2482                 mask = key_get_shift_status();
2483
2484                 z = Control_config[id].key_id;
2485                 if (z >= 0) {
2486                         if ( (z != SDLK_LALT) && (z != SDLK_RALT) && (z != SDLK_LSHIFT) && (z != SDLK_RSHIFT) ) {
2487                                 // if current modifiers don't match action's modifiers, don't register control active.
2488                                 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2489                                         return 0;
2490                         }
2491
2492                         z &= KEY_MASK;
2493
2494                         if (key_pressed(z) || key_down_count(z)) {
2495                                 if ( !hud_squadmsg_read_key(z) ) {
2496                                         control_used(id);
2497                                         return 1;
2498                                 }
2499                         }
2500                 }
2501
2502                 return 0;
2503         }
2504
2505         if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2506                 control_used(id);
2507                 return 1;
2508         }
2509
2510         return 0;
2511 }
2512
2513 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2514 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2515 {
2516         //      joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2517         *h = 0;
2518         if (Axis_map_to[0] >= 0)
2519                 *h = joy_get_scaled_reading(Axis_map_to[0]);
2520
2521         *p = 0;
2522         if (Axis_map_to[1] >= 0)
2523                 *p = joy_get_scaled_reading(Axis_map_to[1]);
2524
2525         *b = 0;
2526         if (Axis_map_to[2] >= 0)
2527                 *b = joy_get_scaled_reading(Axis_map_to[2]);
2528
2529         *ta = 0;
2530         if (Axis_map_to[3] >= 0)
2531                 *ta = joy_get_unscaled_reading(Axis_map_to[3]);
2532
2533         *tr = 0;
2534         if (Axis_map_to[4] >= 0)
2535                 *tr = joy_get_scaled_reading(Axis_map_to[4]);
2536
2537         if (Invert_axis[0])
2538                 *h = -(*h);
2539         if (Invert_axis[1])
2540                 *p = -(*p);
2541         if (Invert_axis[2])
2542                 *b = -(*b);
2543         if (Invert_axis[3])
2544                 *ta = F1_0 - *ta;
2545         if (Invert_axis[4])
2546                 *tr = -(*tr);
2547
2548         return;
2549 }
2550
2551 void control_used(int id)
2552 {
2553         Control_config[id].used = timestamp();
2554 }
2555
2556 void control_config_clear_used_status()
2557 {
2558         int i;
2559
2560         for (i=0; i<CCFG_MAX; i++)
2561                 Control_config[i].used = 0;
2562 }
2563
2564 void control_config_clear()
2565 {
2566         int i;
2567
2568         // Reset keyboard defaults
2569         for (i=0; i<CCFG_MAX; i++)
2570                 Control_config[i].key_id = Control_config[i].joy_id = -1;
2571 }
2572
2573 int control_config_handle_conflict()
2574 {/*
2575         if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {  // we are deleting a conflict
2576                 j = Conflicts[z].joy;
2577                 if ((j >= 0) && (Control_config[j].joy_id < 0))
2578                         j = -1;
2579
2580                 k = Conflicts[z].key;
2581                 if ((k >= 0) && (Control_config[k].key_id < 0))
2582                         k = -1;
2583
2584                 if ((j >= 0) && (k >= 0) && (j != k)) {  // deleting 2 conflicts, each in different actions
2585                         ptr = get_undo_block(2);
2586                         ptr->index[0] = j;
2587                         ptr->list[0] = Control_config[j];
2588                         Control_config[j].joy_id = (short) -1;
2589
2590                         ptr->index[1] = k;
2591                         ptr->list[1] = Control_config[k];
2592                         Control_config[k].key_id = (short) -1;
2593
2594                 } else {  // only 1 action in conflict with selected action (might be both controls, though)
2595                         z = j;
2596                         if (j < 0)
2597                                 z = k;
2598
2599                         Assert(z >= 0);
2600                         ptr = get_undo_block(1);
2601                         ptr->index[0] = z;
2602                         ptr->list[0] = Control_config[z];
2603
2604                         if (j >= 0)
2605                                 Control_config[z].joy_id = (short) -1;
2606
2607                         if (k >= 0)
2608                                 Control_config[z].key_id = (short) -1;
2609                 }*/
2610
2611         return 0;
2612 }