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