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