]> icculus.org git repositories - taylor/freespace2.git/blob - src/controlconfig/controlsconfig.cpp
warnings (clang): unused-but-set-variable
[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;
772
773 /*      int shift = -1, alt = -1;
774
775         for (i=0; i<CCFG_MAX; i++) {
776                 Conflicts[i].key = Conflicts[i].joy = -1;
777                 switch (Control_config[i].key_id) {
778                         case SDLK_LSHIFT:
779                         case SDLK_RSHIFT:
780                                 shift = i;
781                                 break;
782
783                         case SDLK_LALT:
784                         case SDLK_RALT:
785                                 alt = i;
786                                 break;
787                 }
788         }
789 */
790
791         for (i=0; i<NUM_TABS; i++)
792                 Conflicts_tabs[i] = 0;
793
794         for (i=0; i<CCFG_MAX-1; i++) {
795                 if (control_config_valid_action(i)) {
796                         for (j=i+1; j<CCFG_MAX; j++) {
797                                 if (control_config_valid_action(j)) {
798                                         if (Control_config[i].key_id >= 0) {
799                                                 a = Control_config[i].key_id;
800                                                 b = Control_config[j].key_id;
801                                                 if (a == b) {
802                                                         Conflicts[i].key = j;
803                                                         Conflicts[j].key = i;
804                                                         Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
805                                                         Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
806                                                 }
807
808                 /*                              if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) {
809                                                         Conflicts[i].key = shift;
810                                                         Conflicts[shift].key = i;
811                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
812                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
813                                                 }
814
815                                                 if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) {
816                                                         Conflicts[j].key = shift;
817                                                         Conflicts[shift].key = j;
818                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
819                                                         Conflicts_tabs[ Control_config[shift].tab ] = 1;
820                                                 }
821
822                                                 if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) {
823                                                         Conflicts[i].key = alt;
824                                                         Conflicts[alt].key = i;
825                                                         Conflicts_tabs[ Control_config[i].tab ] = 1;
826                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
827                                                 }
828
829                                                 if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) {
830                                                         Conflicts[j].key = alt;
831                                                         Conflicts[alt].key = j;
832                                                         Conflicts_tabs[ Control_config[j].tab ] = 1;
833                                                         Conflicts_tabs[ Control_config[alt].tab ] = 1;
834                                                 }*/
835                                         }
836
837                                         if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) {
838                                                 Conflicts[i].joy = j;
839                                                 Conflicts[j].joy = i;
840                                                 Conflicts_tabs[ (int)Control_config[i].tab ] = 1;
841                                                 Conflicts_tabs[ (int)Control_config[j].tab ] = 1;
842                                         }
843                                 }
844                         }
845                 }
846         }
847
848         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
849                 Conflicts_axes[i] = -1;
850
851         for (i=0; i<NUM_JOY_AXIS_ACTIONS-1; i++) {
852                 for (j=i+1; j<NUM_JOY_AXIS_ACTIONS; j++) {
853                         if ((Axis_map_to[i] >= 0) && (Axis_map_to[i] == Axis_map_to[j])) {
854                                 Conflicts_axes[i] = j;
855                                 Conflicts_axes[j] = i;
856                                 Conflicts_tabs[SHIP_TAB] = 1;
857                         }
858                 }
859         }
860 }
861
862 // do list setup required prior to rendering and checking for the controls listing.  Called when list changes
863 void control_config_list_prepare()
864 {
865         int j, y, z;
866         int font_height = gr_get_font_height();
867
868         Num_cc_lines = y = z = 0;
869         while (z < CCFG_MAX) {
870                 if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) {
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         SDL_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                 SDL_assert(ptr->index);
920                 ptr->list = (config_item *) malloc( sizeof(config_item) * size );
921                 SDL_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;
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         ptr = get_undo_block(1);
1073         ptr->index[0] = z;
1074         ptr->list[0] = Control_config[z];
1075
1076         if (Selected_item && (Control_config[z].joy_id >= 0))  // if not just key selected (which would be 0)
1077                 Control_config[z].joy_id = (short) -1;
1078
1079         if ((Selected_item != 1) && (Control_config[z].key_id >= 0))  // if not just joy button selected (1)
1080                 Control_config[z].key_id = -1;
1081
1082         control_config_conflict_check();
1083         control_config_list_prepare();
1084         gamesnd_play_iface(SND_USER_SELECT);
1085         Selected_item = -1;
1086         return 0;
1087 }
1088
1089 int control_config_clear_other()
1090 {
1091         int z, i, j, total = 0;
1092         config_item_undo *ptr;
1093
1094         if (Selected_line < 0) {
1095                 gamesnd_play_iface(SND_GENERAL_FAIL);
1096                 return -1;
1097         }
1098
1099         z = Cc_lines[Selected_line].cc_index;
1100         if (z & JOY_AXIS) {
1101                 config_item item;
1102
1103                 z &= ~JOY_AXIS;
1104                 if (Axis_map_to[z] < 0) {
1105                         gamesnd_play_iface(SND_GENERAL_FAIL);
1106                         return -1;
1107                 }
1108
1109                 for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1110                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z))
1111                                 total++;
1112
1113                 if (!total) {
1114                         gamesnd_play_iface(SND_GENERAL_FAIL);
1115                         return -1;
1116                 }
1117
1118                 ptr = get_undo_block(total);
1119                 for (i=j=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1120                         if ((Axis_map_to[i] == Axis_map_to[z]) && (i != z)) {
1121                                 item.joy_id = (short) Axis_map_to[i];
1122                                 item.used = Invert_axis[i];
1123
1124                                 ptr->index[j] = i | JOY_AXIS;
1125                                 ptr->list[j] = item;
1126                                 j++;
1127
1128                                 Axis_map_to[i] = -1;
1129                         }
1130
1131                 control_config_conflict_check();
1132                 control_config_list_prepare();
1133                 gamesnd_play_iface(SND_USER_SELECT);
1134                 return 0;
1135         }
1136
1137         for (i=0; i<CCFG_MAX; i++)
1138                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1139                         if (i != z)
1140                                 total++;
1141
1142         if (!total) {
1143                 gamesnd_play_iface(SND_GENERAL_FAIL);
1144                 return -1;
1145         }
1146
1147         if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) {
1148                 gamesnd_play_iface(SND_GENERAL_FAIL);
1149                 return -1;
1150         }
1151
1152         // now, back up the old bindings so we can undo if we want to
1153         ptr = get_undo_block(total);
1154         for (i=j=0; i<CCFG_MAX; i++)
1155                 if ( (Control_config[i].key_id == Control_config[z].key_id) || (Control_config[i].joy_id == Control_config[z].joy_id) )
1156                         if (i != z) {
1157                                 ptr->index[j] = i;
1158                                 ptr->list[j] = Control_config[i];
1159                                 j++;
1160
1161                                 if (Control_config[i].key_id == Control_config[z].key_id)
1162                                         Control_config[i].key_id = -1;
1163                                 if (Control_config[i].joy_id == Control_config[z].joy_id)
1164                                         Control_config[i].joy_id = (short) -1;
1165                         }
1166
1167         control_config_conflict_check();
1168         control_config_list_prepare();
1169         gamesnd_play_iface(SND_USER_SELECT);
1170         return 0;
1171 }
1172
1173 int control_config_clear_all()
1174 {
1175         int i, j, total = 0;
1176         config_item_undo *ptr;
1177
1178         // first, determine how many bindings need to be changed
1179         for (i=0; i<CCFG_MAX; i++)
1180                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0))
1181                         total++;
1182
1183         if (!total) {
1184                 gamesnd_play_iface(SND_GENERAL_FAIL);
1185                 return -1;
1186         }
1187
1188         // now, back up the old bindings so we can undo if we want to
1189         ptr = get_undo_block(total);
1190         for (i=j=0; i<CCFG_MAX; i++) {
1191                 if ((Control_config[i].key_id >= 0) || (Control_config[i].joy_id >= 0)) {
1192                         ptr->index[j] = i;
1193                         ptr->list[j] = Control_config[i];
1194                         j++;
1195                 }
1196         }
1197
1198         SDL_assert(j == total);
1199         for (i=0; i<CCFG_MAX; i++) {
1200                 Control_config[i].key_id = Control_config[i].joy_id = -1;
1201         }
1202
1203         control_config_conflict_check();
1204         control_config_list_prepare();
1205         gamesnd_play_iface(SND_RESET_PRESSED);
1206         return 0;
1207 }
1208
1209 int control_config_axis_default(int axis)
1210 {
1211         SDL_assert(axis >= 0);
1212
1213         if ( axis > 1 ) {
1214                 if (Axis_map_to_defaults[axis] < 0)
1215                         return -1;
1216
1217                 if ( !joy_axis_valid(Axis_map_to_defaults[axis]) )
1218                         return -1;
1219         }
1220
1221         return Axis_map_to_defaults[axis];
1222 }
1223
1224 int control_config_do_reset()
1225 {
1226         int i, j, total = 0;
1227         config_item_undo *ptr;
1228         config_item item;
1229
1230         // first, determine how many bindings need to be changed
1231         for (i=0; i<CCFG_MAX; i++)
1232                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default))
1233                         total++;
1234
1235         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1236                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i]))
1237                         total++;
1238
1239         if (!total) {
1240                 gamesnd_play_iface(SND_GENERAL_FAIL);
1241                 return -1;
1242         }
1243
1244         // now, back up the old bindings so we can undo if we want to
1245         ptr = get_undo_block(total);
1246         for (i=j=0; i<CCFG_MAX; i++) {
1247                 if ((Control_config[i].key_id != Control_config[i].key_default) || (Control_config[i].joy_id != Control_config[i].joy_default)) {
1248                         ptr->index[j] = i;
1249                         ptr->list[j] = Control_config[i];
1250                         j++;
1251                 }
1252         }
1253
1254         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++)
1255                 if ((Axis_map_to[i] != control_config_axis_default(i)) || (Invert_axis[i] != Invert_axis_defaults[i])) {
1256                         item.joy_id = (short) Axis_map_to[i];
1257                         item.used = Invert_axis[i];
1258
1259                         ptr->index[j] = i | JOY_AXIS;
1260                         ptr->list[j] = item;
1261                         j++;
1262                 }
1263
1264         SDL_assert(j == total);
1265         control_config_reset_defaults();
1266         control_config_conflict_check();
1267         control_config_list_prepare();
1268         gamesnd_play_iface(SND_RESET_PRESSED);
1269         return 0;
1270 }
1271
1272 // This sets all the controls to their default values
1273 void control_config_reset_defaults()
1274 {
1275         int i;
1276
1277         // Reset keyboard defaults
1278         for (i=0; i<CCFG_MAX; i++) {
1279                 Control_config[i].key_id = Control_config[i].key_default;
1280                 Control_config[i].joy_id = Control_config[i].joy_default;
1281         }
1282
1283         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1284                 Axis_map_to[i] = control_config_axis_default(i);
1285                 Invert_axis[i] = Invert_axis_defaults[i];
1286         }
1287 }
1288
1289 void control_config_scroll_screen_up()
1290 {
1291         if (Scroll_offset) {
1292                 Scroll_offset--;
1293                 SDL_assert(Selected_line > Scroll_offset);
1294                 while (!cc_line_query_visible(Selected_line))
1295                         Selected_line--;
1296
1297                 Selected_item = -1;
1298                 gamesnd_play_iface(SND_SCROLL);
1299
1300         } else
1301                 gamesnd_play_iface(SND_GENERAL_FAIL);
1302 }
1303
1304 void control_config_scroll_line_up()
1305 {
1306         if (Selected_line) {
1307                 Selected_line--;
1308                 if (Selected_line < Scroll_offset)
1309                         Scroll_offset = Selected_line;
1310
1311                 Selected_item = -1;
1312                 gamesnd_play_iface(SND_SCROLL);
1313
1314         } else
1315                 gamesnd_play_iface(SND_GENERAL_FAIL);
1316 }
1317
1318 void control_config_scroll_screen_down()
1319 {
1320         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]) {
1321                 Scroll_offset++;
1322                 while (!cc_line_query_visible(Selected_line)) {
1323                         Selected_line++;
1324                         SDL_assert(Selected_line < Num_cc_lines);
1325                 }
1326
1327                 Selected_item = -1;
1328                 gamesnd_play_iface(SND_SCROLL);
1329
1330         } else
1331                 gamesnd_play_iface(SND_GENERAL_FAIL);
1332 }
1333
1334 void control_config_scroll_line_down()
1335 {
1336         if (Selected_line < Num_cc_lines - 1) {
1337                 Selected_line++;
1338                 SDL_assert(Selected_line > Scroll_offset);
1339                 while (!cc_line_query_visible(Selected_line))
1340                         Scroll_offset++;
1341
1342                 Selected_item = -1;
1343                 gamesnd_play_iface(SND_SCROLL);
1344
1345         } else
1346                 gamesnd_play_iface(SND_GENERAL_FAIL);
1347 }
1348
1349 void control_config_toggle_modifier(int bit)
1350 {
1351         int k, z;
1352
1353         z = Cc_lines[Selected_line].cc_index;
1354         SDL_assert(!(z & JOY_AXIS));
1355         k = Control_config[z].key_id;
1356         if (k < 0) {
1357                 gamesnd_play_iface(SND_GENERAL_FAIL);
1358                 return;
1359         }
1360
1361         control_config_bind_key(z, k ^ bit);
1362         control_config_conflict_check();
1363         gamesnd_play_iface(SND_USER_SELECT);
1364 }
1365
1366 void control_config_toggle_invert()
1367 {
1368         int z;
1369
1370         z = Cc_lines[Selected_line].cc_index;
1371         SDL_assert(z & JOY_AXIS);
1372         z &= ~JOY_AXIS;
1373         control_config_save_axis_undo(z);
1374         Invert_axis[z] = !Invert_axis[z];
1375 }
1376
1377 void control_config_do_bind()
1378 {
1379         int i;
1380
1381         game_flush();
1382 //      if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) {
1383         if (Selected_line < 0) {
1384                 gamesnd_play_iface(SND_GENERAL_FAIL);
1385                 return;
1386         }
1387
1388         for (i=0; i<NUM_BUTTONS; i++)
1389                 if (i != CANCEL_BUTTON) {
1390                         CC_Buttons[gr_screen.res][i].button.reset_status();
1391                         CC_Buttons[gr_screen.res][i].button.disable();
1392                 }
1393
1394         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1395         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1396
1397         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1398                 joy_down_count(i);  // clear checking status of all joystick buttons
1399         }
1400
1401         control_config_detect_axis_reset();
1402
1403         Binding_mode = 1;
1404         Bind_time = timer_get_milliseconds();
1405         Search_mode = 0;
1406         Last_key = -1;
1407         Axis_override = -1;
1408         gamesnd_play_iface(SND_USER_SELECT);
1409 }
1410
1411 void control_config_do_search()
1412 {
1413         int i;
1414
1415         for (i=0; i<NUM_BUTTONS; i++){
1416                 if (i != CANCEL_BUTTON) {
1417                         CC_Buttons[gr_screen.res][i].button.reset_status();
1418                         CC_Buttons[gr_screen.res][i].button.disable();
1419                 }
1420         }
1421
1422         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.enable();
1423         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(SDLK_ESCAPE);
1424
1425         for (i=0; i<JOY_TOTAL_BUTTONS; i++){
1426                 joy_down_count(i);  // clear checking status of all joystick buttons
1427         }
1428
1429         Binding_mode = 0;
1430         Search_mode = 1;
1431         Last_key = -1;
1432         gamesnd_play_iface(SND_USER_SELECT);
1433 }
1434
1435 void control_config_do_cancel(int fail = 0)
1436 {
1437         int i;
1438
1439         game_flush();
1440
1441         for (i=0; i<NUM_BUTTONS; i++){
1442                 if ( (i != CANCEL_BUTTON) && (i != INVERT_AXIS) ){
1443                         CC_Buttons[gr_screen.res][i].button.enable();
1444                 }
1445         }
1446
1447         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.reset_status();
1448         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1449         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.set_hotkey(-1);
1450         CC_Buttons[gr_screen.res][BIND_BUTTON].button.reset_status();
1451         CC_Buttons[gr_screen.res][SEARCH_MODE].button.reset_status();
1452
1453         Binding_mode = Search_mode = 0;
1454         if (fail){
1455                 gamesnd_play_iface(SND_GENERAL_FAIL);
1456         } else {
1457                 gamesnd_play_iface(SND_USER_SELECT);
1458         }
1459 }
1460
1461 int control_config_accept()
1462 {
1463         int i;
1464
1465         for (i=0; i<NUM_TABS; i++)
1466                 if (Conflicts_tabs[i])
1467                         break;
1468
1469         if (i < NUM_TABS) {
1470                 gamesnd_play_iface(SND_GENERAL_FAIL);
1471                 return -1;
1472         }
1473
1474         hud_squadmsg_save_keys();  // rebuild map for saving/restoring keys in squadmsg mode
1475         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1476         gamesnd_play_iface(SND_COMMIT_PRESSED);
1477         return 0;
1478 }
1479
1480 void control_config_cancel_exit()
1481 {
1482         int i;
1483
1484         for (i=0; i<CCFG_MAX; i++)
1485                 Control_config[i] = Control_config_backup[i];
1486
1487         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1488 }
1489
1490 void control_config_button_pressed(int n)
1491 {
1492         switch (n) {
1493                 case TARGET_TAB:
1494                 case SHIP_TAB:
1495                 case WEAPON_TAB:
1496                 case COMPUTER_TAB:
1497                         Tab = n;
1498                         Scroll_offset = Selected_line = 0;
1499                         control_config_list_prepare();
1500                         gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
1501                         break;
1502
1503                 case BIND_BUTTON:
1504                         control_config_do_bind();
1505                         break;
1506
1507                 case SEARCH_MODE:
1508                         control_config_do_search();
1509                         break;
1510
1511                 case SHIFT_TOGGLE:
1512                         control_config_toggle_modifier(KEY_SHIFTED);
1513                         gamesnd_play_iface(SND_USER_SELECT);
1514                         break;
1515
1516                 case ALT_TOGGLE:
1517                         control_config_toggle_modifier(KEY_ALTED);
1518                         gamesnd_play_iface(SND_USER_SELECT);
1519                         break;
1520
1521                 case INVERT_AXIS:
1522                         control_config_toggle_invert();
1523                         gamesnd_play_iface(SND_USER_SELECT);
1524                         break;
1525
1526                 case SCROLL_UP_BUTTON:
1527                         control_config_scroll_screen_up();
1528                         break;
1529
1530                 case SCROLL_DOWN_BUTTON:
1531                         control_config_scroll_screen_down();
1532                         break;
1533
1534                 case ACCEPT_BUTTON:
1535                         control_config_accept();
1536                         break;
1537
1538                 case CLEAR_BUTTON:
1539                         control_config_remove_binding();
1540                         break;
1541
1542                 case HELP_BUTTON:
1543                         launch_context_help();
1544                         gamesnd_play_iface(SND_HELP_PRESSED);
1545                         break;
1546
1547                 case RESET_BUTTON:
1548                         control_config_do_reset();
1549                         break;
1550
1551                 case UNDO_BUTTON:
1552                         control_config_undo_last();
1553                         break;
1554
1555                 case CANCEL_BUTTON:
1556                         control_config_do_cancel();
1557                         break;
1558
1559                 case CLEAR_OTHER_BUTTON:
1560                         control_config_clear_other();
1561                         break;
1562
1563                 case CLEAR_ALL_BUTTON:
1564                         control_config_clear_all();
1565                         break;          
1566         }
1567 }
1568
1569 const char *control_config_tooltip_handler(const char *str)
1570 {
1571         int i;
1572
1573         if (!SDL_strcasecmp(str, NOX("@conflict"))) {
1574                 for (i=0; i<NUM_TABS; i++) {
1575                         if (Conflicts_tabs[i])
1576                                 return XSTR( "Conflict!", 205);
1577                 }
1578         }
1579
1580         return NULL;
1581 }
1582
1583 void control_config_init()
1584 {
1585         int i;
1586         ui_button_info *b;
1587
1588         // make backup of all controls
1589         for (i=0; i<CCFG_MAX; i++)
1590                 Control_config_backup[i] = Control_config[i];
1591
1592         common_set_interface_palette(NOX("ControlConfigPalette"));  // set the interface palette
1593         Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1594         Ui_window.set_mask_bmap(Conflict_background_bitmap_mask_fname[gr_screen.res]);
1595         Ui_window.tooltip_handler = control_config_tooltip_handler;
1596
1597         // load in help overlay bitmap  
1598         help_overlay_load(CONTROL_CONFIG_OVERLAY);
1599         help_overlay_set_state(CONTROL_CONFIG_OVERLAY,0);
1600
1601         // reset conflict flashing
1602         Conflict_stamp = -1;
1603
1604 #ifdef MAKE_FS1
1605         hud_anim_init(&Conflict_warning_anim, Conflict_warning_coords[gr_screen.res][0], Conflict_warning_coords[gr_screen.res][1], NOX("ConflictFlash"));
1606         hud_anim_load(&Conflict_warning_anim);
1607 #endif
1608
1609         for (i=0; i<NUM_BUTTONS; i++) {
1610                 b = &CC_Buttons[gr_screen.res][i];
1611
1612                 if (b->hotspot < 0) {  // temporary
1613                         b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1);  // temporary
1614                         b->button.set_highlight_action(common_play_highlight_sound);
1615                         continue;
1616                 }
1617
1618                 b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1);
1619
1620                 // set up callback for when a mouse first goes over a button
1621                 b->button.set_highlight_action(common_play_highlight_sound);            
1622                 if (i<4) {
1623                         b->button.set_bmaps(b->filename, 5, 1);         // a bit of a hack here, but buttons 0-3 need 4 frames loaded
1624                 } else {
1625                         b->button.set_bmaps(b->filename);
1626                 }
1627                 b->button.link_hotspot(b->hotspot);
1628         }       
1629
1630 #ifndef MAKE_FS1
1631         // create all text
1632         for(i=0; i<CC_NUM_TEXT; i++){
1633                 Ui_window.add_XSTR(&CC_text[gr_screen.res][i]);
1634         }
1635 #endif
1636
1637         for (i=0; i<LIST_BUTTONS_MAX; i++) {
1638                 List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
1639                 List_buttons[i].hide();
1640                 List_buttons[i].disable();
1641         }
1642
1643         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
1644         CC_Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(SDLK_PAGEUP);
1645         CC_Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(SDLK_PAGEDOWN);
1646         CC_Buttons[gr_screen.res][BIND_BUTTON].button.set_hotkey(SDLK_RETURN);
1647         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_DELETE);
1648         CC_Buttons[gr_screen.res][UNDO_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_z);
1649         CC_Buttons[gr_screen.res][CLEAR_BUTTON].button.set_hotkey(SDLK_DELETE);
1650         CC_Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_RETURN);
1651         CC_Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(SDLK_F1);
1652         CC_Buttons[gr_screen.res][RESET_BUTTON].button.set_hotkey(KEY_CTRLED | SDLK_r);
1653         CC_Buttons[gr_screen.res][INVERT_AXIS].button.set_hotkey(SDLK_i);
1654
1655         CC_Buttons[gr_screen.res][CANCEL_BUTTON].button.disable();
1656         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.disable();
1657
1658         Background_bitmap = bm_load(Conflict_background_bitmap_fname[gr_screen.res]);   
1659
1660         Scroll_offset = Selected_line = 0;
1661         Config_item_undo = NULL;
1662         control_config_conflict_check();
1663
1664         // setup strings                                        
1665         Joy_axis_action_text[0] = strdup(XSTR("Turn (Yaw) Axis", 1016));
1666         Joy_axis_action_text[1] = strdup(XSTR("Pitch Axis", 1017));
1667         Joy_axis_action_text[2] = strdup(XSTR("Bank Axis", 1018));
1668         Joy_axis_action_text[3] = strdup(XSTR("Absolute Throttle Axis", 1019));
1669         Joy_axis_action_text[4] = strdup(XSTR("Relative Throttle Axis", 1020));
1670         Joy_axis_text[0] = strdup(XSTR("Joystick/Mouse X Axis", 1021));
1671         Joy_axis_text[1] = strdup(XSTR("Joystick/Mouse Y Axis", 1022));
1672         Joy_axis_text[2] = strdup(XSTR("Joystick Z Axis", 1023));
1673         Joy_axis_text[3] = strdup(XSTR("Joystick rX Axis", 1024));
1674         Joy_axis_text[4] = strdup(XSTR("Joystick rY Axis", 1025));
1675         Joy_axis_text[5] = strdup(XSTR("Joystick rZ Axis", 1026));
1676         Mouse_button_text[0] = strdup("");
1677         Mouse_button_text[1] = strdup(XSTR("Left Button", 1027));
1678         Mouse_button_text[2] = strdup(XSTR("Right Button", 1028));
1679         Mouse_button_text[3] = strdup(XSTR("Mid Button", 1029));
1680         Mouse_button_text[4] = strdup("");
1681         Mouse_axis_text[0] = strdup(XSTR("L/R", 1030));
1682         Mouse_axis_text[1] = strdup(XSTR("U/B", 1031));
1683         Invert_text[0] = strdup(XSTR("N", 1032));
1684         Invert_text[1] = strdup(XSTR("Y", 1033));
1685
1686         control_config_list_prepare();
1687 }
1688
1689 void control_config_close()
1690 {
1691         int idx;
1692         
1693         while (Config_item_undo){
1694                 free_undo_block();
1695         }
1696
1697         // unload the overlay bitmap
1698         help_overlay_unload(CONTROL_CONFIG_OVERLAY);
1699         
1700         if (Background_bitmap){
1701                 bm_unload(Background_bitmap);
1702         }
1703
1704 #ifdef MAKE_FS1
1705         hud_anim_release(&Conflict_warning_anim);
1706 #endif
1707
1708         Ui_window.destroy();
1709         common_free_interface_palette();                // restore game palette
1710         hud_squadmsg_save_keys();                               // rebuild map for saving/restoring keys in squadmsg mode
1711         game_flush();
1712         write_pilot_file();
1713
1714         // free strings 
1715         for(idx=0; idx<NUM_JOY_AXIS_ACTIONS; idx++){
1716                 if(Joy_axis_action_text[idx] != NULL){
1717                         free(Joy_axis_action_text[idx]);
1718                         Joy_axis_action_text[idx] = NULL;
1719                 }
1720         }
1721         for(idx=0; idx<NUM_AXIS_TEXT; idx++){
1722                 if(Joy_axis_text[idx] != NULL){
1723                         free(Joy_axis_text[idx]);
1724                         Joy_axis_text[idx] = NULL;
1725                 }
1726         }
1727         for(idx=0; idx<NUM_MOUSE_TEXT; idx++){
1728                 if(Mouse_button_text[idx] != NULL){
1729                         free(Mouse_button_text[idx]);
1730                         Mouse_button_text[idx] = NULL;
1731                 }
1732         }
1733         for(idx=0; idx<NUM_MOUSE_AXIS_TEXT; idx++){
1734                 if(Mouse_axis_text[idx] != NULL){
1735                         free(Mouse_axis_text[idx]);
1736                         Mouse_axis_text[idx] = NULL;
1737                 }
1738         }
1739         for(idx=0; idx<NUM_INVERT_TEXT; idx++){
1740                 if(Invert_text[idx] != NULL){
1741                         free(Invert_text[idx]);
1742                         Invert_text[idx] = NULL;
1743                 }
1744         }
1745 }
1746
1747 void control_config_do_frame(float frametime)
1748 {
1749         char buf[256];
1750         int i, j, k, w, x, y, z, line, conflict;
1751         int font_height = gr_get_font_height();
1752         int select_tease_line = -1;  // line mouse is down on, but won't be selected until button released
1753         static float timer = 0.0f;
1754         color *c;
1755         static int bound_timestamp = 0;
1756         static char bound_string[40];
1757         
1758         timer += frametime;
1759
1760         if (Binding_mode) {
1761                 if (Cc_lines[Selected_line].cc_index & JOY_AXIS) {
1762                         int bind = 0;
1763
1764                         z = Cc_lines[Selected_line].cc_index & ~JOY_AXIS;
1765                         i = control_config_detect_axis();
1766                         if (i >= 0) {
1767                                 Axis_override = i;
1768                                 bind = 1;
1769                         }
1770
1771                         k = game_poll();
1772                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1773                         Ui_window.process(0);
1774
1775                         if (k == SDLK_ESCAPE) {
1776                                 strcpy(bound_string, XSTR( "Canceled", 206));
1777                                 bound_timestamp = timestamp(2500);
1778                                 control_config_do_cancel();
1779
1780                         } else {
1781                                 if (k == SDLK_RETURN)
1782                                         bind = 1;
1783
1784                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1785                                         if (joy_down_count(i))
1786                                                 bind = 1;
1787
1788                                 if (bind) {
1789                                         if (Axis_override >= 0) {
1790                                                 control_config_bind_axis(z, Axis_override);
1791                                                 strcpy(bound_string, Joy_axis_text[Axis_override]);
1792                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1793                                                 bound_timestamp = timestamp(2500);
1794                                                 control_config_conflict_check();
1795                                                 control_config_list_prepare();
1796                                                 control_config_do_cancel();
1797
1798                                         } else {
1799                                                 control_config_do_cancel(1);
1800                                         }
1801                                 }
1802                         }
1803
1804                 } else {
1805                         if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1806                                 CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1807                                 Ui_window.set_ignore_gadgets(1);
1808                         }
1809
1810                         k = game_poll();
1811                         Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1812                         Ui_window.process(0);
1813
1814                         if ( (k > 0) || B1_JUST_RELEASED ) {
1815                                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1816                                         help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1817                                         Ui_window.set_ignore_gadgets(0);
1818                                         k = 0;
1819                                 }
1820                         }
1821
1822                         if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1823                                 Ui_window.set_ignore_gadgets(0);
1824                         }
1825
1826                         if (k == SDLK_ESCAPE) {
1827                                 strcpy(bound_string, XSTR( "Canceled", 206));
1828                                 bound_timestamp = timestamp(2500);
1829                                 control_config_do_cancel();
1830
1831                         } else {
1832                                 switch (k & KEY_MASK) {
1833                                         case SDLK_LSHIFT:
1834                                         case SDLK_RSHIFT:
1835                                         case SDLK_LALT:
1836                                         case SDLK_RALT:
1837                                                 Last_key = k & KEY_MASK;
1838                                                 k = 0;
1839                                                 break;
1840                                 }
1841
1842                                 if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED)  // a special hack just for Mike K.
1843                                         if ( (Last_key >= 0) && (k <= 0) && !key_pressed(Last_key) )
1844                                                 k = Last_key;
1845
1846                                 if ((k > 0) && !Config_allowed[k & KEY_MASK]) {
1847                                         popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key.  Please try again.", 207));
1848                                         k = 0;
1849                                 }
1850
1851                                 k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1852                                 if (k > 0) {
1853                                         z = Cc_lines[Selected_line].cc_index;
1854                                         SDL_assert(!(z & JOY_AXIS));
1855                                         control_config_bind_key(z, k);
1856
1857                                         strcpy(bound_string, textify_scancode(k));
1858                                         gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1859                                         bound_timestamp = timestamp(2500);
1860                                         control_config_conflict_check();
1861                                         control_config_list_prepare();
1862                                         control_config_do_cancel();
1863                                 }
1864
1865                                 for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1866                                         if (joy_down_count(i)) {
1867                                                 z = Cc_lines[Selected_line].cc_index;
1868                                                 SDL_assert(!(z & JOY_AXIS));
1869                                                 control_config_bind_joy(z, i);
1870
1871                                                 strcpy(bound_string, Joy_button_text[i]);
1872                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1873                                                 bound_timestamp = timestamp(2500);
1874                                                 control_config_conflict_check();
1875                                                 control_config_list_prepare();
1876                                                 control_config_do_cancel();
1877                                                 break;
1878                                         }
1879
1880                                 if (Bind_time + 375 < timer_get_milliseconds()) {
1881                                         for (i=0; i<NUM_BUTTONS; i++){
1882                                                 if ( (CC_Buttons[gr_screen.res][i].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][i].button.enabled()) ){
1883                                                         break;
1884                                                 }
1885                                         }
1886
1887                                         if (i == NUM_BUTTONS) {  // no buttons pressed
1888                                                 for (i=0; i<MOUSE_NUM_BUTTONS; i++)
1889                                                         if (mouse_down(1 << i)) {
1890                                                                 z = Cc_lines[Selected_line].cc_index;
1891                                                                 SDL_assert(!(z & JOY_AXIS));
1892                                                                 control_config_bind_joy(z, i);
1893
1894                                                                 strcpy(bound_string, Joy_button_text[i]);
1895                                                                 gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
1896                                                                 bound_timestamp = timestamp(2500);
1897                                                                 control_config_conflict_check();
1898                                                                 control_config_list_prepare();
1899                                                                 control_config_do_cancel();
1900                                                                 for (j=0; j<NUM_BUTTONS; j++){
1901                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1902                                                                 }
1903
1904                                                                 break;
1905                                                         }
1906                                         }
1907                                 }
1908                         }
1909                 }
1910
1911         } else if (Search_mode) {
1912                 if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) {
1913                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
1914                         Ui_window.set_ignore_gadgets(1);
1915                 }
1916
1917                 k = game_poll();
1918                 Ui_window.use_hack_to_get_around_stupid_problem_flag = 1;
1919                 Ui_window.process(0);
1920
1921                 if ( (k > 0) || B1_JUST_RELEASED ) {
1922                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1923                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
1924                                 Ui_window.set_ignore_gadgets(0);
1925                                 k = 0;
1926                         }
1927                 }
1928
1929                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
1930                         Ui_window.set_ignore_gadgets(0);
1931                 }
1932
1933                 if (k == SDLK_ESCAPE) {
1934                         control_config_do_cancel();
1935
1936                 } else {
1937                         if ((k > 0) && !Config_allowed[k & KEY_MASK])
1938                                 k = 0;
1939
1940                         k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED);
1941                         z = -1;
1942                         if (k > 0) {
1943                                 for (i=0; i<CCFG_MAX; i++)
1944                                         if (Control_config[i].key_id == k) {
1945                                                 z = i;
1946                                                 break;
1947                                         }
1948                         }
1949
1950                         for (i=0; i<JOY_TOTAL_BUTTONS; i++)
1951                                 if (joy_down_count(i)) {
1952                                         j = i;
1953                                         for (i=0; i<CCFG_MAX; i++)
1954                                                 if (Control_config[i].joy_id == j) {
1955                                                         z = i;
1956                                                         break;
1957                                                 }
1958
1959                                         break;
1960                                 }
1961
1962                         // check if not on enabled button
1963                         for (j=0; j<NUM_BUTTONS; j++){
1964                                 if ( (CC_Buttons[gr_screen.res][j].button.is_mouse_on()) && (CC_Buttons[gr_screen.res][j].button.enabled()) ){
1965                                         break;
1966                                 }
1967                         }
1968
1969                         if (j == NUM_BUTTONS) {  // no buttons pressed
1970                                 for (j=0; j<MOUSE_NUM_BUTTONS; j++)
1971                                         if (mouse_down(1 << j)) {
1972                                                 for (i=0; i<CCFG_MAX; i++)
1973                                                         if (Control_config[i].joy_id == j) {
1974                                                                 z = i;
1975                                                                 for (j=0; j<NUM_BUTTONS; j++){
1976                                                                         CC_Buttons[gr_screen.res][j].button.reset();
1977                                                                 }
1978                                                                 break;
1979                                                         }
1980
1981                                                 break;
1982                                         }
1983                         }
1984
1985                         if (z >= 0) {
1986                                 Tab = Control_config[z].tab;
1987                                 control_config_list_prepare();
1988                                 Selected_line = Scroll_offset = 0;
1989                                 for (i=0; i<Num_cc_lines; i++)
1990                                         if (Cc_lines[i].cc_index == z) {
1991                                                 Selected_line = i;
1992                                                 break;
1993                                         }
1994
1995                                 while (!cc_line_query_visible(Selected_line)) {
1996                                         Scroll_offset++;
1997                                         SDL_assert(Scroll_offset < Num_cc_lines);
1998                                 }
1999                         }
2000                 }
2001
2002         } else {
2003                 z = Cc_lines[Selected_line].cc_index & JOY_AXIS;
2004                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(!z);
2005                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(!z);
2006                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.enable(z);
2007
2008                 if (!z) {
2009                         z = Cc_lines[Selected_line].cc_index;
2010                         k = Control_config[z].key_id;
2011                         if ( (k == SDLK_LALT) || (k == SDLK_RALT) || (k == SDLK_LSHIFT) || (k == SDLK_RSHIFT) ) {
2012                                 CC_Buttons[gr_screen.res][ALT_TOGGLE].button.enable(0);
2013                                 CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.enable(0);
2014                         }
2015                 }
2016
2017                 CC_Buttons[gr_screen.res][UNDO_BUTTON].button.enable(Config_item_undo != NULL);
2018
2019                 if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2020                         CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2021                         Ui_window.set_ignore_gadgets(1);
2022                 }
2023
2024                 k = Ui_window.process();
2025
2026                 if ( (k > 0) || B1_JUST_RELEASED ) {
2027                         if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2028                                 help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0);
2029                                 Ui_window.set_ignore_gadgets(0);
2030                                 k = 0;
2031                         }
2032                 }
2033
2034                 if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) {
2035                         Ui_window.set_ignore_gadgets(0);
2036                 }
2037
2038                 switch (k) {
2039                         case SDLK_DOWN:  // select next line
2040                                 control_config_scroll_line_down();
2041                                 break;
2042
2043                         case SDLK_UP:  // select previous line
2044                                 control_config_scroll_line_up();
2045                                 break;
2046
2047                         case KEY_SHIFTED | SDLK_TAB:  // activate previous tab
2048                                 Tab--;
2049                                 if (Tab < 0)
2050                                         Tab = NUM_TABS - 1;
2051
2052                                 Scroll_offset = Selected_line = 0;
2053                                 control_config_list_prepare();
2054                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2055                                 break;
2056
2057                         case SDLK_TAB:  // activate next tab
2058                                 Tab++;
2059                                 if (Tab >= NUM_TABS)
2060                                         Tab = 0;
2061
2062                                 Scroll_offset = Selected_line = 0;
2063                                 control_config_list_prepare();
2064                                 gamesnd_play_iface(SND_SCREEN_MODE_PRESSED);
2065                                 break;
2066
2067                         case SDLK_LEFT:
2068                                 Selected_item--;
2069                                 if (Selected_item == -2) {
2070                                         Selected_item = 1;
2071                                         if (Cc_lines[Selected_line].jw < 1) {
2072                                                 Selected_item = 0;
2073                                                 if (Cc_lines[Selected_line].kw < 1)
2074                                                         Selected_item = -1;
2075                                         }
2076                                 }
2077
2078                                 gamesnd_play_iface(SND_SCROLL);
2079                                 break;
2080
2081                         case SDLK_RIGHT:
2082                                 Selected_item++;
2083                                 if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1))
2084                                         Selected_item = -1;
2085                                 else if (!Selected_item && (Cc_lines[Selected_line].kw < 1))
2086                                         Selected_item = -1;
2087                                 else if (Selected_item > 1)
2088                                         Selected_item = -1;
2089
2090                                 gamesnd_play_iface(SND_SCROLL);
2091                                 break;
2092
2093                         case SDLK_BACKSPACE:  // undo
2094                                 control_config_undo_last();
2095                                 break;
2096
2097                         case SDLK_ESCAPE:
2098                                 control_config_cancel_exit();
2099                                 break;
2100                 }       // end switch
2101         }
2102
2103         for (i=0; i<NUM_BUTTONS; i++){
2104                 if (CC_Buttons[gr_screen.res][i].button.pressed()){
2105                         control_config_button_pressed(i);
2106                 }
2107         }
2108
2109         for (i=0; i<LIST_BUTTONS_MAX; i++) {
2110                 if (List_buttons[i].is_mouse_on())
2111                         select_tease_line = i + Scroll_offset;
2112         
2113                 if (List_buttons[i].pressed()) {
2114                         Selected_line = i + Scroll_offset;
2115                         Selected_item = -1;
2116                         List_buttons[i].get_mouse_pos(&x, &y);
2117                         if ((x >= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw))
2118                                 Selected_item = 0;
2119
2120                         if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw))
2121                                 Selected_item = 1;
2122
2123                         gamesnd_play_iface(SND_USER_SELECT);
2124                 }
2125
2126                 if (List_buttons[i].double_clicked())
2127                         control_config_do_bind();
2128         }
2129
2130         GR_MAYBE_CLEAR_RES(Background_bitmap);
2131         if (Background_bitmap >= 0) {
2132                 gr_set_bitmap(Background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
2133                 gr_bitmap(0, 0);
2134         } 
2135
2136         // highlight tab with conflict
2137         Ui_window.draw();
2138         for (i=z=0; i<NUM_TABS; i++) {
2139                 if (Conflicts_tabs[i]) {
2140                         CC_Buttons[gr_screen.res][i].button.draw_forced(4);
2141                         z++;
2142                 }
2143         }
2144
2145         if (z) {
2146                 // maybe switch from bright to normal
2147                 if((Conflict_stamp == -1) || timestamp_elapsed(Conflict_stamp)){
2148                         Conflict_bright = !Conflict_bright;
2149
2150                         Conflict_stamp = timestamp(CONFLICT_FLASH_TIME);
2151                 }
2152
2153 #ifndef MAKE_FS1
2154                 // set color and font
2155                 gr_set_font(FONT2);
2156                 if(Conflict_bright){
2157                         gr_set_color_fast(&Color_bright_red);
2158                 } else {
2159                         gr_set_color_fast(&Color_red);
2160                 }
2161
2162                 // setup the conflict string
2163                 char conflict_str[512] = "";
2164                 strncpy(conflict_str, XSTR("Conflict!", 205), 511);
2165                 int sw, sh;
2166                 gr_get_string_size(&sw, &sh, conflict_str);
2167
2168                 gr_string((gr_screen.max_w / 2) - (sw / 2), Conflict_warning_coords[gr_screen.res][1], conflict_str);
2169
2170                 gr_set_font(FONT1);
2171 #else
2172                 hud_anim_render(&Conflict_warning_anim, frametime);
2173 #endif
2174         } else {
2175                 // might as well always reset the conflict stamp
2176                 Conflict_stamp = -1;
2177         }
2178
2179         for (i=0; i<NUM_TABS; i++) {
2180                 if (CC_Buttons[gr_screen.res][i].button.button_down()) {
2181                         break;
2182                 }
2183         }
2184
2185         if (i == NUM_TABS) {
2186                 CC_Buttons[gr_screen.res][Tab].button.draw_forced(2);
2187         }
2188
2189         if (Search_mode) {
2190                 CC_Buttons[gr_screen.res][SEARCH_MODE].button.draw_forced(2);
2191         }
2192
2193         if (Selected_line >= 0) {
2194                 z = Cc_lines[Selected_line].cc_index;
2195                 if (z & JOY_AXIS) {
2196                         if (Invert_axis[z & ~JOY_AXIS]) {
2197                                 CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2);
2198                         }
2199
2200                 } else {
2201                         z = Control_config[z].key_id;
2202                         if (z >= 0) {
2203                                 if (z & KEY_SHIFTED) {
2204                                         CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2);
2205                                 }
2206                                 if (z & KEY_ALTED) {
2207                                         CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2);
2208                                 }
2209                         }
2210                 }
2211         }
2212
2213         if (Binding_mode) {
2214                 CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2);
2215         }
2216
2217         z = Cc_lines[Selected_line].cc_index;
2218         x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2;
2219         y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2;
2220         if (Binding_mode) {
2221                 int t;
2222
2223                 t = (int) (timer * 3);
2224                 if (t % 2) {
2225                         gr_set_color_fast(&Color_text_normal);
2226                         gr_get_string_size(&w, NULL, XSTR( "?", 208));
2227                         gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208));
2228                 }
2229
2230         } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {
2231                 i = Conflicts[z].key;
2232                 if (i < 0)
2233                         i = Conflicts[z].joy;
2234
2235                 gr_set_color_fast(&Color_text_normal);
2236                 const char *str = XSTR( "Control conflicts with:", 209);
2237                 gr_get_string_size(&w, NULL, str);
2238                 gr_printf(x - w / 2, y - font_height, str);
2239
2240                 strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i));
2241                 gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]);
2242                 gr_get_string_size(&w, NULL, buf);
2243                 gr_printf(x - w / 2, y, buf);
2244
2245         } else if (*bound_string) {
2246                 gr_set_color_fast(&Color_text_normal);
2247                 gr_get_string_size(&w, NULL, bound_string);
2248                 gr_printf(x - w / 2, y - font_height / 2, bound_string);
2249                 if (timestamp_elapsed(bound_timestamp))
2250                         *bound_string = 0;
2251         }
2252
2253 //      gr_set_color_fast(&Color_text_heading);
2254 //      gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]);
2255
2256 //      gr_get_string_size(&w, &h, Heading[Tab]);
2257 //      y = HEADING_Y + h / 2 - 1;
2258 //      gr_line(LIST_X, y, LIST_X + 18, y);
2259 //      gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y);
2260
2261         if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) {
2262                 gr_set_color_fast(&Color_white);
2263                 gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210));
2264         }
2265
2266         conflict = 0;
2267         line = Scroll_offset;
2268         while (cc_line_query_visible(line)) {
2269                 z = Cc_lines[line].cc_index;
2270                 y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y;
2271
2272                 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);
2273                 List_buttons[line - Scroll_offset].enable(!Binding_mode);
2274
2275                 Cc_lines[line].kw = Cc_lines[line].jw = 0;
2276
2277                 if (line == Selected_line){
2278                         c = &Color_text_selected;
2279                 } else if (line == select_tease_line) {
2280                         c = &Color_text_subselected;
2281                 } else {
2282                         c = &Color_text_normal;
2283                 }
2284
2285                 gr_set_color_fast(c);
2286                 if (Cc_lines[line].label) {
2287                         strcpy(buf, Cc_lines[line].label);
2288                         gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]);
2289                         gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf);
2290                 }
2291
2292                 if (!(z & JOY_AXIS)) {
2293                         k = Control_config[z].key_id;
2294                         j = Control_config[z].joy_id;
2295                         x = Control_list_key_x[gr_screen.res];
2296                         *buf = 0;
2297
2298                         if ((k < 0) && (j < 0)) {
2299                                 gr_set_color_fast(&Color_grey);
2300                                 gr_printf(x, y, XSTR( "None", 211));
2301
2302                         } else {
2303                                 if (k >= 0) {
2304                                         strcpy(buf, textify_scancode(k));
2305                                         if (Conflicts[z].key >= 0) {
2306                                                 if (c == &Color_text_normal)
2307                                                         gr_set_color_fast(&Color_text_error);
2308                                                 else {
2309                                                         gr_set_color_fast(&Color_text_error_hi);
2310                                                         conflict++;
2311                                                 }
2312
2313                                         } else if (Selected_item == 1) {
2314                                                 gr_set_color_fast(&Color_text_normal);
2315
2316                                         } else
2317                                                 gr_set_color_fast(c);
2318
2319                                         gr_printf(x, y, buf);
2320
2321                                         Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2322                                         gr_get_string_size(&w, NULL, buf);
2323                                         Cc_lines[line].kw = w;
2324                                         x += w;
2325
2326                                         if (j >= 0) {
2327                                                 gr_set_color_fast(&Color_text_normal);
2328                                                 gr_printf(x, y, XSTR( ", ", 212));
2329                                                 gr_get_string_size(&w, NULL, XSTR( ", ", 212));
2330                                                 x += w;
2331                                         }
2332                                 }
2333
2334                                 if (j >= 0) {
2335                                         strcpy(buf, Joy_button_text[j]);
2336                                         if (Conflicts[z].joy >= 0) {
2337                                                 if (c == &Color_text_normal)
2338                                                         gr_set_color_fast(&Color_text_error);
2339                                                 else {
2340                                                         gr_set_color_fast(&Color_text_error_hi);
2341                                                         conflict++;
2342                                                 }
2343
2344                                         } else if (!Selected_item) {
2345                                                 gr_set_color_fast(&Color_text_normal);
2346
2347                                         } else
2348                                                 gr_set_color_fast(c);
2349
2350                                         gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x);
2351                                         gr_printf(x, y, buf);
2352
2353                                         Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD];
2354                                         gr_get_string_size(&Cc_lines[line].jw, NULL, buf);
2355                                 }
2356                         }
2357
2358                 } else {
2359                         x = Control_list_key_x[gr_screen.res];
2360                         j = Axis_map_to[z & ~JOY_AXIS];
2361                         if (Binding_mode && (line == Selected_line))
2362                                 j = Axis_override;
2363
2364                         if (j < 0) {
2365                                 gr_set_color_fast(&Color_grey);
2366                                 gr_printf(x, y, XSTR( "None", 211));
2367
2368                         } else {
2369                                 if (Conflicts_axes[z & ~JOY_AXIS] >= 0) {
2370                                         if (c == &Color_text_normal)
2371                                                 gr_set_color_fast(&Color_text_error);
2372
2373                                         else {
2374                                                 gr_set_color_fast(&Color_text_error_hi);
2375                                                 conflict++;
2376                                         }
2377
2378                                 } else if (!Selected_item) {
2379                                         gr_set_color_fast(&Color_text_normal);
2380
2381                                 } else
2382                                         gr_set_color_fast(c);
2383
2384                                 gr_string(x, y, Joy_axis_text[j]);
2385                         }
2386                 }
2387
2388                 line++;
2389         }
2390
2391         CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict);
2392
2393         i = line - Scroll_offset;
2394         while (i < LIST_BUTTONS_MAX)
2395                 List_buttons[i++].disable();
2396
2397         // blit help overlay if active
2398         help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY);
2399
2400         gr_flip();
2401 }
2402
2403 void clear_key_binding(short key)
2404 {
2405         int i;
2406
2407         for (i=0; i<CCFG_MAX; i++) {
2408                 if (Control_config[i].key_id == key)
2409                         Control_config[i].key_id = -1;
2410         }
2411 }
2412
2413 float check_control_timef(int id)
2414 {
2415         float t1, t2;
2416
2417         // if type isn't continuous, we shouldn't be using this function, cause it won't work.
2418         SDL_assert(Control_config[id].type == CC_TYPE_CONTINUOUS);
2419
2420         // first, see if control actually used (makes sure modifiers match as well)
2421         if (!check_control(id))
2422                 return 0.0f;
2423
2424         t1 = key_down_timef(Control_config[id].key_id);
2425         if (t1)
2426                 control_used(id);
2427
2428         t2 = joy_down_time(Control_config[id].joy_id);
2429         if (t2)
2430                 control_used(id);
2431
2432         if (t1 + t2)
2433                 return t1 + t2;
2434
2435         return 1.0f;
2436 }
2437
2438 void control_check_indicate()
2439 {
2440 #ifndef NDEBUG
2441         if (Show_controls_info) {
2442                 gr_set_color_fast(&HUD_color_debug);
2443                 gr_printf(490, 15, NOX("Ctrls checked: %d"), Control_check_count);
2444         }
2445 #endif
2446
2447         Control_check_count = 0;
2448 }
2449
2450 int check_control(int id, int key)
2451 {
2452         int z, mask;
2453         static int last_key = 0;
2454
2455         Control_check_count++;
2456         if (key < 0)
2457                 key = last_key;
2458
2459         last_key = key;
2460
2461         // if we're in multiplayer text enter (for chat) mode, check to see if we should ignore controls
2462         if ((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls()){
2463                 return 0;
2464         }
2465
2466         if (Control_config[id].type == CC_TYPE_CONTINUOUS) {
2467                 if (joy_down(Control_config[id].joy_id) || joy_down_count(Control_config[id].joy_id)) {
2468                         control_used(id);
2469                         return 1;
2470                 }
2471
2472                 if ((Control_config[id].joy_id >= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS))
2473                         if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2474                                 control_used(id);
2475                                 return 1;
2476                         }
2477
2478                 // check what current modifiers are pressed
2479                 int tmp = key_get_shift_status();
2480                 mask = 0;
2481
2482                 if (tmp & KEY_SHIFTED) {
2483                         mask |= KEY_SHIFTED;
2484                 }
2485
2486                 if (tmp & KEY_ALTED) {
2487                         mask |= KEY_ALTED;
2488                 }
2489
2490                 z = Control_config[id].key_id;
2491                 if (z >= 0) {
2492                         if ( (z != SDLK_LALT) && (z != SDLK_RALT) && (z != SDLK_LSHIFT) && (z != SDLK_RSHIFT) ) {
2493                                 // if current modifiers don't match action's modifiers, don't register control active.
2494                                 if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask)
2495                                         return 0;
2496                         }
2497
2498                         z &= KEY_MASK;
2499
2500                         if (key_pressed(z) || key_down_count(z)) {
2501                                 if ( !hud_squadmsg_read_key(z) ) {
2502                                         control_used(id);
2503                                         return 1;
2504                                 }
2505                         }
2506                 }
2507
2508                 return 0;
2509         }
2510
2511         if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) {
2512                 control_used(id);
2513                 return 1;
2514         }
2515
2516         return 0;
2517 }
2518
2519 // get heading, pitch, bank, throttle abs. and throttle rel. values.
2520 void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr)
2521 {
2522         //      joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point)
2523         *h = 0;
2524         if (Axis_map_to[0] >= 0)
2525                 *h = joy_get_scaled_reading(Axis_map_to[0]);
2526
2527         *p = 0;
2528         if (Axis_map_to[1] >= 0)
2529                 *p = joy_get_scaled_reading(Axis_map_to[1]);
2530
2531         *b = 0;
2532         if (Axis_map_to[2] >= 0)
2533                 *b = joy_get_scaled_reading(Axis_map_to[2]);
2534
2535         *ta = 0;
2536         if (Axis_map_to[3] >= 0)
2537                 *ta = joy_get_unscaled_reading(Axis_map_to[3]);
2538
2539         *tr = 0;
2540         if (Axis_map_to[4] >= 0)
2541                 *tr = joy_get_scaled_reading(Axis_map_to[4]);
2542
2543         if (Invert_axis[0])
2544                 *h = -(*h);
2545         if (Invert_axis[1])
2546                 *p = -(*p);
2547         if (Invert_axis[2])
2548                 *b = -(*b);
2549         if (Invert_axis[3])
2550                 *ta = F1_0 - *ta;
2551         if (Invert_axis[4])
2552                 *tr = -(*tr);
2553
2554         return;
2555 }
2556
2557 void control_used(int id)
2558 {
2559         Control_config[id].used = timestamp();
2560 }
2561
2562 void control_config_clear_used_status()
2563 {
2564         int i;
2565
2566         for (i=0; i<CCFG_MAX; i++)
2567                 Control_config[i].used = 0;
2568 }
2569
2570 void control_config_clear()
2571 {
2572         int i;
2573
2574         // Reset keyboard defaults
2575         for (i=0; i<CCFG_MAX; i++)
2576                 Control_config[i].key_id = Control_config[i].joy_id = -1;
2577 }
2578
2579 int control_config_handle_conflict()
2580 {/*
2581         if ((Selected_item == -1) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) {  // we are deleting a conflict
2582                 j = Conflicts[z].joy;
2583                 if ((j >= 0) && (Control_config[j].joy_id < 0))
2584                         j = -1;
2585
2586                 k = Conflicts[z].key;
2587                 if ((k >= 0) && (Control_config[k].key_id < 0))
2588                         k = -1;
2589
2590                 if ((j >= 0) && (k >= 0) && (j != k)) {  // deleting 2 conflicts, each in different actions
2591                         ptr = get_undo_block(2);
2592                         ptr->index[0] = j;
2593                         ptr->list[0] = Control_config[j];
2594                         Control_config[j].joy_id = (short) -1;
2595
2596                         ptr->index[1] = k;
2597                         ptr->list[1] = Control_config[k];
2598                         Control_config[k].key_id = (short) -1;
2599
2600                 } else {  // only 1 action in conflict with selected action (might be both controls, though)
2601                         z = j;
2602                         if (j < 0)
2603                                 z = k;
2604
2605                         SDL_assert(z >= 0);
2606                         ptr = get_undo_block(1);
2607                         ptr->index[0] = z;
2608                         ptr->list[0] = Control_config[z];
2609
2610                         if (j >= 0)
2611                                 Control_config[z].joy_id = (short) -1;
2612
2613                         if (k >= 0)
2614                                 Control_config[z].key_id = (short) -1;
2615                 }*/
2616
2617         return 0;
2618 }