Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

ui_atoms.c (25522B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 /**********************************************************************
     24 	UI_ATOMS.C
     25 
     26 	User interface building blocks and support functions.
     27 **********************************************************************/
     28 #include "ui_local.h"
     29 
     30 uiStatic_t		uis;
     31 qboolean		m_entersound;		// after a frame, so caching won't disrupt the sound
     32 
     33 // these are here so the functions in q_shared.c can link
     34 #ifndef UI_HARD_LINKED
     35 
     36 void QDECL Com_Error( int level, const char *error, ... ) {
     37 	va_list		argptr;
     38 	char		text[1024];
     39 
     40 	va_start (argptr, error);
     41 	vsprintf (text, error, argptr);
     42 	va_end (argptr);
     43 
     44 	trap_Error( va("%s", text) );
     45 }
     46 
     47 void QDECL Com_Printf( const char *msg, ... ) {
     48 	va_list		argptr;
     49 	char		text[1024];
     50 
     51 	va_start (argptr, msg);
     52 	vsprintf (text, msg, argptr);
     53 	va_end (argptr);
     54 
     55 	trap_Print( va("%s", text) );
     56 }
     57 
     58 #endif
     59 
     60 /*
     61 =================
     62 UI_ClampCvar
     63 =================
     64 */
     65 float UI_ClampCvar( float min, float max, float value )
     66 {
     67 	if ( value < min ) return min;
     68 	if ( value > max ) return max;
     69 	return value;
     70 }
     71 
     72 /*
     73 =================
     74 UI_StartDemoLoop
     75 =================
     76 */
     77 void UI_StartDemoLoop( void ) {
     78 	trap_Cmd_ExecuteText( EXEC_APPEND, "d1\n" );
     79 }
     80 
     81 /*
     82 =================
     83 UI_PushMenu
     84 =================
     85 */
     86 void UI_PushMenu( menuframework_s *menu )
     87 {
     88 	int				i;
     89 	menucommon_s*	item;
     90 
     91 	// avoid stacking menus invoked by hotkeys
     92 	for (i=0 ; i<uis.menusp ; i++)
     93 	{
     94 		if (uis.stack[i] == menu)
     95 		{
     96 			uis.menusp = i;
     97 			break;
     98 		}
     99 	}
    100 
    101 	if (i == uis.menusp)
    102 	{
    103 		if (uis.menusp >= MAX_MENUDEPTH)
    104 			trap_Error("UI_PushMenu: menu stack overflow");
    105 
    106 		uis.stack[uis.menusp++] = menu;
    107 	}
    108 
    109 	uis.activemenu = menu;
    110 
    111 	// default cursor position
    112 	menu->cursor      = 0;
    113 	menu->cursor_prev = 0;
    114 
    115 	m_entersound = qtrue;
    116 
    117 	trap_Key_SetCatcher( KEYCATCH_UI );
    118 
    119 	// force first available item to have focus
    120 	for (i=0; i<menu->nitems; i++)
    121 	{
    122 		item = (menucommon_s *)menu->items[i];
    123 		if (!(item->flags & (QMF_GRAYED|QMF_MOUSEONLY|QMF_INACTIVE)))
    124 		{
    125 			menu->cursor_prev = -1;
    126 			Menu_SetCursor( menu, i );
    127 			break;
    128 		}
    129 	}
    130 
    131 	uis.firstdraw = qtrue;
    132 }
    133 
    134 /*
    135 =================
    136 UI_PopMenu
    137 =================
    138 */
    139 void UI_PopMenu (void)
    140 {
    141 	trap_S_StartLocalSound( menu_out_sound, CHAN_LOCAL_SOUND );
    142 
    143 	uis.menusp--;
    144 
    145 	if (uis.menusp < 0)
    146 		trap_Error ("UI_PopMenu: menu stack underflow");
    147 
    148 	if (uis.menusp) {
    149 		uis.activemenu = uis.stack[uis.menusp-1];
    150 		uis.firstdraw = qtrue;
    151 	}
    152 	else {
    153 		UI_ForceMenuOff ();
    154 	}
    155 }
    156 
    157 void UI_ForceMenuOff (void)
    158 {
    159 	uis.menusp     = 0;
    160 	uis.activemenu = NULL;
    161 
    162 	trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
    163 	trap_Key_ClearStates();
    164 	trap_Cvar_Set( "cl_paused", "0" );
    165 }
    166 
    167 /*
    168 =================
    169 UI_LerpColor
    170 =================
    171 */
    172 void UI_LerpColor(vec4_t a, vec4_t b, vec4_t c, float t)
    173 {
    174 	int i;
    175 
    176 	// lerp and clamp each component
    177 	for (i=0; i<4; i++)
    178 	{
    179 		c[i] = a[i] + t*(b[i]-a[i]);
    180 		if (c[i] < 0)
    181 			c[i] = 0;
    182 		else if (c[i] > 1.0)
    183 			c[i] = 1.0;
    184 	}
    185 }
    186 
    187 /*
    188 =================
    189 UI_DrawProportionalString2
    190 =================
    191 */
    192 static int	propMap[128][3] = {
    193 {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
    194 {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
    195 
    196 {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
    197 {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
    198 
    199 {0, 0, PROP_SPACE_WIDTH},		// SPACE
    200 {11, 122, 7},	// !
    201 {154, 181, 14},	// "
    202 {55, 122, 17},	// #
    203 {79, 122, 18},	// $
    204 {101, 122, 23},	// %
    205 {153, 122, 18},	// &
    206 {9, 93, 7},		// '
    207 {207, 122, 8},	// (
    208 {230, 122, 9},	// )
    209 {177, 122, 18},	// *
    210 {30, 152, 18},	// +
    211 {85, 181, 7},	// ,
    212 {34, 93, 11},	// -
    213 {110, 181, 6},	// .
    214 {130, 152, 14},	// /
    215 
    216 {22, 64, 17},	// 0
    217 {41, 64, 12},	// 1
    218 {58, 64, 17},	// 2
    219 {78, 64, 18},	// 3
    220 {98, 64, 19},	// 4
    221 {120, 64, 18},	// 5
    222 {141, 64, 18},	// 6
    223 {204, 64, 16},	// 7
    224 {162, 64, 17},	// 8
    225 {182, 64, 18},	// 9
    226 {59, 181, 7},	// :
    227 {35,181, 7},	// ;
    228 {203, 152, 14},	// <
    229 {56, 93, 14},	// =
    230 {228, 152, 14},	// >
    231 {177, 181, 18},	// ?
    232 
    233 {28, 122, 22},	// @
    234 {5, 4, 18},		// A
    235 {27, 4, 18},	// B
    236 {48, 4, 18},	// C
    237 {69, 4, 17},	// D
    238 {90, 4, 13},	// E
    239 {106, 4, 13},	// F
    240 {121, 4, 18},	// G
    241 {143, 4, 17},	// H
    242 {164, 4, 8},	// I
    243 {175, 4, 16},	// J
    244 {195, 4, 18},	// K
    245 {216, 4, 12},	// L
    246 {230, 4, 23},	// M
    247 {6, 34, 18},	// N
    248 {27, 34, 18},	// O
    249 
    250 {48, 34, 18},	// P
    251 {68, 34, 18},	// Q
    252 {90, 34, 17},	// R
    253 {110, 34, 18},	// S
    254 {130, 34, 14},	// T
    255 {146, 34, 18},	// U
    256 {166, 34, 19},	// V
    257 {185, 34, 29},	// W
    258 {215, 34, 18},	// X
    259 {234, 34, 18},	// Y
    260 {5, 64, 14},	// Z
    261 {60, 152, 7},	// [
    262 {106, 151, 13},	// '\'
    263 {83, 152, 7},	// ]
    264 {128, 122, 17},	// ^
    265 {4, 152, 21},	// _
    266 
    267 {134, 181, 5},	// '
    268 {5, 4, 18},		// A
    269 {27, 4, 18},	// B
    270 {48, 4, 18},	// C
    271 {69, 4, 17},	// D
    272 {90, 4, 13},	// E
    273 {106, 4, 13},	// F
    274 {121, 4, 18},	// G
    275 {143, 4, 17},	// H
    276 {164, 4, 8},	// I
    277 {175, 4, 16},	// J
    278 {195, 4, 18},	// K
    279 {216, 4, 12},	// L
    280 {230, 4, 23},	// M
    281 {6, 34, 18},	// N
    282 {27, 34, 18},	// O
    283 
    284 {48, 34, 18},	// P
    285 {68, 34, 18},	// Q
    286 {90, 34, 17},	// R
    287 {110, 34, 18},	// S
    288 {130, 34, 14},	// T
    289 {146, 34, 18},	// U
    290 {166, 34, 19},	// V
    291 {185, 34, 29},	// W
    292 {215, 34, 18},	// X
    293 {234, 34, 18},	// Y
    294 {5, 64, 14},	// Z
    295 {153, 152, 13},	// {
    296 {11, 181, 5},	// |
    297 {180, 152, 13},	// }
    298 {79, 93, 17},	// ~
    299 {0, 0, -1}		// DEL
    300 };
    301 
    302 static int propMapB[26][3] = {
    303 {11, 12, 33},
    304 {49, 12, 31},
    305 {85, 12, 31},
    306 {120, 12, 30},
    307 {156, 12, 21},
    308 {183, 12, 21},
    309 {207, 12, 32},
    310 
    311 {13, 55, 30},
    312 {49, 55, 13},
    313 {66, 55, 29},
    314 {101, 55, 31},
    315 {135, 55, 21},
    316 {158, 55, 40},
    317 {204, 55, 32},
    318 
    319 {12, 97, 31},
    320 {48, 97, 31},
    321 {82, 97, 30},
    322 {118, 97, 30},
    323 {153, 97, 30},
    324 {185, 97, 25},
    325 {213, 97, 30},
    326 
    327 {11, 139, 32},
    328 {42, 139, 51},
    329 {93, 139, 32},
    330 {126, 139, 31},
    331 {158, 139, 25},
    332 };
    333 
    334 #define PROPB_GAP_WIDTH		4
    335 #define PROPB_SPACE_WIDTH	12
    336 #define PROPB_HEIGHT		36
    337 
    338 // bk001205 - code below duplicated in cgame/cg_drawtools.c
    339 // bk001205 - FIXME: does this belong in ui_shared.c?
    340 /*
    341 =================
    342 UI_DrawBannerString
    343 =================
    344 */
    345 static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color )
    346 {
    347 	const char* s;
    348 	unsigned char	ch; // bk001204 - unsigned
    349 	float	ax;
    350 	float	ay;
    351 	float	aw;
    352 	float	ah;
    353 	float	frow;
    354 	float	fcol;
    355 	float	fwidth;
    356 	float	fheight;
    357 
    358 	// draw the colored text
    359 	trap_R_SetColor( color );
    360 	
    361 	ax = x * uis.scale + uis.bias;
    362 	ay = y * uis.scale;
    363 
    364 	s = str;
    365 	while ( *s )
    366 	{
    367 		ch = *s & 127;
    368 		if ( ch == ' ' ) {
    369 			ax += ((float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH)* uis.scale;
    370 		}
    371 		else if ( ch >= 'A' && ch <= 'Z' ) {
    372 			ch -= 'A';
    373 			fcol = (float)propMapB[ch][0] / 256.0f;
    374 			frow = (float)propMapB[ch][1] / 256.0f;
    375 			fwidth = (float)propMapB[ch][2] / 256.0f;
    376 			fheight = (float)PROPB_HEIGHT / 256.0f;
    377 			aw = (float)propMapB[ch][2] * uis.scale;
    378 			ah = (float)PROPB_HEIGHT * uis.scale;
    379 			trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, uis.charsetPropB );
    380 			ax += (aw + (float)PROPB_GAP_WIDTH * uis.scale);
    381 		}
    382 		s++;
    383 	}
    384 
    385 	trap_R_SetColor( NULL );
    386 }
    387 
    388 void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) {
    389 	const char *	s;
    390 	int				ch;
    391 	int				width;
    392 	vec4_t			drawcolor;
    393 
    394 	// find the width of the drawn text
    395 	s = str;
    396 	width = 0;
    397 	while ( *s ) {
    398 		ch = *s;
    399 		if ( ch == ' ' ) {
    400 			width += PROPB_SPACE_WIDTH;
    401 		}
    402 		else if ( ch >= 'A' && ch <= 'Z' ) {
    403 			width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH;
    404 		}
    405 		s++;
    406 	}
    407 	width -= PROPB_GAP_WIDTH;
    408 
    409 	switch( style & UI_FORMATMASK ) {
    410 		case UI_CENTER:
    411 			x -= width / 2;
    412 			break;
    413 
    414 		case UI_RIGHT:
    415 			x -= width;
    416 			break;
    417 
    418 		case UI_LEFT:
    419 		default:
    420 			break;
    421 	}
    422 
    423 	if ( style & UI_DROPSHADOW ) {
    424 		drawcolor[0] = drawcolor[1] = drawcolor[2] = 0;
    425 		drawcolor[3] = color[3];
    426 		UI_DrawBannerString2( x+2, y+2, str, drawcolor );
    427 	}
    428 
    429 	UI_DrawBannerString2( x, y, str, color );
    430 }
    431 
    432 
    433 int UI_ProportionalStringWidth( const char* str ) {
    434 	const char *	s;
    435 	int				ch;
    436 	int				charWidth;
    437 	int				width;
    438 
    439 	s = str;
    440 	width = 0;
    441 	while ( *s ) {
    442 		ch = *s & 127;
    443 		charWidth = propMap[ch][2];
    444 		if ( charWidth != -1 ) {
    445 			width += charWidth;
    446 			width += PROP_GAP_WIDTH;
    447 		}
    448 		s++;
    449 	}
    450 
    451 	width -= PROP_GAP_WIDTH;
    452 	return width;
    453 }
    454 
    455 static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset )
    456 {
    457 	const char* s;
    458 	unsigned char	ch; // bk001204 - unsigned
    459 	float	ax;
    460 	float	ay;
    461 	float	aw = 0; // bk001204 - init
    462 	float	ah;
    463 	float	frow;
    464 	float	fcol;
    465 	float	fwidth;
    466 	float	fheight;
    467 
    468 	// draw the colored text
    469 	trap_R_SetColor( color );
    470 	
    471 	ax = x * uis.scale + uis.bias;
    472 	ay = y * uis.scale;
    473 
    474 	s = str;
    475 	while ( *s )
    476 	{
    477 		ch = *s & 127;
    478 		if ( ch == ' ' ) {
    479 			aw = (float)PROP_SPACE_WIDTH * uis.scale * sizeScale;
    480 		}
    481 		else if ( propMap[ch][2] != -1 ) {
    482 			fcol = (float)propMap[ch][0] / 256.0f;
    483 			frow = (float)propMap[ch][1] / 256.0f;
    484 			fwidth = (float)propMap[ch][2] / 256.0f;
    485 			fheight = (float)PROP_HEIGHT / 256.0f;
    486 			aw = (float)propMap[ch][2] * uis.scale * sizeScale;
    487 			ah = (float)PROP_HEIGHT * uis.scale * sizeScale;
    488 			trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, charset );
    489 		}
    490 
    491 		ax += (aw + (float)PROP_GAP_WIDTH * uis.scale * sizeScale);
    492 		s++;
    493 	}
    494 
    495 	trap_R_SetColor( NULL );
    496 }
    497 
    498 /*
    499 =================
    500 UI_ProportionalSizeScale
    501 =================
    502 */
    503 float UI_ProportionalSizeScale( int style ) {
    504 	if(  style & UI_SMALLFONT ) {
    505 		return PROP_SMALL_SIZE_SCALE;
    506 	}
    507 
    508 	return 1.00;
    509 }
    510 
    511 
    512 /*
    513 =================
    514 UI_DrawProportionalString
    515 =================
    516 */
    517 void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) {
    518 	vec4_t	drawcolor;
    519 	int		width;
    520 	float	sizeScale;
    521 
    522 	sizeScale = UI_ProportionalSizeScale( style );
    523 
    524 	switch( style & UI_FORMATMASK ) {
    525 		case UI_CENTER:
    526 			width = UI_ProportionalStringWidth( str ) * sizeScale;
    527 			x -= width / 2;
    528 			break;
    529 
    530 		case UI_RIGHT:
    531 			width = UI_ProportionalStringWidth( str ) * sizeScale;
    532 			x -= width;
    533 			break;
    534 
    535 		case UI_LEFT:
    536 		default:
    537 			break;
    538 	}
    539 
    540 	if ( style & UI_DROPSHADOW ) {
    541 		drawcolor[0] = drawcolor[1] = drawcolor[2] = 0;
    542 		drawcolor[3] = color[3];
    543 		UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, uis.charsetProp );
    544 	}
    545 
    546 	if ( style & UI_INVERSE ) {
    547 		drawcolor[0] = color[0] * 0.7;
    548 		drawcolor[1] = color[1] * 0.7;
    549 		drawcolor[2] = color[2] * 0.7;
    550 		drawcolor[3] = color[3];
    551 		UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, uis.charsetProp );
    552 		return;
    553 	}
    554 
    555 	if ( style & UI_PULSE ) {
    556 		drawcolor[0] = color[0] * 0.7;
    557 		drawcolor[1] = color[1] * 0.7;
    558 		drawcolor[2] = color[2] * 0.7;
    559 		drawcolor[3] = color[3];
    560 		UI_DrawProportionalString2( x, y, str, color, sizeScale, uis.charsetProp );
    561 
    562 		drawcolor[0] = color[0];
    563 		drawcolor[1] = color[1];
    564 		drawcolor[2] = color[2];
    565 		drawcolor[3] = 0.5 + 0.5 * sin( uis.realtime / PULSE_DIVISOR );
    566 		UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, uis.charsetPropGlow );
    567 		return;
    568 	}
    569 
    570 	UI_DrawProportionalString2( x, y, str, color, sizeScale, uis.charsetProp );
    571 }
    572 
    573 /*
    574 =================
    575 UI_DrawProportionalString_Wrapped
    576 =================
    577 */
    578 void UI_DrawProportionalString_AutoWrapped( int x, int y, int xmax, int ystep, const char* str, int style, vec4_t color ) {
    579 	int width;
    580 	char *s1,*s2,*s3;
    581 	char c_bcp;
    582 	char buf[1024];
    583 	float   sizeScale;
    584 
    585 	if (!str || str[0]=='\0')
    586 		return;
    587 	
    588 	sizeScale = UI_ProportionalSizeScale( style );
    589 	
    590 	Q_strncpyz(buf, str, sizeof(buf));
    591 	s1 = s2 = s3 = buf;
    592 
    593 	while (1) {
    594 		do {
    595 			s3++;
    596 		} while (*s3!=' ' && *s3!='\0');
    597 		c_bcp = *s3;
    598 		*s3 = '\0';
    599 		width = UI_ProportionalStringWidth(s1) * sizeScale;
    600 		*s3 = c_bcp;
    601 		if (width > xmax) {
    602 			if (s1==s2)
    603 			{
    604 				// fuck, don't have a clean cut, we'll overflow
    605 				s2 = s3;
    606 			}
    607 			*s2 = '\0';
    608 			UI_DrawProportionalString(x, y, s1, style, color);
    609 			y += ystep;
    610 			if (c_bcp == '\0')
    611       {
    612         // that was the last word
    613         // we could start a new loop, but that wouldn't be much use
    614         // even if the word is too long, we would overflow it (see above)
    615         // so just print it now if needed
    616         s2++;
    617         if (*s2 != '\0') // if we are printing an overflowing line we have s2 == s3
    618           UI_DrawProportionalString(x, y, s2, style, color);
    619 				break; 
    620       }
    621 			s2++;
    622 			s1 = s2;
    623 			s3 = s2;
    624 		}
    625 		else
    626 		{
    627 			s2 = s3;
    628 			if (c_bcp == '\0') // we reached the end
    629 			{
    630 				UI_DrawProportionalString(x, y, s1, style, color);
    631 				break;
    632 			}
    633 		}
    634 	}
    635 }
    636 
    637 /*
    638 =================
    639 UI_DrawString2
    640 =================
    641 */
    642 static void UI_DrawString2( int x, int y, const char* str, vec4_t color, int charw, int charh )
    643 {
    644 	const char* s;
    645 	char	ch;
    646 	int forceColor = qfalse; //APSFIXME;
    647 	vec4_t	tempcolor;
    648 	float	ax;
    649 	float	ay;
    650 	float	aw;
    651 	float	ah;
    652 	float	frow;
    653 	float	fcol;
    654 
    655 	if (y < -charh)
    656 		// offscreen
    657 		return;
    658 
    659 	// draw the colored text
    660 	trap_R_SetColor( color );
    661 	
    662 	ax = x * uis.scale + uis.bias;
    663 	ay = y * uis.scale;
    664 	aw = charw * uis.scale;
    665 	ah = charh * uis.scale;
    666 
    667 	s = str;
    668 	while ( *s )
    669 	{
    670 		if ( Q_IsColorString( s ) )
    671 		{
    672 			if ( !forceColor )
    673 			{
    674 				memcpy( tempcolor, g_color_table[ColorIndex(s[1])], sizeof( tempcolor ) );
    675 				tempcolor[3] = color[3];
    676 				trap_R_SetColor( tempcolor );
    677 			}
    678 			s += 2;
    679 			continue;
    680 		}
    681 
    682 		ch = *s & 255;
    683 		if (ch != ' ')
    684 		{
    685 			frow = (ch>>4)*0.0625;
    686 			fcol = (ch&15)*0.0625;
    687 			trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + 0.0625, frow + 0.0625, uis.charset );
    688 		}
    689 
    690 		ax += aw;
    691 		s++;
    692 	}
    693 
    694 	trap_R_SetColor( NULL );
    695 }
    696 
    697 /*
    698 =================
    699 UI_DrawString
    700 =================
    701 */
    702 void UI_DrawString( int x, int y, const char* str, int style, vec4_t color )
    703 {
    704 	int		len;
    705 	int		charw;
    706 	int		charh;
    707 	vec4_t	newcolor;
    708 	vec4_t	lowlight;
    709 	float	*drawcolor;
    710 	vec4_t	dropcolor;
    711 
    712 	if( !str ) {
    713 		return;
    714 	}
    715 
    716 	if ((style & UI_BLINK) && ((uis.realtime/BLINK_DIVISOR) & 1))
    717 		return;
    718 
    719 	if (style & UI_SMALLFONT)
    720 	{
    721 		charw =	SMALLCHAR_WIDTH;
    722 		charh =	SMALLCHAR_HEIGHT;
    723 	}
    724 	else if (style & UI_GIANTFONT)
    725 	{
    726 		charw =	GIANTCHAR_WIDTH;
    727 		charh =	GIANTCHAR_HEIGHT;
    728 	}
    729 	else
    730 	{
    731 		charw =	BIGCHAR_WIDTH;
    732 		charh =	BIGCHAR_HEIGHT;
    733 	}
    734 
    735 	if (style & UI_PULSE)
    736 	{
    737 		lowlight[0] = 0.8*color[0]; 
    738 		lowlight[1] = 0.8*color[1];
    739 		lowlight[2] = 0.8*color[2];
    740 		lowlight[3] = 0.8*color[3];
    741 		UI_LerpColor(color,lowlight,newcolor,0.5+0.5*sin(uis.realtime/PULSE_DIVISOR));
    742 		drawcolor = newcolor;
    743 	}	
    744 	else
    745 		drawcolor = color;
    746 
    747 	switch (style & UI_FORMATMASK)
    748 	{
    749 		case UI_CENTER:
    750 			// center justify at x
    751 			len = strlen(str);
    752 			x   = x - len*charw/2;
    753 			break;
    754 
    755 		case UI_RIGHT:
    756 			// right justify at x
    757 			len = strlen(str);
    758 			x   = x - len*charw;
    759 			break;
    760 
    761 		default:
    762 			// left justify at x
    763 			break;
    764 	}
    765 
    766 	if ( style & UI_DROPSHADOW )
    767 	{
    768 		dropcolor[0] = dropcolor[1] = dropcolor[2] = 0;
    769 		dropcolor[3] = drawcolor[3];
    770 		UI_DrawString2(x+2,y+2,str,dropcolor,charw,charh);
    771 	}
    772 
    773 	UI_DrawString2(x,y,str,drawcolor,charw,charh);
    774 }
    775 
    776 /*
    777 =================
    778 UI_DrawChar
    779 =================
    780 */
    781 void UI_DrawChar( int x, int y, int ch, int style, vec4_t color )
    782 {
    783 	char	buff[2];
    784 
    785 	buff[0] = ch;
    786 	buff[1] = '\0';
    787 
    788 	UI_DrawString( x, y, buff, style, color );
    789 }
    790 
    791 qboolean UI_IsFullscreen( void ) {
    792 	if ( uis.activemenu && ( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
    793 		return uis.activemenu->fullscreen;
    794 	}
    795 
    796 	return qfalse;
    797 }
    798 
    799 static void NeedCDAction( qboolean result ) {
    800 	if ( !result ) {
    801 		trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
    802 	}
    803 }
    804 
    805 static void NeedCDKeyAction( qboolean result ) {
    806 	if ( !result ) {
    807 		trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
    808 	}
    809 }
    810 
    811 void UI_SetActiveMenu( uiMenuCommand_t menu ) {
    812 	// this should be the ONLY way the menu system is brought up
    813 	// enusure minumum menu data is cached
    814 	Menu_Cache();
    815 
    816 	switch ( menu ) {
    817 	case UIMENU_NONE:
    818 		UI_ForceMenuOff();
    819 		return;
    820 	case UIMENU_MAIN:
    821 		UI_MainMenu();
    822 		return;
    823 	case UIMENU_NEED_CD:
    824 		UI_ConfirmMenu( "Insert the CD", (voidfunc_f)NULL, NeedCDAction );
    825 		return;
    826 	case UIMENU_BAD_CD_KEY:
    827 		UI_ConfirmMenu( "Bad CD Key", (voidfunc_f)NULL, NeedCDKeyAction );
    828 		return;
    829 	case UIMENU_INGAME:
    830 		/*
    831 		//GRank
    832 		UI_RankingsMenu();
    833 		return;
    834 		*/
    835 		trap_Cvar_Set( "cl_paused", "1" );
    836 		UI_InGameMenu();
    837 		return;
    838 		
    839 	// bk001204
    840 	case UIMENU_TEAM:
    841 	case UIMENU_POSTGAME:
    842 	default:
    843 #ifndef NDEBUG
    844 	  Com_Printf("UI_SetActiveMenu: bad enum %d\n", menu );
    845 #endif
    846 	  break;
    847 	}
    848 }
    849 
    850 /*
    851 =================
    852 UI_KeyEvent
    853 =================
    854 */
    855 void UI_KeyEvent( int key, int down ) {
    856 	sfxHandle_t		s;
    857 
    858 	if (!uis.activemenu) {
    859 		return;
    860 	}
    861 
    862 	if (!down) {
    863 		return;
    864 	}
    865 
    866 	if (uis.activemenu->key)
    867 		s = uis.activemenu->key( key );
    868 	else
    869 		s = Menu_DefaultKey( uis.activemenu, key );
    870 
    871 	if ((s > 0) && (s != menu_null_sound))
    872 		trap_S_StartLocalSound( s, CHAN_LOCAL_SOUND );
    873 }
    874 
    875 /*
    876 =================
    877 UI_MouseEvent
    878 =================
    879 */
    880 void UI_MouseEvent( int dx, int dy )
    881 {
    882 	int				i;
    883 	menucommon_s*	m;
    884 
    885 	if (!uis.activemenu)
    886 		return;
    887 
    888 	// update mouse screen position
    889 	uis.cursorx += dx;
    890 	if (uis.cursorx < 0)
    891 		uis.cursorx = 0;
    892 	else if (uis.cursorx > SCREEN_WIDTH)
    893 		uis.cursorx = SCREEN_WIDTH;
    894 
    895 	uis.cursory += dy;
    896 	if (uis.cursory < 0)
    897 		uis.cursory = 0;
    898 	else if (uis.cursory > SCREEN_HEIGHT)
    899 		uis.cursory = SCREEN_HEIGHT;
    900 
    901 	// region test the active menu items
    902 	for (i=0; i<uis.activemenu->nitems; i++)
    903 	{
    904 		m = (menucommon_s*)uis.activemenu->items[i];
    905 
    906 		if (m->flags & (QMF_GRAYED|QMF_INACTIVE))
    907 			continue;
    908 
    909 		if ((uis.cursorx < m->left) ||
    910 			(uis.cursorx > m->right) ||
    911 			(uis.cursory < m->top) ||
    912 			(uis.cursory > m->bottom))
    913 		{
    914 			// cursor out of item bounds
    915 			continue;
    916 		}
    917 
    918 		// set focus to item at cursor
    919 		if (uis.activemenu->cursor != i)
    920 		{
    921 			Menu_SetCursor( uis.activemenu, i );
    922 			((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor_prev]))->flags &= ~QMF_HASMOUSEFOCUS;
    923 
    924 			if ( !(((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags & QMF_SILENT ) ) {
    925 				trap_S_StartLocalSound( menu_move_sound, CHAN_LOCAL_SOUND );
    926 			}
    927 		}
    928 
    929 		((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags |= QMF_HASMOUSEFOCUS;
    930 		return;
    931 	}  
    932 
    933 	if (uis.activemenu->nitems > 0) {
    934 		// out of any region
    935 		((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags &= ~QMF_HASMOUSEFOCUS;
    936 	}
    937 }
    938 
    939 char *UI_Argv( int arg ) {
    940 	static char	buffer[MAX_STRING_CHARS];
    941 
    942 	trap_Argv( arg, buffer, sizeof( buffer ) );
    943 
    944 	return buffer;
    945 }
    946 
    947 
    948 char *UI_Cvar_VariableString( const char *var_name ) {
    949 	static char	buffer[MAX_STRING_CHARS];
    950 
    951 	trap_Cvar_VariableStringBuffer( var_name, buffer, sizeof( buffer ) );
    952 
    953 	return buffer;
    954 }
    955 
    956 
    957 /*
    958 =================
    959 UI_Cache
    960 =================
    961 */
    962 void UI_Cache_f( void ) {
    963 	MainMenu_Cache();
    964 	InGame_Cache();
    965 	ConfirmMenu_Cache();
    966 	PlayerModel_Cache();
    967 	PlayerSettings_Cache();
    968 	Controls_Cache();
    969 	Demos_Cache();
    970 	UI_CinematicsMenu_Cache();
    971 	Preferences_Cache();
    972 	ServerInfo_Cache();
    973 	SpecifyServer_Cache();
    974 	ArenaServers_Cache();
    975 	StartServer_Cache();
    976 	ServerOptions_Cache();
    977 	DriverInfo_Cache();
    978 	GraphicsOptions_Cache();
    979 	UI_DisplayOptionsMenu_Cache();
    980 	UI_SoundOptionsMenu_Cache();
    981 	UI_NetworkOptionsMenu_Cache();
    982 	UI_SPLevelMenu_Cache();
    983 	UI_SPSkillMenu_Cache();
    984 	UI_SPPostgameMenu_Cache();
    985 	TeamMain_Cache();
    986 	UI_AddBots_Cache();
    987 	UI_RemoveBots_Cache();
    988 	UI_SetupMenu_Cache();
    989 //	UI_LoadConfig_Cache();
    990 //	UI_SaveConfigMenu_Cache();
    991 	UI_BotSelectMenu_Cache();
    992 	UI_CDKeyMenu_Cache();
    993 	UI_ModsMenu_Cache();
    994 
    995 }
    996 
    997 
    998 /*
    999 =================
   1000 UI_ConsoleCommand
   1001 =================
   1002 */
   1003 qboolean UI_ConsoleCommand( int realTime ) {
   1004 	char	*cmd;
   1005 
   1006 	cmd = UI_Argv( 0 );
   1007 
   1008 	// ensure minimum menu data is available
   1009 	Menu_Cache();
   1010 
   1011 	if ( Q_stricmp (cmd, "levelselect") == 0 ) {
   1012 		UI_SPLevelMenu_f();
   1013 		return qtrue;
   1014 	}
   1015 
   1016 	if ( Q_stricmp (cmd, "postgame") == 0 ) {
   1017 		UI_SPPostgameMenu_f();
   1018 		return qtrue;
   1019 	}
   1020 
   1021 	if ( Q_stricmp (cmd, "ui_cache") == 0 ) {
   1022 		UI_Cache_f();
   1023 		return qtrue;
   1024 	}
   1025 
   1026 	if ( Q_stricmp (cmd, "ui_cinematics") == 0 ) {
   1027 		UI_CinematicsMenu_f();
   1028 		return qtrue;
   1029 	}
   1030 
   1031 	if ( Q_stricmp (cmd, "ui_teamOrders") == 0 ) {
   1032 		UI_TeamOrdersMenu_f();
   1033 		return qtrue;
   1034 	}
   1035 
   1036 	if ( Q_stricmp (cmd, "iamacheater") == 0 ) {
   1037 		UI_SPUnlock_f();
   1038 		return qtrue;
   1039 	}
   1040 
   1041 	if ( Q_stricmp (cmd, "iamamonkey") == 0 ) {
   1042 		UI_SPUnlockMedals_f();
   1043 		return qtrue;
   1044 	}
   1045 
   1046 	if ( Q_stricmp (cmd, "ui_cdkey") == 0 ) {
   1047 		UI_CDKeyMenu_f();
   1048 		return qtrue;
   1049 	}
   1050 
   1051 	return qfalse;
   1052 }
   1053 
   1054 /*
   1055 =================
   1056 UI_Shutdown
   1057 =================
   1058 */
   1059 void UI_Shutdown( void ) {
   1060 }
   1061 
   1062 /*
   1063 =================
   1064 UI_Init
   1065 =================
   1066 */
   1067 void UI_Init( void ) {
   1068 	UI_RegisterCvars();
   1069 
   1070 	UI_InitGameinfo();
   1071 
   1072 	// cache redundant calulations
   1073 	trap_GetGlconfig( &uis.glconfig );
   1074 
   1075 	// for 640x480 virtualized screen
   1076 	uis.scale = uis.glconfig.vidHeight * (1.0/480.0);
   1077 	if ( uis.glconfig.vidWidth * 480 > uis.glconfig.vidHeight * 640 ) {
   1078 		// wide screen
   1079 		uis.bias = 0.5 * ( uis.glconfig.vidWidth - ( uis.glconfig.vidHeight * (640.0/480.0) ) );
   1080 	}
   1081 	else {
   1082 		// no wide screen
   1083 		uis.bias = 0;
   1084 	}
   1085 
   1086 	// initialize the menu system
   1087 	Menu_Cache();
   1088 
   1089 	uis.activemenu = NULL;
   1090 	uis.menusp     = 0;
   1091 }
   1092 
   1093 /*
   1094 ================
   1095 UI_AdjustFrom640
   1096 
   1097 Adjusted for resolution and screen aspect ratio
   1098 ================
   1099 */
   1100 void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) {
   1101 	// expect valid pointers
   1102 	*x = *x * uis.scale + uis.bias;
   1103 	*y *= uis.scale;
   1104 	*w *= uis.scale;
   1105 	*h *= uis.scale;
   1106 }
   1107 
   1108 void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
   1109 	qhandle_t	hShader;
   1110 
   1111 	hShader = trap_R_RegisterShaderNoMip( picname );
   1112 	UI_AdjustFrom640( &x, &y, &width, &height );
   1113 	trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
   1114 }
   1115 
   1116 void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) {
   1117 	float	s0;
   1118 	float	s1;
   1119 	float	t0;
   1120 	float	t1;
   1121 
   1122 	if( w < 0 ) {	// flip about vertical
   1123 		w  = -w;
   1124 		s0 = 1;
   1125 		s1 = 0;
   1126 	}
   1127 	else {
   1128 		s0 = 0;
   1129 		s1 = 1;
   1130 	}
   1131 
   1132 	if( h < 0 ) {	// flip about horizontal
   1133 		h  = -h;
   1134 		t0 = 1;
   1135 		t1 = 0;
   1136 	}
   1137 	else {
   1138 		t0 = 0;
   1139 		t1 = 1;
   1140 	}
   1141 	
   1142 	UI_AdjustFrom640( &x, &y, &w, &h );
   1143 	trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, hShader );
   1144 }
   1145 
   1146 /*
   1147 ================
   1148 UI_FillRect
   1149 
   1150 Coordinates are 640*480 virtual values
   1151 =================
   1152 */
   1153 void UI_FillRect( float x, float y, float width, float height, const float *color ) {
   1154 	trap_R_SetColor( color );
   1155 
   1156 	UI_AdjustFrom640( &x, &y, &width, &height );
   1157 	trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, uis.whiteShader );
   1158 
   1159 	trap_R_SetColor( NULL );
   1160 }
   1161 
   1162 /*
   1163 ================
   1164 UI_DrawRect
   1165 
   1166 Coordinates are 640*480 virtual values
   1167 =================
   1168 */
   1169 void UI_DrawRect( float x, float y, float width, float height, const float *color ) {
   1170 	trap_R_SetColor( color );
   1171 
   1172 	UI_AdjustFrom640( &x, &y, &width, &height );
   1173 
   1174 	trap_R_DrawStretchPic( x, y, width, 1, 0, 0, 0, 0, uis.whiteShader );
   1175 	trap_R_DrawStretchPic( x, y, 1, height, 0, 0, 0, 0, uis.whiteShader );
   1176 	trap_R_DrawStretchPic( x, y + height - 1, width, 1, 0, 0, 0, 0, uis.whiteShader );
   1177 	trap_R_DrawStretchPic( x + width - 1, y, 1, height, 0, 0, 0, 0, uis.whiteShader );
   1178 
   1179 	trap_R_SetColor( NULL );
   1180 }
   1181 
   1182 void UI_SetColor( const float *rgba ) {
   1183 	trap_R_SetColor( rgba );
   1184 }
   1185 
   1186 void UI_UpdateScreen( void ) {
   1187 	trap_UpdateScreen();
   1188 }
   1189 
   1190 /*
   1191 =================
   1192 UI_Refresh
   1193 =================
   1194 */
   1195 void UI_Refresh( int realtime )
   1196 {
   1197 	uis.frametime = realtime - uis.realtime;
   1198 	uis.realtime  = realtime;
   1199 
   1200 	if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
   1201 		return;
   1202 	}
   1203 
   1204 	UI_UpdateCvars();
   1205 
   1206 	if ( uis.activemenu )
   1207 	{
   1208 		if (uis.activemenu->fullscreen)
   1209 		{
   1210 			// draw the background
   1211 			if( uis.activemenu->showlogo ) {
   1212 				UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackShader );
   1213 			}
   1214 			else {
   1215 				UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackNoLogoShader );
   1216 			}
   1217 		}
   1218 
   1219 		if (uis.activemenu->draw)
   1220 			uis.activemenu->draw();
   1221 		else
   1222 			Menu_Draw( uis.activemenu );
   1223 
   1224 		if( uis.firstdraw ) {
   1225 			UI_MouseEvent( 0, 0 );
   1226 			uis.firstdraw = qfalse;
   1227 		}
   1228 	}
   1229 
   1230 	// draw cursor
   1231 	UI_SetColor( NULL );
   1232 	UI_DrawHandlePic( uis.cursorx-16, uis.cursory-16, 32, 32, uis.cursor);
   1233 
   1234 #ifndef NDEBUG
   1235 	if (uis.debug)
   1236 	{
   1237 		// cursor coordinates
   1238 		UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed );
   1239 	}
   1240 #endif
   1241 
   1242 	// delay playing the enter sound until after the
   1243 	// menu has been drawn, to avoid delay while
   1244 	// caching images
   1245 	if (m_entersound)
   1246 	{
   1247 		trap_S_StartLocalSound( menu_in_sound, CHAN_LOCAL_SOUND );
   1248 		m_entersound = qfalse;
   1249 	}
   1250 }
   1251 
   1252 void UI_DrawTextBox (int x, int y, int width, int lines)
   1253 {
   1254 	UI_FillRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorBlack );
   1255 	UI_DrawRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorWhite );
   1256 }
   1257 
   1258 qboolean UI_CursorInRect (int x, int y, int width, int height)
   1259 {
   1260 	if (uis.cursorx < x ||
   1261 		uis.cursory < y ||
   1262 		uis.cursorx > x+width ||
   1263 		uis.cursory > y+height)
   1264 		return qfalse;
   1265 
   1266 	return qtrue;
   1267 }