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