2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Ui/WINDOW.cpp $
15 * Routines to handle UI windows.
18 * Revision 1.3 2004/09/20 01:31:45 theoddone33
21 * Revision 1.2 2002/06/09 04:41:29 relnev
22 * added copyright header
24 * Revision 1.1.1.1 2002/05/03 03:28:11 root
28 * 30 10/14/99 2:52p Jefff
29 * localization fixes. added support for hi-res specific xstr offsets
31 * 29 8/16/99 4:06p Dave
32 * Big honking checkin.
34 * 28 7/16/99 1:50p Dave
35 * 8 bit aabitmaps. yay.
37 * 27 7/15/99 3:07p Dave
38 * 32 bit detection support. Mouse coord commandline.
40 * 26 7/13/99 5:38p Jefff
41 * Added support for x offsets when drawing strings
43 * 25 6/25/99 11:59a Dave
44 * Multi options screen.
46 * 24 6/22/99 7:03p Dave
47 * New detail options screen.
49 * 23 6/19/99 2:46p Dave
50 * New control config screen.
52 * 22 6/18/99 5:16p Dave
53 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
54 * dialog to PXO screen.
56 * 21 6/04/99 11:32a Dave
57 * Added reset text to ship select screen. Fixed minor xstr bug in ui
60 * 20 6/02/99 6:18p Dave
61 * Fixed TNT lockup problems! Wheeeee!
63 * 19 6/01/99 3:52p Dave
64 * View footage screen. Fixed xstrings to not display the & symbol. Popup,
65 * dead popup, pxo find player popup, pxo private room popup.
67 * 18 5/22/99 5:35p Dave
68 * Debrief and chatbox screens. Fixed small hi-res HUD bug.
70 * 17 5/21/99 6:45p Dave
71 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
72 * start game screen, multi password, and multi pxo-help screen.
74 * 16 5/17/99 11:28a Dave
76 * 15 5/17/99 9:25a Dave
77 * Updated PXO screen. Still needs popups.
79 * 14 5/03/99 8:33p Dave
80 * New version of multi host options screen.
82 * 13 4/16/99 5:22p Neilk
83 * Fixed UI_SLIDER2 bug
85 * 12 2/21/99 6:02p Dave
86 * Fixed standalone WSS packets.
88 * 11 2/01/99 5:55p Dave
89 * Removed the idea of explicit bitmaps for buttons. Fixed text
90 * highlighting for disabled gadgets.
92 * 10 1/29/99 2:08a Dave
93 * Fixed beam weapon collisions with players. Reduced size of scoring
94 * struct for multiplayer. Disabled PXO.
96 * 9 1/29/99 12:47a Dave
97 * Put in sounds for beam weapon. A bunch of interface screens (tech
100 * 8 12/18/98 1:13a Dave
101 * Rough 1024x768 support for Direct3D. Proper detection and usage through
104 * 7 12/02/98 5:47p Dave
105 * Put in interface xstr code. Converted barracks screen to new format.
107 * 6 11/30/98 1:07p Dave
108 * 16 bit conversion, first run.
110 * 5 10/23/98 3:51p Dave
111 * Full support for tstrings.tbl and foreign languages. All that remains
112 * is to make it active in Fred.
114 * 4 10/13/98 9:29a Dave
115 * Started neatening up freespace.h. Many variables renamed and
116 * reorganized. Added AlphaColors.[h,cpp]
118 * 3 10/07/98 6:27p Dave
119 * Globalized mission and campaign file extensions. Removed Silent Threat
120 * special code. Moved \cache \players and \multidata into the \data
123 * 2 10/07/98 10:54a Dave
126 * 1 10/07/98 10:51a Dave
135 #include "freespace.h"
140 #include "alphacolors.h"
142 #include "localize.h"
145 // global xstr colors
146 color *Xstr_colors[UI_NUM_XSTR_COLORS][3] = {
147 { // UI_XSTR_COLOR_GREEN
149 &Color_ui_light_green,
152 { // UI_XSTR_COLOR_PINK
154 &Color_ui_light_pink,
159 // --------------------------------------------------------------------
160 // UI_WINDOW constructor
163 UI_WINDOW::UI_WINDOW()
167 mask_bmap_id = -1; // bitmap id of the mask bitmap to define hotspots
168 foreground_bmap_id = -1; // bitmap id of the foreground bitmap to display
169 mask_bmap_ptr = NULL; // pointer to bitmap of the mask
170 mask_data = NULL; // points to raw mask bitmap data
171 mask_w = -1; // bitmap width
172 mask_h = -1; // bitmap height
173 tooltip_handler = NULL; // pointer to function to handle custom tooltips
175 // NULL all the xstring structs
176 for(idx=0; idx<MAX_UI_XSTRS; idx++){
181 // --------------------------------------------------------------------
182 // UI_WINDOW destructor
185 UI_WINDOW::~UI_WINDOW()
189 // --------------------------------------------------------------------
190 // UI_WINDOW::set_mask_bmap()
192 // Specify the filename for the mask bitmap to use. This has the hotspots
193 // for all the different controls.
195 void UI_WINDOW::set_mask_bmap(const char *fname)
199 bmap = bm_load(fname);
202 Error(LOCATION, "Could not load in %s!", fname);
205 set_mask_bmap(bmap, fname);
209 void UI_WINDOW::set_mask_bmap(int bmap, const char *name)
214 SDL_assert(bmap >= 0);
216 if (bmap != mask_bmap_id) {
217 if (mask_bmap_id >= 0){
218 bm_unlock(mask_bmap_id);
225 mask_bmap_ptr = bm_lock(mask_bmap_id, 8, BMP_AABITMAP);
226 mask_data = (ushort *) mask_bmap_ptr->data;
227 bm_get_info( bmap, &mask_w, &mask_h );
230 for (i=0; i<Num_tooltip_groups; i++){
231 if (!SDL_strcasecmp(Tooltip_groups[i].mask, name)){
237 nprintf(("UI", "Warning: tried to switch bitmap mask to the same bitmap\n"));
241 // --------------------------------------------------------------------
242 // UI_WINDOW::set_foreground_bmap()
244 // Specify the filename for the mask bitmap to display on the ui window as
247 void UI_WINDOW::set_foreground_bmap(const char *fname)
249 // load in the background bitmap
250 foreground_bmap_id = bm_load(fname);
251 if (foreground_bmap_id < 0) {
252 Error(LOCATION,"Could not load in %s!",fname);
257 void UI_WINDOW::create( int _x, int _y, int _w, int _h, int _flags )
265 selected_gadget = NULL;
266 tooltip_handler = NULL; // pointer to function to handle custom tooltips
268 use_hack_to_get_around_stupid_problem_flag = 0;
270 f_id = gr_init_font("font01.vf");
274 if (x + w - 1 >= gr_screen.max_w)
275 x = gr_screen.max_w - w;
278 if (y + h - 1 >= gr_screen.max_h)
279 y = gr_screen.max_h - h;
284 void UI_WINDOW::release_bitmaps()
287 // done with the mask bitmap, so unlock it
288 bm_unlock(mask_bmap_id);
290 // unload the bitmaps
291 if (mask_bmap_id >= 0) {
292 bm_release(mask_bmap_id);
296 mask_bmap_ptr = NULL;
299 if (foreground_bmap_id >= 0) {
300 bm_release(foreground_bmap_id);
301 foreground_bmap_id = -1;
305 void UI_WINDOW::destroy()
310 // free up any bitmaps
313 // destroy all gadgets
320 } while (cur != first_gadget);
324 for(idx=0; idx<MAX_UI_XSTRS; idx++){
325 // free up this struct
326 if(xstrs[idx] != NULL){
327 if(xstrs[idx]->xstr != NULL){
328 free((char *)xstrs[idx]->xstr);
336 void UI_WINDOW::draw()
343 if (foreground_bmap_id >= 0) {
344 gr_set_bitmap(foreground_bmap_id, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
348 if (flags & WIN_FILLED) {
349 ui_draw_box_out(x, y, x+w-1, y+h-1);
352 if (flags & WIN_BORDER) {
353 ui_draw_frame(x-BORDER_WIDTH, y-BORDER_WIDTH, x+w+BORDER_WIDTH-1, y+h+BORDER_WIDTH-1);
364 } while (tmp != first_gadget);
370 if (!tmp->hidden && (tmp->kind == UI_KIND_BUTTON) && ((UI_BUTTON *) tmp)->button_down()){
375 } while (tmp != first_gadget);
384 // convenient debug code for showing mouse coords
385 if(Cmdline_mouse_coords){
387 mouse_get_pos(&mx, &my);
388 // mprintf(("MOUSE (%d, %d)\n", mx, my));
389 gr_set_color_fast(&Color_normal);
390 gr_printf(mx, my - 12, "%d %d", mx, my);
394 void UI_WINDOW::draw_XSTR_forced(UI_GADGET *owner, int frame)
398 // lookup the xstr and draw it if necessary
399 for(idx=0; idx<MAX_UI_XSTRS; idx++){
400 if((xstrs[idx] != NULL) && (xstrs[idx]->assoc == owner)){
401 draw_one_xstr(xstrs[idx], frame);
406 int UI_WINDOW::get_current_hotspot()
408 int offset, pixel_val;
414 // check the pixel value under the mouse
415 offset = ui_mouse.y * gr_screen.max_w + ui_mouse.x;
416 pixel_val = *( ((ubyte*)mask_data) + offset);
420 void UI_WINDOW::draw_tooltip()
423 // tooltip_group *ptr;
430 // ptr = &Tooltip_groups[tt_group];
431 hotspot = get_current_hotspot();
433 // mprintf(("HOTSPOT: %d [%d]\n",hotspot, Framecount));
436 if (hotspot != last_tooltip_hotspot) {
437 last_tooltip_hotspot = hotspot;
438 last_tooltip_time = timer_get_milliseconds();
442 } else if (timer_get_milliseconds() - last_tooltip_time < TOOLTIP_DELAY)
447 gadget = first_gadget;
449 if (gadget->get_hotspot() == hotspot) {
450 if (gadget->hidden) { // if control is hidden, don't draw tooltip for it.
457 gadget = gadget->next;
459 } while (gadget != first_gadget);
463 for (i=ptr->start; i<ptr->end; i++) {
464 if (Tooltips[i].hotspot == hotspot) {
468 str = Tooltips[i].text;
470 if (!tooltip_handler)
471 Error(LOCATION, "No tooltip handler for screen with mask \"%s\"", ptr->mask);
473 str = (*tooltip_handler)(str); // Let the screen handle the custom tooltips
478 if (ttx < 0 || tty < 0) {
479 gr_get_string_size(&w, &h, str);
480 SDL_assert(w < 320 && h < 100);
481 ttx = ui_mouse.x - w / 2;
482 tty = ui_mouse.y - h;
491 void UI_WINDOW::render_tooltip(const char *str)
495 gr_get_string_size(&width, &height, str);
496 SDL_assert(width < gr_screen.max_w - 4 && height < gr_screen.max_h - 4);
504 if (ttx + width + 2 > gr_screen.max_w)
505 ttx = gr_screen.max_w - width;
507 if (tty + height + 2 > gr_screen.max_h)
508 tty = gr_screen.max_h - height;
510 gr_set_color_fast(&Color_black);
511 gr_rect(ttx - 1, tty - 1, width + 2, height + 1);
513 gr_set_color_fast(&Color_bright_white);
514 gr_string(ttx, tty, str);
517 // key_in: If not -1, this means to use this key as input, and not call game_poll()
518 int UI_WINDOW::process(int key_in,int process_mouse)
522 // only does stuff in non THREADED mode
530 keypress = game_check_key();
535 last_keypress = keypress;
537 if (mouse_captured_gadget && B1_RELEASED){
538 mouse_captured_gadget = NULL;
541 keypress_text = key_get_text_input();
543 // The following code was commented out by NeilK on 4/15/99 to fix a problem we were having with
544 // the UI_SLIDER2 class not receiving the process event when the mouse was dragging the scroller
545 // but outside the mask region. I checked a handful of other screens and so no adverse affects
546 // of this change at the time.
549 if (mouse_captured_gadget) {
550 mouse_captured_gadget->process(); // if a control has captured the mouse, only it gets processed
551 use_hack_to_get_around_stupid_problem_flag = 0;
552 return last_keypress;
556 use_hack_to_get_around_stupid_problem_flag = 0;
557 return last_keypress;
560 check_focus_switch_keys();
562 // run through all top level gadgets and process them (they are responsible for processing
563 // their children, which UI_GADGET will handle if you don't override process() or if you
564 // do, you call UI_GADGET::process()).
565 if ( !ignore_gadgets ) {
568 if ( !tmp->check_move() )
573 } while (tmp != first_gadget);
576 use_hack_to_get_around_stupid_problem_flag = 0;
577 return last_keypress;
580 void UI_WINDOW::check_focus_switch_keys()
585 void UI_WINDOW::capture_mouse(UI_GADGET *gadget)
587 mouse_captured_gadget = gadget;
590 void UI_WINDOW::set_ignore_gadgets(int state)
592 ignore_gadgets = state;
595 void UI_WINDOW::add_XSTR(const char *string, int _xstr_id, int _x, int _y, UI_GADGET *_assoc, int _color_type, int _font_id)
601 // try and find a free xstr
602 for(idx=0; idx<MAX_UI_XSTRS; idx++){
603 if(xstrs[idx] == NULL){
609 // if we don't have a free spot
611 Int3(); // aieee! we need to up the max # of xstrs allowed in a window.
615 // allocate a new struct
616 xstrs[idx] = (UI_XSTR*)malloc(sizeof(UI_XSTR));
617 if(xstrs[idx] == NULL){
623 xp->xstr = strdup(string);
624 if(xp->xstr == NULL){
629 xp->xstr_id = _xstr_id;
633 xp->font_id = _font_id;
634 xp->clr = _color_type;
635 SDL_assert((xp->clr >= 0) && (xp->clr < UI_NUM_XSTR_COLORS));
636 if((xp->clr < 0) || (xp->clr >= UI_NUM_XSTR_COLORS)){
641 void UI_WINDOW::add_XSTR(UI_XSTR *xstr)
647 // try and find a free xstr
648 for(idx=0; idx<MAX_UI_XSTRS; idx++){
649 if(xstrs[idx] == NULL){
655 // if we don't have a free spot
657 Int3(); // aieee! we need to up the max # of xstrs allowed in a window.
661 // allocate a new struct
662 xstrs[idx] = (UI_XSTR*)malloc(sizeof(UI_XSTR));
663 if(xstrs[idx] == NULL){
669 xp->xstr = strdup(xstr->xstr);
670 if(xp->xstr == NULL){
675 xp->xstr_id = xstr->xstr_id;
678 xp->assoc = xstr->assoc;
679 xp->font_id = xstr->font_id;
681 SDL_assert((xp->clr >= 0) && (xp->clr < UI_NUM_XSTR_COLORS));
682 if((xp->clr < 0) || (xp->clr >= UI_NUM_XSTR_COLORS)){
687 void UI_WINDOW::draw_one_xstr(UI_XSTR *xp, int frame)
689 font *f_backup = NULL;
693 if((xp == NULL) || (xp->xstr == NULL)){
697 // if it has an associated gadet that is hidden, do nothing
698 if((xp->assoc != NULL) && (xp->assoc->hidden)){
702 // maybe set the font
703 if(xp->font_id >= 0){
704 // backup the current font
705 SDL_assert(Current_font != NULL);
706 f_backup = Current_font;
709 gr_set_font(xp->font_id);
713 if(xp->assoc == NULL){
714 gr_set_color_fast(&Color_normal);
716 // just buttons for now
717 switch(xp->assoc->kind){
720 if((frame != -1) && (frame < 3)){
721 gr_set_color_fast(Xstr_colors[xp->clr][frame]);
725 // if the button is pressed
726 if(((UI_BUTTON*)xp->assoc)->button_down()){
727 gr_set_color_fast(Xstr_colors[xp->clr][2]);
729 // if the mouse is just over it
730 else if(xp->assoc->is_mouse_on()){
731 gr_set_color_fast(Xstr_colors[xp->clr][1]);
733 gr_set_color_fast(Xstr_colors[xp->clr][0]);
739 // all other controls just draw the normal frame
741 if((frame != -1) && (frame < 3)){
742 gr_set_color_fast(Xstr_colors[xp->clr][frame]);
744 gr_set_color_fast(Xstr_colors[xp->clr][0]);
749 // if the gadget disabled, just draw the normal nonhighlighted frame
750 if(xp->assoc->disabled()){
751 gr_set_color_fast(Xstr_colors[xp->clr][0]);
755 // print this puppy out
756 int xoffset = lcl_get_xstr_offset(xp->xstr_id, gr_screen.res);
757 SDL_strlcpy(str, XSTR(xp->xstr, xp->xstr_id), SDL_arraysize(str));
760 gr_string((xp->x) + xoffset, xp->y, str + 1);
763 gr_string((xp->x) + xoffset, xp->y, str);
766 // maybe restore the old font
767 if(f_backup != NULL){
768 Current_font = f_backup;
772 void UI_WINDOW::draw_xstrs()
778 draw_one_xstr(xstrs[idx], -1);
782 } while(idx < MAX_UI_XSTRS);
787 void UI_WINDOW::do_dump_check()
790 if ( keypress == KEY_SHIFTED+KEY_CTRLED+KEY_ALTED+SDLK_F12 ) {
793 last_keypress = keypress = 0;
795 mprintf(( "\n========== WINDOW GADGETS =========\n" ));
796 mprintf(( "(Also dumped to ui.out)\n" ));
798 fp = fopen( "ui.out", "wt" );
801 if ( tmp->parent == NULL ) {
802 switch ( tmp->kind ) {
804 mprintf(( "UI: Button at %d,%d\n", tmp->x, tmp->y ));
805 fprintf( fp, "UI: Button at %d,%d\n", tmp->x, tmp->y );
807 case UI_KIND_KEYTRAP:
808 mprintf(( "UI: Keytrap at %d,%d\n", tmp->x, tmp->y ));
809 fprintf( fp, "UI: Keytrap at %d,%d\n", tmp->x, tmp->y );
811 case UI_KIND_CHECKBOX:
812 mprintf(( "UI: Checkbox at %d,%d\n", tmp->x, tmp->y ));
813 fprintf( fp, "UI: Checkbox at %d,%d\n", tmp->x, tmp->y );
816 mprintf(( "UI: Radiobutton at %d,%d\n", tmp->x, tmp->y ));
817 fprintf( fp, "UI: Radiobutton at %d,%d\n", tmp->x, tmp->y );
819 case UI_KIND_SCROLLBAR:
820 mprintf(( "UI: Scrollbar at %d,%d\n", tmp->x, tmp->y ));
821 fprintf( fp, "UI: Scrollbar at %d,%d\n", tmp->x, tmp->y );
823 case UI_KIND_LISTBOX:
824 mprintf(( "UI: Listbox at %d,%d\n", tmp->x, tmp->y ));
825 fprintf( fp, "UI: Listbox at %d,%d\n", tmp->x, tmp->y );
827 case UI_KIND_INPUTBOX:
828 mprintf(( "UI: Inputbox at %d,%d\n", tmp->x, tmp->y ));
829 fprintf( fp, "UI: Inputbox at %d,%d\n", tmp->x, tmp->y );
832 mprintf(( "UI: Unknown type %d at %d,%d\n", tmp->kind, tmp->x, tmp->y ));
833 fprintf( fp, "UI: Unknown type %d at %d,%d\n", tmp->kind, tmp->x, tmp->y );
837 } while( tmp != first_gadget );
839 mprintf(( "===================================\n" ));
845 void parse_tooltip(int n)
847 char buf[MESSAGE_LENGTH];
849 stuff_int(&Tooltips[n].hotspot);
850 stuff_string(buf, F_MESSAGE, NULL);
851 Tooltips[n].text = strdup(buf);
854 int parse_tooltips_group(int group, int n)
856 char buf[NAME_LENGTH];
858 SDL_assert(group < MAX_TOOLTIP_GROUPS);
859 required_string("$Mask Filename:");
860 stuff_string(buf, F_NAME, NULL);
861 Tooltip_groups[group].mask = strdup(buf);
862 Tooltip_groups[group].start = n;
865 if (check_for_string("#") || check_for_string("$")) {
866 Tooltip_groups[group].end = n;
870 SDL_assert(n < MAX_TOOLTIPS);
875 void parse_ship_tooltip(int n)
879 void parse_weapon_tooltip(int n)
883 void parse_tooltips()
890 read_file_text("tooltips.tbl");
892 n = Num_tooltip_groups = 0;
895 if (optional_string("#UI"))
896 while (required_string_either("#", "$")) {
897 n = parse_tooltips_group(Num_tooltip_groups, n);
898 Num_tooltip_groups++;
901 if (optional_string("#Ships"))
902 while (required_string_either("#", "$")) {
903 parse_ship_tooltip(Num_ship_tooltips);
907 if (optional_string("#Weapons"))
908 while (required_string_either("#", "$")) {
909 parse_ship_tooltip(Num_weapon_tooltips);
910 Num_weapon_tooltips++;
913 required_string("#End");
915 // close localization
921 static int inited = 0;
926 if ((rval = setjmp(parse_abort)) != 0) {
941 mprintf(( "OK Clicked!\n" ));
946 mprintf(( "Help!\n" ));