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