From 1e0b515a6031a2cd2a77e9c34f2e4292fc9f9750 Mon Sep 17 00:00:00 2001 From: Tim Angus Date: Tue, 30 Oct 2007 18:18:47 +0000 Subject: * (bug 3384) Remove server side armoury refresh and replace with UI side refresh * Add trap_Key_SetOverstrikeMode and trap_Key_GetOverstrikeMode to cgame * Change Text_[Width|Height] to return floats * Add Text_Em[Width|Height] * Add CG_AlignText to cut down on code duplication * Add itemDef_t::textvalignment for vertical text alignment * Add UI_DrawTextBlock to replace a lot of duplicate code * Rewrite text wrapping code from scratch so that it actually works * Add UI_OwnerDrawText * Add expression evaluator to .menu parser * Split off Border_Paint from Window_Paint * Scale slider items to fit their rect * Rework edit field widgets to behave somewhat more correctly * Fix a few listbox widget layout issues * Don't display scrollbars in "notselectable" listbox widgets * Make scoreboard team labels ownerdrawn * Menu script rework + Use the expression evaluator to replace lots and lots of absolute coords with relative ones, hopefully easing future maintenance + Remove lots and lots of textalign[xy] that were present to work around broken text alignment routines + Replace a bunch of numeric constants with ones from menudef.h + Compose the huds from tremulous_common_hud.h + Generally neaten things up + Lots of whitespace fixes --- src/ui/ui_gameinfo.c | 2 +- src/ui/ui_local.h | 3 + src/ui/ui_main.c | 708 ++++++++++--------------- src/ui/ui_shared.c | 1444 +++++++++++++++++++++++++++++++++----------------- src/ui/ui_shared.h | 27 +- 5 files changed, 1252 insertions(+), 932 deletions(-) (limited to 'src/ui') diff --git a/src/ui/ui_gameinfo.c b/src/ui/ui_gameinfo.c index 43639e56..ad31218e 100644 --- a/src/ui/ui_gameinfo.c +++ b/src/ui/ui_gameinfo.c @@ -303,7 +303,7 @@ char *UI_GetBotNameByNumber( int num ) { if (info) { return Info_ValueForKey( info, "name" ); } - return "Sarge"; + return ""; } void UI_ServerInfo( void ) diff --git a/src/ui/ui_local.h b/src/ui/ui_local.h index ff8367ba..5f5ea862 100644 --- a/src/ui/ui_local.h +++ b/src/ui/ui_local.h @@ -865,6 +865,9 @@ typedef struct { int humanBuildCount; int humanBuildIndex; + int weapons; + int upgrades; + serverStatus_t serverStatus; // for the showing the status of a server diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c index 768008f6..edecd280 100644 --- a/src/ui/ui_main.c +++ b/src/ui/ui_main.c @@ -250,7 +250,7 @@ void _UI_DrawRect( float x, float y, float width, float height, float size, cons -int Text_Width(const char *text, float scale, int limit) { +float Text_Width(const char *text, float scale, int limit) { int count,len; float out; glyphInfo_t *glyph; @@ -265,7 +265,7 @@ int Text_Width(const char *text, float scale, int limit) { useScale = scale * font->glyphScale; out = 0; if (text) { - len = strlen(text); + len = Q_PrintStrlen( text ); if (limit > 0 && len > limit) { len = limit; } @@ -285,7 +285,7 @@ int Text_Width(const char *text, float scale, int limit) { return out * useScale; } -int Text_Height(const char *text, float scale, int limit) { +float Text_Height(const char *text, float scale, int limit) { int len, count; float max; glyphInfo_t *glyph; @@ -322,6 +322,16 @@ int Text_Height(const char *text, float scale, int limit) { return max * useScale; } +float Text_EmWidth( float scale ) +{ + return Text_Width( "M", scale, 0 ); +} + +float Text_EmHeight( float scale ) +{ + return Text_Height( "M", scale, 0 ); +} + void Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) { float w, h; w = width * scale; @@ -353,8 +363,6 @@ void Text_Paint(float x, float y, float scale, vec4_t color, const char *text, f count = 0; while (s && *s && count < len) { glyph = &font->glyphs[(int)*s]; - //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top; - //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height); if ( Q_IsColorString( s ) ) { memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); newColor[3] = color[3]; @@ -455,6 +463,7 @@ void Text_Paint(float x, float y, float scale, vec4_t color, const char *text, f } } +//FIXME: merge this with Text_Paint, somehow void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) { int len, count; vec4_t newColor; @@ -480,88 +489,12 @@ void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const cha glyph2 = &font->glyphs[ (int) cursor]; while (s && *s && count < len) { glyph = &font->glyphs[(int)*s]; - if ( Q_IsColorString( s ) ) { - memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); - newColor[3] = color[3]; - trap_R_SetColor( newColor ); - s += 2; - continue; - } else { - yadj = useScale * glyph->top; - if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { - int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; - colorBlack[3] = newColor[3]; - trap_R_SetColor( colorBlack ); - Text_PaintChar(x + ofs, y - yadj + ofs, - glyph->imageWidth, - glyph->imageHeight, - useScale, - glyph->s, - glyph->t, - glyph->s2, - glyph->t2, - glyph->glyph); - colorBlack[3] = 1.0; - trap_R_SetColor( newColor ); - } - else if( style == ITEM_TEXTSTYLE_NEON ) - { - vec4_t glow, outer, inner, white; - - glow[ 0 ] = newColor[ 0 ] * 0.5; - glow[ 1 ] = newColor[ 1 ] * 0.5; - glow[ 2 ] = newColor[ 2 ] * 0.5; - glow[ 3 ] = newColor[ 3 ] * 0.2; - - outer[ 0 ] = newColor[ 0 ]; - outer[ 1 ] = newColor[ 1 ]; - outer[ 2 ] = newColor[ 2 ]; - outer[ 3 ] = newColor[ 3 ]; - - inner[ 0 ] = newColor[ 0 ] * 1.5 > 1.0f ? 1.0f : newColor[ 0 ] * 1.5; - inner[ 1 ] = newColor[ 1 ] * 1.5 > 1.0f ? 1.0f : newColor[ 1 ] * 1.5; - inner[ 2 ] = newColor[ 2 ] * 1.5 > 1.0f ? 1.0f : newColor[ 2 ] * 1.5; - inner[ 3 ] = newColor[ 3 ]; - - white[ 0 ] = white[ 1 ] = white[ 2 ] = white[ 3 ] = 1.0f; - - trap_R_SetColor( glow ); - Text_PaintChar( x - 1.5, y - yadj - 1.5, - glyph->imageWidth + 3, - glyph->imageHeight + 3, - useScale, - glyph->s, - glyph->t, - glyph->s2, - glyph->t2, - glyph->glyph ); - - trap_R_SetColor( outer ); - Text_PaintChar( x - 1, y - yadj - 1, - glyph->imageWidth + 2, - glyph->imageHeight + 2, - useScale, - glyph->s, - glyph->t, - glyph->s2, - glyph->t2, - glyph->glyph ); - - trap_R_SetColor( inner ); - Text_PaintChar( x - 0.5, y - yadj - 0.5, - glyph->imageWidth + 1, - glyph->imageHeight + 1, - useScale, - glyph->s, - glyph->t, - glyph->s2, - glyph->t2, - glyph->glyph ); - - trap_R_SetColor( white ); - } - - Text_PaintChar(x, y - yadj, + yadj = useScale * glyph->top; + if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) { + int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2; + colorBlack[3] = newColor[3]; + trap_R_SetColor( colorBlack ); + Text_PaintChar(x + ofs, y - yadj + ofs, glyph->imageWidth, glyph->imageHeight, useScale, @@ -570,25 +503,92 @@ void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const cha glyph->s2, glyph->t2, glyph->glyph); + colorBlack[3] = 1.0; + trap_R_SetColor( newColor ); + } + else if( style == ITEM_TEXTSTYLE_NEON ) + { + vec4_t glow, outer, inner, white; + + glow[ 0 ] = newColor[ 0 ] * 0.5; + glow[ 1 ] = newColor[ 1 ] * 0.5; + glow[ 2 ] = newColor[ 2 ] * 0.5; + glow[ 3 ] = newColor[ 3 ] * 0.2; + + outer[ 0 ] = newColor[ 0 ]; + outer[ 1 ] = newColor[ 1 ]; + outer[ 2 ] = newColor[ 2 ]; + outer[ 3 ] = newColor[ 3 ]; + + inner[ 0 ] = newColor[ 0 ] * 1.5 > 1.0f ? 1.0f : newColor[ 0 ] * 1.5; + inner[ 1 ] = newColor[ 1 ] * 1.5 > 1.0f ? 1.0f : newColor[ 1 ] * 1.5; + inner[ 2 ] = newColor[ 2 ] * 1.5 > 1.0f ? 1.0f : newColor[ 2 ] * 1.5; + inner[ 3 ] = newColor[ 3 ]; + + white[ 0 ] = white[ 1 ] = white[ 2 ] = white[ 3 ] = 1.0f; + + trap_R_SetColor( glow ); + Text_PaintChar( x - 1.5, y - yadj - 1.5, + glyph->imageWidth + 3, + glyph->imageHeight + 3, + useScale, + glyph->s, + glyph->t, + glyph->s2, + glyph->t2, + glyph->glyph ); + + trap_R_SetColor( outer ); + Text_PaintChar( x - 1, y - yadj - 1, + glyph->imageWidth + 2, + glyph->imageHeight + 2, + useScale, + glyph->s, + glyph->t, + glyph->s2, + glyph->t2, + glyph->glyph ); + + trap_R_SetColor( inner ); + Text_PaintChar( x - 0.5, y - yadj - 0.5, + glyph->imageWidth + 1, + glyph->imageHeight + 1, + useScale, + glyph->s, + glyph->t, + glyph->s2, + glyph->t2, + glyph->glyph ); + + trap_R_SetColor( white ); + } - // CG_DrawPic(x, y - yadj, scale * uiDC.Assets.textFont.glyphs[text[i]].imageWidth, scale * uiDC.Assets.textFont.glyphs[text[i]].imageHeight, uiDC.Assets.textFont.glyphs[text[i]].glyph); - yadj = useScale * glyph2->top; - if (count == cursorPos && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) { - Text_PaintChar(x, y - yadj, - glyph2->imageWidth, - glyph2->imageHeight, - useScale, - glyph2->s, - glyph2->t, - glyph2->s2, - glyph2->t2, - glyph2->glyph); - } - - x += (glyph->xSkip * useScale); - s++; - count++; + Text_PaintChar(x, y - yadj, + glyph->imageWidth, + glyph->imageHeight, + useScale, + glyph->s, + glyph->t, + glyph->s2, + glyph->t2, + glyph->glyph); + + yadj = useScale * glyph2->top; + if (count == cursorPos && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) { + Text_PaintChar(x, y - yadj, + glyph2->imageWidth, + glyph2->imageHeight, + useScale, + glyph2->s, + glyph2->t, + glyph2->s2, + glyph2->t2, + glyph2->glyph); } + + x += (glyph->xSkip * useScale); + s++; + count++; } // need to paint cursor at end of text if (cursorPos == len && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) { @@ -611,62 +611,6 @@ void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const cha } -static void Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit) { - int len, count; - vec4_t newColor; - glyphInfo_t *glyph; - if (text) { - const char *s = text; - float max = *maxX; - float useScale; - fontInfo_t *font = &uiInfo.uiDC.Assets.textFont; - if (scale <= ui_smallFont.value) { - font = &uiInfo.uiDC.Assets.smallFont; - } else if (scale > ui_bigFont.value) { - font = &uiInfo.uiDC.Assets.bigFont; - } - useScale = scale * font->glyphScale; - trap_R_SetColor( color ); - len = strlen(text); - if (limit > 0 && len > limit) { - len = limit; - } - count = 0; - while (s && *s && count < len) { - glyph = &font->glyphs[(int)*s]; - if ( Q_IsColorString( s ) ) { - memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) ); - newColor[3] = color[3]; - trap_R_SetColor( newColor ); - s += 2; - continue; - } else { - float yadj = useScale * glyph->top; - if (Text_Width(s, useScale, 1) + x > max) { - *maxX = 0; - break; - } - Text_PaintChar(x, y - yadj, - glyph->imageWidth, - glyph->imageHeight, - useScale, - glyph->s, - glyph->t, - glyph->s2, - glyph->t2, - glyph->glyph); - x += (glyph->xSkip * useScale) + adjust; - *maxX = x; - count++; - s++; - } - } - trap_R_SetColor( NULL ); - } - -} - - void UI_ShowPostGame(qboolean newHigh) { trap_Cvar_Set ("cg_cameraOrbit", "0"); trap_Cvar_Set("cg_thirdPerson", "0"); @@ -1079,6 +1023,8 @@ void UI_Load( void ) { String_Init(); UI_LoadMenus("ui/menus.txt", qtrue); + UI_LoadMenus("ui/ingame.txt", qfalse); + UI_LoadMenus("ui/tremulous.txt", qfalse); Menus_CloseAll(); Menus_ActivateByName(lastName); @@ -1214,44 +1160,34 @@ UI_DrawInfoPane =============== */ static void UI_DrawInfoPane( menuItem_t *item, rectDef_t *rect, float text_x, float text_y, - float scale, vec4_t color, int textStyle ) + float scale, int textalign, int textvalign, vec4_t color, int textStyle ) { - float maxLeft = 0, maxTop = 0; - float maxRight = 0, maxBottom = 0; - float x = rect->x - text_x, y = rect->y - text_y, w, h; - menuDef_t dummyParent; - itemDef_t textItem; - int value = 0; - char *string = ""; - int class, credits; - char ui_currentClass[ MAX_STRING_CHARS ]; + int value = 0; + const char *s = ""; + char *string = ""; + int class, credits; + char ui_currentClass[ MAX_STRING_CHARS ]; trap_Cvar_VariableStringBuffer( "ui_currentClass", ui_currentClass, MAX_STRING_CHARS ); sscanf( ui_currentClass, "%d %d", &class, &credits ); - //offset the text - x = rect->x + maxLeft; - y = rect->y + maxTop; - w = rect->w - ( maxLeft + maxRight + 16 + ( 2 * text_x ) ); //16 to ensure text within frame - h = rect->h - ( maxTop + maxBottom ); - switch( item->type ) { case INFOTYPE_TEXT: - textItem.text = item->v.text; + s = item->v.text; break; case INFOTYPE_CLASS: value = BG_ClassCanEvolveFromTo( class, item->v.pclass, credits, 0 ); if( value < 1 ) { - textItem.text = va( "%s\n\n%s", + s = va( "%s\n\n%s", BG_FindHumanNameForClassNum( item->v.pclass ), BG_FindInfoForClassNum( item->v.pclass ) ); } else { - textItem.text = va( "%s\n\n%s\n\nKills: %d", + s = va( "%s\n\n%s\n\nKills: %d", BG_FindHumanNameForClassNum( item->v.pclass ), BG_FindInfoForClassNum( item->v.pclass ), value ); @@ -1262,13 +1198,13 @@ static void UI_DrawInfoPane( menuItem_t *item, rectDef_t *rect, float text_x, fl value = BG_FindPriceForWeapon( item->v.weapon ); if( value == 0 ) { - textItem.text = va( "%s\n\n%s\n\nCredits: Free", + s = va( "%s\n\n%s\n\nCredits: Free", BG_FindHumanNameForWeapon( item->v.weapon ), BG_FindInfoForWeapon( item->v.weapon ) ); } else { - textItem.text = va( "%s\n\n%s\n\nCredits: %d", + s = va( "%s\n\n%s\n\nCredits: %d", BG_FindHumanNameForWeapon( item->v.weapon ), BG_FindInfoForWeapon( item->v.weapon ), value ); @@ -1279,13 +1215,13 @@ static void UI_DrawInfoPane( menuItem_t *item, rectDef_t *rect, float text_x, fl value = BG_FindPriceForUpgrade( item->v.upgrade ); if( value == 0 ) { - textItem.text = va( "%s\n\n%s\n\nCredits: Free", + s = va( "%s\n\n%s\n\nCredits: Free", BG_FindHumanNameForUpgrade( item->v.upgrade ), BG_FindInfoForUpgrade( item->v.upgrade ) ); } else { - textItem.text = va( "%s\n\n%s\n\nCredits: %d", + s = va( "%s\n\n%s\n\nCredits: %d", BG_FindHumanNameForUpgrade( item->v.upgrade ), BG_FindInfoForUpgrade( item->v.upgrade ), value ); @@ -1303,13 +1239,13 @@ static void UI_DrawInfoPane( menuItem_t *item, rectDef_t *rect, float text_x, fl if( value == 0 ) { - textItem.text = va( "%s\n\n%s", + s = va( "%s\n\n%s", BG_FindHumanNameForBuildable( item->v.buildable ), BG_FindInfoForBuildable( item->v.buildable ) ); } else { - textItem.text = va( "%s\n\n%s\n\n%s: %d", + s = va( "%s\n\n%s\n\n%s: %d", BG_FindHumanNameForBuildable( item->v.buildable ), BG_FindInfoForBuildable( item->v.buildable ), string, value ); @@ -1317,30 +1253,8 @@ static void UI_DrawInfoPane( menuItem_t *item, rectDef_t *rect, float text_x, fl break; } - textItem.parent = &dummyParent; - memcpy( textItem.window.foreColor, color, sizeof( vec4_t ) ); - textItem.window.flags = 0; - - textItem.window.rect.x = x; - textItem.window.rect.y = y; - textItem.window.rect.w = w; - textItem.window.rect.h = h; - textItem.window.borderSize = 0; - textItem.textRect.x = 0; - textItem.textRect.y = 0; - textItem.textRect.w = 0; - textItem.textRect.h = 0; - textItem.textalignment = ITEM_ALIGN_LEFT; - textItem.textalignx = text_x; - textItem.textaligny = text_y; - textItem.textscale = scale; - textItem.textStyle = textStyle; - - textItem.enableCvar = NULL; - textItem.cvarTest = NULL; - - //hack to utilise existing autowrap code - Item_Text_AutoWrapped_Paint( &textItem ); + UI_DrawTextBlock( rect, text_x, text_y, color, scale, + textalign, textvalign, textStyle, s ); } @@ -1502,13 +1416,6 @@ static void UI_DrawPlayerModel(rectDef_t *rect) { } -static void UI_DrawNetSource(rectDef_t *rect, float scale, vec4_t color, int textStyle) { - if (ui_netSource.integer < 0 || ui_netSource.integer > numNetSources) { - ui_netSource.integer = 0; - } - Text_Paint(rect->x, rect->y, scale, color, va("Source: %s", netSources[ui_netSource.integer]), 0, 0, textStyle); -} - static void UI_DrawNetMapPreview(rectDef_t *rect, float scale, vec4_t color) { if (uiInfo.serverStatus.currentServerPreview > 0) { @@ -1535,14 +1442,6 @@ static void UI_DrawNetMapCinematic(rectDef_t *rect, float scale, vec4_t color) { -static void UI_DrawNetFilter(rectDef_t *rect, float scale, vec4_t color, int textStyle) { - if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) { - ui_serverFilterType.integer = 0; - } - Text_Paint(rect->x, rect->y, scale, color, va("Filter: %s", serverFilters[ui_serverFilterType.integer].description), 0, 0, textStyle); -} - - static void UI_DrawTier(rectDef_t *rect, float scale, vec4_t color, int textStyle) { int i; i = trap_Cvar_VariableValue( "ui_currentTier" ); @@ -1789,6 +1688,63 @@ static void UI_DrawOpponentName(rectDef_t *rect, float scale, vec4_t color, int Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_opponentName"), 0, 0, textStyle); } +static const char *UI_OwnerDrawText(int ownerDraw) { + const char *s = NULL; + + switch( ownerDraw ) + { + case UI_NETSOURCE: + if (ui_netSource.integer < 0 || ui_netSource.integer >= numNetSources) { + ui_netSource.integer = 0; + } + s = netSources[ui_netSource.integer]; + break; + + case UI_NETFILTER: + if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) { + ui_serverFilterType.integer = 0; + } + s = serverFilters[ui_serverFilterType.integer].description; + break; + + case UI_KEYBINDSTATUS: + if (Display_KeyBindPending()) { + s = "Waiting for new key... Press ESCAPE to cancel"; + } else { + s = "Press ENTER or CLICK to change, Press BACKSPACE to clear"; + } + break; + + case UI_SERVERREFRESHDATE: + if (uiInfo.serverStatus.refreshActive) { +#define MAX_DOTS 5 + int numServers = trap_LAN_GetServerCount( ui_netSource.integer ); + int numDots = ( uiInfo.uiDC.realTime / 500 ) % ( MAX_DOTS + 1 ); + char dots[ MAX_DOTS + 1 ]; + int i; + + for( i = 0; i < numDots; i++ ) + dots[ i ] = '.'; + + dots[ i ] = '\0'; + + s = numServers < 0 ? va( "Waiting for response%s", dots ) : + va("Getting info for %d servers (ESC to cancel)%s", numServers, dots ); + } else { + s = va("Refresh Time: %s", UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer))); + } + break; + + case UI_SERVERMOTD: + s = uiInfo.serverStatus.motd; + break; + + default: + break; + } + + return s; +} static int UI_OwnerDrawWidth(int ownerDraw, float scale) { int i, h, value; @@ -1866,16 +1822,11 @@ static int UI_OwnerDrawWidth(int ownerDraw, float scale) { s = va("%i. %s", ownerDraw-UI_REDTEAM1 + 1, text); break; case UI_NETSOURCE: - if (ui_netSource.integer < 0 || ui_netSource.integer > uiInfo.numJoinGameTypes) { - ui_netSource.integer = 0; - } - s = va("Source: %s", netSources[ui_netSource.integer]); - break; case UI_NETFILTER: - if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) { - ui_serverFilterType.integer = 0; - } - s = va("Filter: %s", serverFilters[ui_serverFilterType.integer].description ); + case UI_KEYBINDSTATUS: + case UI_SERVERREFRESHDATE: + case UI_SERVERMOTD: + s = UI_OwnerDrawText( ownerDraw ); break; case UI_TIER: break; @@ -1891,16 +1842,6 @@ static int UI_OwnerDrawWidth(int ownerDraw, float scale) { break; case UI_OPPONENT_NAME: break; - case UI_KEYBINDSTATUS: - if (Display_KeyBindPending()) { - s = "Waiting for new key... Press ESCAPE to cancel"; - } else { - s = "Press ENTER or CLICK to change, Press BACKSPACE to clear"; - } - break; - case UI_SERVERREFRESHDATE: - s = UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)); - break; default: break; } @@ -2015,193 +1956,75 @@ static void UI_DrawSelectedPlayer(rectDef_t *rect, float scale, vec4_t color, in Text_Paint(rect->x, rect->y, scale, color, name, 0, 0, textStyle); } -static void UI_DrawServerRefreshDate(rectDef_t *rect, float scale, vec4_t color, int textStyle) { - if (uiInfo.serverStatus.refreshActive) { - vec4_t lowLight, newColor; - int numServers = trap_LAN_GetServerCount( ui_netSource.integer ); - - lowLight[0] = 0.8 * color[0]; - lowLight[1] = 0.8 * color[1]; - lowLight[2] = 0.8 * color[2]; - lowLight[3] = 0.8 * color[3]; - LerpColor(color,lowLight,newColor,0.5+0.5*sin(uiInfo.uiDC.realTime / PULSE_DIVISOR)); - Text_Paint(rect->x, rect->y, scale, newColor, - numServers < 0 ? "Waiting for response..." : - va("Getting info for %d servers (ESC to cancel)", numServers), 0, 0, textStyle); - } else { - char buff[64]; - Q_strncpyz(buff, UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)), 64); - Text_Paint(rect->x, rect->y, scale, color, va("Refresh Time: %s", buff), 0, 0, textStyle); - } -} - -static void UI_DrawServerMOTD(rectDef_t *rect, float scale, vec4_t color) { - if (uiInfo.serverStatus.motdLen) { - float maxX; - - if (uiInfo.serverStatus.motdWidth == -1) { - uiInfo.serverStatus.motdWidth = 0; - uiInfo.serverStatus.motdPaintX = rect->x + 1; - uiInfo.serverStatus.motdPaintX2 = -1; - } - - if (uiInfo.serverStatus.motdOffset > uiInfo.serverStatus.motdLen) { - uiInfo.serverStatus.motdOffset = 0; - uiInfo.serverStatus.motdPaintX = rect->x + 1; - uiInfo.serverStatus.motdPaintX2 = -1; - } - - if (uiInfo.uiDC.realTime > uiInfo.serverStatus.motdTime) { - uiInfo.serverStatus.motdTime = uiInfo.uiDC.realTime + 10; - if (uiInfo.serverStatus.motdPaintX <= rect->x + 2) { - if (uiInfo.serverStatus.motdOffset < uiInfo.serverStatus.motdLen) { - uiInfo.serverStatus.motdPaintX += Text_Width(&uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], scale, 1) - 1; - uiInfo.serverStatus.motdOffset++; - } else { - uiInfo.serverStatus.motdOffset = 0; - if (uiInfo.serverStatus.motdPaintX2 >= 0) { - uiInfo.serverStatus.motdPaintX = uiInfo.serverStatus.motdPaintX2; - } else { - uiInfo.serverStatus.motdPaintX = rect->x + rect->w - 2; - } - uiInfo.serverStatus.motdPaintX2 = -1; - } - } else { - //serverStatus.motdPaintX--; - uiInfo.serverStatus.motdPaintX -= 2; - if (uiInfo.serverStatus.motdPaintX2 >= 0) { - //serverStatus.motdPaintX2--; - uiInfo.serverStatus.motdPaintX2 -= 2; - } - } - } - - maxX = rect->x + rect->w - 2; - Text_Paint_Limit(&maxX, uiInfo.serverStatus.motdPaintX, rect->y + rect->h - 3, scale, color, &uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], 0, 0); - if (uiInfo.serverStatus.motdPaintX2 >= 0) { - float maxX2 = rect->x + rect->w - 2; - Text_Paint_Limit(&maxX2, uiInfo.serverStatus.motdPaintX2, rect->y + rect->h - 3, scale, color, uiInfo.serverStatus.motd, 0, uiInfo.serverStatus.motdOffset); - } - if (uiInfo.serverStatus.motdOffset && maxX > 0) { - // if we have an offset ( we are skipping the first part of the string ) and we fit the string - if (uiInfo.serverStatus.motdPaintX2 == -1) { - uiInfo.serverStatus.motdPaintX2 = rect->x + rect->w - 2; - } - } else { - uiInfo.serverStatus.motdPaintX2 = -1; - } - - } -} - -static void UI_DrawKeyBindStatus(rectDef_t *rect, float scale, vec4_t color, int textStyle) { - if (Display_KeyBindPending()) { - Text_Paint(rect->x, rect->y, scale, color, "Waiting for new key... Press ESCAPE to cancel", 0, 0, textStyle); - } else { - Text_Paint(rect->x, rect->y, scale, color, "Press ENTER or CLICK to change, Press BACKSPACE to clear", 0, 0, textStyle); - } -} - -static void UI_DrawGLInfo(rectDef_t *rect, float scale, vec4_t color, int textStyle) { - char * eptr; - char buff[1024]; - const char *lines[64]; - int y, numLines, i; - - Text_Paint(rect->x + 2, rect->y, scale, color, va("VENDOR: %s", uiInfo.uiDC.glconfig.vendor_string), 0, 30, textStyle); - Text_Paint(rect->x + 2, rect->y + 15, scale, color, va("VERSION: %s: %s", uiInfo.uiDC.glconfig.version_string,uiInfo.uiDC.glconfig.renderer_string), 0, 30, textStyle); - Text_Paint(rect->x + 2, rect->y + 30, scale, color, va ("PIXELFORMAT: color(%d-bits) Z(%d-bits) stencil(%d-bits)", uiInfo.uiDC.glconfig.colorBits, uiInfo.uiDC.glconfig.depthBits, uiInfo.uiDC.glconfig.stencilBits), 0, 30, textStyle); - - // build null terminated extension strings - // in TA this was not directly crashing, but displaying a nasty broken shader right in the middle - // brought down the string size to 1024, there's not much that can be shown on the screen anyway - Q_strncpyz(buff, uiInfo.uiDC.glconfig.extensions_string, 1024); - eptr = buff; - y = rect->y + 45; - numLines = 0; - while ( y < rect->y + rect->h && *eptr ) - { - while ( *eptr && *eptr == ' ' ) - *eptr++ = '\0'; - - // track start of valid string - if (*eptr && *eptr != ' ') { - lines[numLines++] = eptr; - } - - while ( *eptr && *eptr != ' ' ) - eptr++; - } - - i = 0; - while (i < numLines) { - Text_Paint(rect->x + 2, y, scale, color, lines[i++], 0, 20, textStyle); - if (i < numLines) { - Text_Paint(rect->x + rect->w / 2, y, scale, color, lines[i++], 0, 20, textStyle); - } - y += 10; - if (y > rect->y + rect->h - 11) { - break; - } - } +static void UI_DrawGLInfo( rectDef_t *rect, float scale, int textalign, int textvalign, + vec4_t color, int textStyle, float text_x, float text_y) { + char buffer[ 4096 ]; + Com_sprintf( buffer, sizeof( buffer ), "VENDOR: %s\nVERSION: %s\n" + "PIXELFORMAT: color(%d-bits) Z(%d-bits) stencil(%d-bits)\n%s", + uiInfo.uiDC.glconfig.vendor_string, uiInfo.uiDC.glconfig.renderer_string, + uiInfo.uiDC.glconfig.colorBits, uiInfo.uiDC.glconfig.depthBits, + uiInfo.uiDC.glconfig.stencilBits, uiInfo.uiDC.glconfig.extensions_string ); + UI_DrawTextBlock( rect, text_x, text_y, color, scale, + textalign, textvalign, textStyle, buffer ); } // FIXME: table drive // static void UI_OwnerDraw( float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, - int ownerDrawFlags, int align, float special, + int ownerDrawFlags, int align, + int textalign, int textvalign, float special, float scale, vec4_t color, qhandle_t shader, int textStyle ) { rectDef_t rect; - rect.x = x + text_x; - rect.y = y + text_y; + rect.x = x; + rect.y = y; rect.w = w; rect.h = h; switch( ownerDraw ) { case UI_TEAMINFOPANE: - UI_DrawInfoPane( &uiInfo.tremTeamList[ uiInfo.tremTeamIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.tremTeamList[ uiInfo.tremTeamIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_ACLASSINFOPANE: - UI_DrawInfoPane( &uiInfo.alienClassList[ uiInfo.alienClassIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.alienClassList[ uiInfo.alienClassIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_AUPGRADEINFOPANE: - UI_DrawInfoPane( &uiInfo.alienUpgradeList[ uiInfo.alienUpgradeIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.alienUpgradeList[ uiInfo.alienUpgradeIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_HITEMINFOPANE: - UI_DrawInfoPane( &uiInfo.humanItemList[ uiInfo.humanItemIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.humanItemList[ uiInfo.humanItemIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_HBUYINFOPANE: - UI_DrawInfoPane( &uiInfo.humanArmouryBuyList[ uiInfo.humanArmouryBuyIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.humanArmouryBuyList[ uiInfo.humanArmouryBuyIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_HSELLINFOPANE: - UI_DrawInfoPane( &uiInfo.humanArmourySellList[ uiInfo.humanArmourySellIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.humanArmourySellList[ uiInfo.humanArmourySellIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_ABUILDINFOPANE: - UI_DrawInfoPane( &uiInfo.alienBuildList[ uiInfo.alienBuildIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.alienBuildList[ uiInfo.alienBuildIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_HBUILDINFOPANE: - UI_DrawInfoPane( &uiInfo.humanBuildList[ uiInfo.humanBuildIndex ], - &rect, text_x, text_y, scale, color, textStyle ); + UI_DrawInfoPane( &uiInfo.humanBuildList[ uiInfo.humanBuildIndex ], + &rect, text_x, text_y, scale, textalign, textvalign, color, textStyle ); break; case UI_HANDICAP: @@ -2229,7 +2052,7 @@ static void UI_OwnerDraw( float x, float y, float w, float h, UI_DrawNetGameType(&rect, scale, color, textStyle); break; case UI_JOINGAMETYPE: - UI_DrawJoinGameType(&rect, scale, color, textStyle); + UI_DrawJoinGameType(&rect, scale, color, textStyle); break; case UI_MAPPREVIEW: UI_DrawMapPreview(&rect, scale, color, qtrue); @@ -2266,18 +2089,12 @@ static void UI_OwnerDraw( float x, float y, float w, float h, case UI_REDTEAM5: UI_DrawTeamMember(&rect, scale, color, qfalse, ownerDraw - UI_REDTEAM1 + 1, textStyle); break; - case UI_NETSOURCE: - UI_DrawNetSource(&rect, scale, color, textStyle); - break; case UI_NETMAPPREVIEW: UI_DrawNetMapPreview(&rect, scale, color); break; case UI_NETMAPCINEMATIC: UI_DrawNetMapCinematic(&rect, scale, color); break; - case UI_NETFILTER: - UI_DrawNetFilter(&rect, scale, color, textStyle); - break; case UI_TIER: UI_DrawTier(&rect, scale, color, textStyle); break; @@ -2344,17 +2161,8 @@ static void UI_OwnerDraw( float x, float y, float w, float h, case UI_SELECTEDPLAYER: UI_DrawSelectedPlayer(&rect, scale, color, textStyle); break; - case UI_SERVERREFRESHDATE: - UI_DrawServerRefreshDate(&rect, scale, color, textStyle); - break; - case UI_SERVERMOTD: - UI_DrawServerMOTD(&rect, scale, color); - break; case UI_GLINFO: - UI_DrawGLInfo(&rect,scale, color, textStyle); - break; - case UI_KEYBINDSTATUS: - UI_DrawKeyBindStatus(&rect,scale, color, textStyle); + UI_DrawGLInfo(&rect, scale, textalign, textvalign, color, textStyle, text_x, text_y); break; default: break; @@ -3096,7 +2904,7 @@ static void UI_LoadHumanItems( void ) UI_ParseCarriageList =============== */ -static void UI_ParseCarriageList( int *weapons, int *upgrades ) +static void UI_ParseCarriageList( void ) { int i; char carriageCvar[ MAX_TOKEN_CHARS ]; @@ -3107,11 +2915,8 @@ static void UI_ParseCarriageList( int *weapons, int *upgrades ) trap_Cvar_VariableStringBuffer( "ui_carriage", carriageCvar, sizeof( carriageCvar ) ); iterator = carriageCvar; - if( weapons ) - *weapons = 0; - - if( upgrades ) - *upgrades = 0; + uiInfo.weapons = 0; + uiInfo.upgrades = 0; //simple parser to give rise to weapon/upgrade list while( iterator && iterator[ 0 ] != '$' ) @@ -3129,8 +2934,7 @@ static void UI_ParseCarriageList( int *weapons, int *upgrades ) i = atoi( buffer ); - if( weapons ) - *weapons |= ( 1 << i ); + uiInfo.weapons |= ( 1 << i ); } else if( iterator[ 0 ] == 'U' ) { @@ -3143,8 +2947,7 @@ static void UI_ParseCarriageList( int *weapons, int *upgrades ) i = atoi( buffer ); - if( upgrades ) - *upgrades |= ( 1 << i ); + uiInfo.upgrades |= ( 1 << i ); } iterator++; @@ -3160,20 +2963,19 @@ static void UI_LoadHumanArmouryBuys( void ) { int i, j = 0; stage_t stage = UI_GetCurrentHumanStage( ); - int weapons, upgrades; int slots = 0; - UI_ParseCarriageList( &weapons, &upgrades ); + UI_ParseCarriageList( ); for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ ) { - if( weapons & ( 1 << i ) ) + if( uiInfo.weapons & ( 1 << i ) ) slots |= BG_FindSlotsForWeapon( i ); } for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) { - if( upgrades & ( 1 << i ) ) + if( uiInfo.upgrades & ( 1 << i ) ) slots |= BG_FindSlotsForUpgrade( i ); } @@ -3186,12 +2988,12 @@ static void UI_LoadHumanArmouryBuys( void ) BG_FindStagesForWeapon( i, stage ) && BG_WeaponIsAllowed( i ) && !( BG_FindSlotsForWeapon( i ) & slots ) && - !( weapons & ( 1 << i ) ) ) + !( uiInfo.weapons & ( 1 << i ) ) ) { uiInfo.humanArmouryBuyList[ j ].text = String_Alloc( BG_FindHumanNameForWeapon( i ) ); uiInfo.humanArmouryBuyList[ j ].cmd = - String_Alloc( va( "cmd buy %s retrigger\n", BG_FindNameForWeapon( i ) ) ); + String_Alloc( va( "cmd buy %s\n", BG_FindNameForWeapon( i ) ) ); uiInfo.humanArmouryBuyList[ j ].type = INFOTYPE_WEAPON; uiInfo.humanArmouryBuyList[ j ].v.weapon = i; @@ -3208,12 +3010,12 @@ static void UI_LoadHumanArmouryBuys( void ) BG_FindStagesForUpgrade( i, stage ) && BG_UpgradeIsAllowed( i ) && !( BG_FindSlotsForUpgrade( i ) & slots ) && - !( upgrades & ( 1 << i ) ) ) + !( uiInfo.upgrades & ( 1 << i ) ) ) { uiInfo.humanArmouryBuyList[ j ].text = String_Alloc( BG_FindHumanNameForUpgrade( i ) ); uiInfo.humanArmouryBuyList[ j ].cmd = - String_Alloc( va( "cmd buy %s retrigger\n", BG_FindNameForUpgrade( i ) ) ); + String_Alloc( va( "cmd buy %s\n", BG_FindNameForUpgrade( i ) ) ); uiInfo.humanArmouryBuyList[ j ].type = INFOTYPE_UPGRADE; uiInfo.humanArmouryBuyList[ j ].v.upgrade = i; @@ -3231,19 +3033,18 @@ UI_LoadHumanArmourySells */ static void UI_LoadHumanArmourySells( void ) { - int weapons, upgrades; int i, j = 0; uiInfo.humanArmourySellCount = 0; - UI_ParseCarriageList( &weapons, &upgrades ); + UI_ParseCarriageList( ); for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ ) { - if( weapons & ( 1 << i ) ) + if( uiInfo.weapons & ( 1 << i ) ) { uiInfo.humanArmourySellList[ j ].text = String_Alloc( BG_FindHumanNameForWeapon( i ) ); uiInfo.humanArmourySellList[ j ].cmd = - String_Alloc( va( "cmd sell %s retrigger\n", BG_FindNameForWeapon( i ) ) ); + String_Alloc( va( "cmd sell %s\n", BG_FindNameForWeapon( i ) ) ); uiInfo.humanArmourySellList[ j ].type = INFOTYPE_WEAPON; uiInfo.humanArmourySellList[ j ].v.weapon = i; @@ -3255,11 +3056,11 @@ static void UI_LoadHumanArmourySells( void ) for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) { - if( upgrades & ( 1 << i ) ) + if( uiInfo.upgrades & ( 1 << i ) ) { uiInfo.humanArmourySellList[ j ].text = String_Alloc( BG_FindHumanNameForUpgrade( i ) ); uiInfo.humanArmourySellList[ j ].cmd = - String_Alloc( va( "cmd sell %s retrigger\n", BG_FindNameForUpgrade( i ) ) ); + String_Alloc( va( "cmd sell %s\n", BG_FindNameForUpgrade( i ) ) ); uiInfo.humanArmourySellList[ j ].type = INFOTYPE_UPGRADE; uiInfo.humanArmourySellList[ j ].v.upgrade = i; @@ -3270,6 +3071,26 @@ static void UI_LoadHumanArmourySells( void ) } } +/* +=============== +UI_ArmouryRefreshCb +=============== +*/ +static void UI_ArmouryRefreshCb( void *data ) +{ + int oldWeapons = uiInfo.weapons; + int oldUpgrades = uiInfo.upgrades; + + UI_ParseCarriageList( ); + + if( uiInfo.weapons != oldWeapons || uiInfo.upgrades != oldUpgrades ) + { + UI_LoadHumanArmouryBuys( ); + UI_LoadHumanArmourySells( ); + UI_RemoveCaptureFunc( ); + } +} + /* =============== UI_LoadAlienUpgrades @@ -3313,19 +3134,18 @@ UI_LoadAlienBuilds */ static void UI_LoadAlienBuilds( void ) { - int weapons; int i, j = 0; stage_t stage; - UI_ParseCarriageList( &weapons, NULL ); + UI_ParseCarriageList( ); stage = UI_GetCurrentAlienStage( ); uiInfo.alienBuildCount = 0; - for( i = BA_NONE +1; i < BA_NUM_BUILDABLES; i++ ) + for( i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++ ) { if( BG_FindTeamForBuildable( i ) == BIT_ALIENS && - BG_FindBuildWeaponForBuildable( i ) & weapons && + BG_FindBuildWeaponForBuildable( i ) & uiInfo.weapons && BG_FindStagesForBuildable( i, stage ) && BG_BuildableIsAllowed( i ) ) { @@ -3350,19 +3170,18 @@ UI_LoadHumanBuilds */ static void UI_LoadHumanBuilds( void ) { - int weapons; int i, j = 0; stage_t stage; - UI_ParseCarriageList( &weapons, NULL ); + UI_ParseCarriageList( ); stage = UI_GetCurrentHumanStage( ); uiInfo.humanBuildCount = 0; - for( i = BA_NONE +1; i < BA_NUM_BUILDABLES; i++ ) + for( i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++ ) { if( BG_FindTeamForBuildable( i ) == BIT_HUMANS && - BG_FindBuildWeaponForBuildable( i ) & weapons && + BG_FindBuildWeaponForBuildable( i ) & uiInfo.weapons && BG_FindStagesForBuildable( i, stage ) && BG_BuildableIsAllowed( i ) ) { @@ -3848,6 +3667,8 @@ static void UI_RunMenuScript(char **args) { { if( ( cmd = uiInfo.humanArmouryBuyList[ uiInfo.humanArmouryBuyIndex ].cmd ) ) trap_Cmd_ExecuteText( EXEC_APPEND, cmd ); + + UI_InstallCaptureFunc( UI_ArmouryRefreshCb, NULL, 1000 ); } else if( Q_stricmp( name, "LoadHumanArmourySells" ) == 0 ) UI_LoadHumanArmourySells( ); @@ -3855,6 +3676,8 @@ static void UI_RunMenuScript(char **args) { { if( ( cmd = uiInfo.humanArmourySellList[ uiInfo.humanArmourySellIndex ].cmd ) ) trap_Cmd_ExecuteText( EXEC_APPEND, cmd ); + + UI_InstallCaptureFunc( UI_ArmouryRefreshCb, NULL, 1000 ); } else if( Q_stricmp( name, "LoadAlienUpgrades" ) == 0 ) { @@ -5366,6 +5189,8 @@ void _UI_Init( qboolean inGameLoad ) { uiInfo.uiDC.drawText = &Text_Paint; uiInfo.uiDC.textWidth = &Text_Width; uiInfo.uiDC.textHeight = &Text_Height; + uiInfo.uiDC.textEmWidth = &Text_EmWidth; + uiInfo.uiDC.textEmHeight = &Text_EmHeight; uiInfo.uiDC.registerModel = &trap_R_RegisterModel; uiInfo.uiDC.modelBounds = &trap_R_ModelBounds; uiInfo.uiDC.fillRect = &UI_FillRect; @@ -5402,6 +5227,7 @@ void _UI_Init( qboolean inGameLoad ) { uiInfo.uiDC.Print = &Com_Printf; uiInfo.uiDC.Pause = &UI_Pause; uiInfo.uiDC.ownerDrawWidth = &UI_OwnerDrawWidth; + uiInfo.uiDC.ownerDrawText = &UI_OwnerDrawText; uiInfo.uiDC.registerSound = &trap_S_RegisterSound; uiInfo.uiDC.startBackgroundTrack = &trap_S_StartBackgroundTrack; uiInfo.uiDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack; @@ -5974,6 +5800,7 @@ vmCvar_t ui_recordSPDemo; vmCvar_t ui_realCaptureLimit; vmCvar_t ui_realWarmUp; vmCvar_t ui_serverStatusTimeOut; +vmCvar_t ui_textWrapCache; vmCvar_t ui_winner; @@ -6100,6 +5927,7 @@ static cvarTable_t cvarTable[] = { { &ui_realWarmUp, "g_warmup", "20", CVAR_ARCHIVE}, { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART}, { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE}, + { &ui_textWrapCache, "ui_textWrapCache", "1", CVAR_ARCHIVE }, }; static int cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]); diff --git a/src/ui/ui_shared.c b/src/ui/ui_shared.c index 0a8a3c25..3224b697 100644 --- a/src/ui/ui_shared.c +++ b/src/ui/ui_shared.c @@ -45,7 +45,8 @@ static scrollInfo_t scrollInfo; void voidFunction( void *var ) { return; } qboolean voidFunction2( itemDef_t *var1, int var2 ) { return qfalse; } -static void (*captureFunc) (void *p) = voidFunction; +static CaptureFunc *captureFunc = voidFunction; +static int captureFuncExpiry = 0; static void *captureData = NULL; static itemDef_t *itemCapture = NULL; // item that has the mouse captured ( if any ) @@ -77,6 +78,34 @@ itemDef_t *Menu_SetPrevCursorItem(menuDef_t *menu); itemDef_t *Menu_SetNextCursorItem(menuDef_t *menu); static qboolean Menu_OverActiveItem(menuDef_t *menu, float x, float y); +/* +=============== +UI_InstallCaptureFunc +=============== +*/ +void UI_InstallCaptureFunc( CaptureFunc *f, void *data, int timeout ) +{ + captureFunc = f; + captureData = data; + + if( timeout > 0 ) + captureFuncExpiry = DC->realTime + timeout; + else + captureFuncExpiry = 0; +} + +/* +=============== +UI_RemoveCaptureFunc +=============== +*/ +void UI_RemoveCaptureFunc( void ) +{ + captureFunc = voidFunction; + captureData = NULL; + captureFuncExpiry = 0; +} + #ifdef CGAME #define MEM_POOL_SIZE 128 * 1024 #else @@ -333,6 +362,238 @@ qboolean Float_Parse(char **p, float *f) { } } +#define MAX_EXPR_ELEMENTS 32 + +typedef enum +{ + EXPR_OPERATOR, + EXPR_VALUE +} exprType_t; + +typedef struct exprToken_s +{ + exprType_t type; + union + { + char op; + float val; + } u; +} exprToken_t; + +typedef struct exprList_s +{ + exprToken_t l[ MAX_EXPR_ELEMENTS ]; + int f, b; +} exprList_t; + +/* +================= +OpPrec + +Return a value reflecting operator precedence +================= +*/ +static ID_INLINE int OpPrec( char op ) +{ + switch( op ) + { + case '*': return 4; + case '/': return 3; + case '+': return 2; + case '-': return 1; + case '(': return 0; + default: return -1; + } +} + +/* +================= +PC_Expression_Parse +================= +*/ +static qboolean PC_Expression_Parse( int handle, float *f ) +{ + pc_token_t token; + int unmatchedParentheses = 0; + exprList_t stack, fifo; + exprToken_t value; + qboolean expectingNumber = qtrue; + +#define FULL( a ) ( a.b >= ( MAX_EXPR_ELEMENTS - 1 ) ) +#define EMPTY( a ) ( a.f > a.b ) + +#define PUSH_VAL( a, v ) \ + { \ + if( FULL( a ) ) \ + return qfalse; \ + a.b++; \ + a.l[ a.b ].type = EXPR_VALUE; \ + a.l[ a.b ].u.val = v; \ + } + +#define PUSH_OP( a, o ) \ + { \ + if( FULL( a ) ) \ + return qfalse; \ + a.b++; \ + a.l[ a.b ].type = EXPR_OPERATOR; \ + a.l[ a.b ].u.op = o; \ + } + +#define POP_STACK( a ) \ + { \ + if( EMPTY( a ) ) \ + return qfalse; \ + value = a.l[ a.b ]; \ + a.b--; \ + } + +#define PEEK_STACK_OP( a ) ( a.l[ a.b ].u.op ) +#define PEEK_STACK_VAL( a ) ( a.l[ a.b ].u.val ) + +#define POP_FIFO( a ) \ + { \ + if( EMPTY( a ) ) \ + return qfalse; \ + value = a.l[ a.f ]; \ + a.f++; \ + } + + stack.f = fifo.f = 0; + stack.b = fifo.b = -1; + + while( trap_Parse_ReadToken( handle, &token ) ) + { + if( !unmatchedParentheses && token.string[ 0 ] == ')' ) + break; + + // Special case to catch negative numbers + if( expectingNumber && token.string[ 0 ] == '-' ) + { + if( !trap_Parse_ReadToken( handle, &token ) ) + return qfalse; + + token.floatvalue = -token.floatvalue; + } + + if( token.type == TT_NUMBER ) + { + if( !expectingNumber ) + return qfalse; + expectingNumber = !expectingNumber; + + PUSH_VAL( fifo, token.floatvalue ); + } + else + { + switch( token.string[ 0 ] ) + { + case '(': + unmatchedParentheses++; + PUSH_OP( stack, '(' ); + break; + + case ')': + unmatchedParentheses--; + if( unmatchedParentheses < 0 ) + return qfalse; + + while( !EMPTY( stack ) && PEEK_STACK_OP( stack ) != '(' ) + { + POP_STACK( stack ); + PUSH_OP( fifo, value.u.op ); + } + + // Pop the '(' + POP_STACK( stack ); + break; + + case '*': + case '/': + case '+': + case '-': + if( expectingNumber ) + return qfalse; + expectingNumber = !expectingNumber; + + if( EMPTY( stack ) ) + { + PUSH_OP( stack, token.string[ 0 ] ); + } + else + { + while( !EMPTY( stack ) && OpPrec( token.string[ 0 ] ) < OpPrec( PEEK_STACK_OP( stack ) ) ) + { + POP_STACK( stack ); + PUSH_OP( fifo, value.u.op ); + } + + PUSH_OP( stack, token.string[ 0 ] ); + } + break; + + default: + // Unknown token + return qfalse; + } + } + } + + while( !EMPTY( stack ) ) + { + POP_STACK( stack ); + PUSH_OP( fifo, value.u.op ); + } + + while( !EMPTY( fifo ) ) + { + POP_FIFO( fifo ); + + if( value.type == EXPR_VALUE ) + { + PUSH_VAL( stack, value.u.val ); + } + else if( value.type == EXPR_OPERATOR ) + { + char op = value.u.op; + float operand1, operand2, result; + + POP_STACK( stack ); + operand2 = value.u.val; + POP_STACK( stack ); + operand1 = value.u.val; + + switch( op ) + { + case '*': result = operand1 * operand2; break; + case '/': result = operand1 / operand2; break; + case '+': result = operand1 + operand2; break; + case '-': result = operand1 - operand2; break; + default: + Com_Error( ERR_FATAL, "Unknown operator '%c' in postfix string", op ); + return qfalse; + } + + PUSH_VAL( stack, result ); + } + } + + POP_STACK( stack ); + + *f = value.u.val; + + return qtrue; + +#undef FULL +#undef EMPTY +#undef PUSH_VAL +#undef PUSH_OP +#undef POP_STACK +#undef PEEK_STACK_OP +#undef PEEK_STACK_VAL +#undef POP_FIFO +} + /* ================= PC_Float_Parse @@ -344,6 +605,10 @@ qboolean PC_Float_Parse(int handle, float *f) { if (!trap_Parse_ReadToken(handle, &token)) return qfalse; + + if( token.string[ 0 ] == '(' ) + return PC_Expression_Parse( handle, f ); + if (token.string[0] == '-') { if (!trap_Parse_ReadToken(handle, &token)) return qfalse; @@ -424,6 +689,20 @@ qboolean PC_Int_Parse(int handle, int *i) { if (!trap_Parse_ReadToken(handle, &token)) return qfalse; + + if( token.string[ 0 ] == '(' ) + { + float f; + + if( PC_Expression_Parse( handle, &f ) ) + { + *i = (int)f; + return qtrue; + } + else + return qfalse; + } + if (token.string[0] == '-') { if (!trap_Parse_ReadToken(handle, &token)) return qfalse; @@ -611,8 +890,7 @@ void Fade(int *flags, float *f, float clamp, int *nextTime, int offsetTime, qboo -void Window_Paint(Window *w, float fadeAmount, float fadeClamp, float fadeCycle) { - //float bordersize = 0; +static void Window_Paint(Window *w, float fadeAmount, float fadeClamp, float fadeCycle) { vec4_t color; rectDef_t fillRect = w->rect; @@ -669,6 +947,14 @@ void Window_Paint(Window *w, float fadeAmount, float fadeClamp, float fadeCycle) DC->drawCinematic(w->cinematic, fillRect.x, fillRect.y, fillRect.w, fillRect.h); } } +} + +static void Border_Paint(Window *w) { + vec4_t color; + + if (w == NULL || (w->style == 0 && w->border == 0)) { + return; + } if (w->border == WINDOW_BORDER_FULL) { // full @@ -706,7 +992,6 @@ void Window_Paint(Window *w, float fadeAmount, float fadeClamp, float fadeCycle) r.y = w->rect.y + w->rect.h - 1; GradientBar_Paint(&r, w->borderColor); } - } @@ -1490,7 +1775,7 @@ float Item_Slider_ThumbPosition(itemDef_t *item) { editFieldDef_t *editDef = item->typeData; if (item->text) { - x = item->textRect.x + item->textRect.w + 8; + x = item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET; } else { x = item->window.rect.x; } @@ -1510,21 +1795,29 @@ float Item_Slider_ThumbPosition(itemDef_t *item) { range = editDef->maxVal - editDef->minVal; value -= editDef->minVal; value /= range; - //value /= (editDef->maxVal - editDef->minVal); value *= SLIDER_WIDTH; x += value; - // vm fuckage - //x = x + (((float)value / editDef->maxVal) * SLIDER_WIDTH); + return x; } +static float Item_Slider_VScale( itemDef_t *item ) +{ + if( SLIDER_THUMB_HEIGHT > item->window.rect.h ) + return item->window.rect.h / SLIDER_THUMB_HEIGHT; + else + return 1.0f; +} + int Item_Slider_OverSlider(itemDef_t *item, float x, float y) { rectDef_t r; + float vScale = Item_Slider_VScale( item ); r.x = Item_Slider_ThumbPosition(item) - (SLIDER_THUMB_WIDTH / 2); - r.y = item->window.rect.y - 2; + r.y = item->textRect.y - item->textRect.h + + ( ( item->textRect.h - ( SLIDER_THUMB_HEIGHT * vScale ) ) / 2.0f ); r.w = SLIDER_THUMB_WIDTH; - r.h = SLIDER_THUMB_HEIGHT; + r.h = SLIDER_THUMB_HEIGHT * vScale; if (Rect_ContainsPoint(&r, x, y)) { return WINDOW_LB_THUMB; @@ -2058,148 +2351,182 @@ qboolean Item_Multi_HandleKey(itemDef_t *item, int key) { return qfalse; } -qboolean Item_TextField_HandleKey(itemDef_t *item, int key) { +#define MIN_FIELD_WIDTH 10 +#define EDIT_CURSOR_WIDTH 10 + +static void Item_TextField_CalcPaintOffset( itemDef_t *item, char *buff ) +{ + editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; + + if( item->cursorPos < editPtr->paintOffset ) + { + editPtr->paintOffset = item->cursorPos; + } + else + { + // If there is a maximum field width + if( editPtr->maxFieldWidth > 0 ) + { + // If the cursor is at the end of the string, maximise the amount of the + // string that's visible + if( buff[ item->cursorPos + 1 ] == '\0' ) + { + while( DC->textWidth( &buff[ editPtr->paintOffset ], item->textscale, 0 ) <= + ( editPtr->maxFieldWidth - EDIT_CURSOR_WIDTH ) && editPtr->paintOffset > 0 ) + editPtr->paintOffset--; + } + + buff[ item->cursorPos + 1 ] = '\0'; + + // Shift paintOffset so that the cursor is visible + while( DC->textWidth( &buff[ editPtr->paintOffset ], item->textscale, 0 ) > + ( editPtr->maxFieldWidth - EDIT_CURSOR_WIDTH ) ) + editPtr->paintOffset++; + } + } +} + +qboolean Item_TextField_HandleKey(itemDef_t *item, int key) +{ char buff[1024]; int len; itemDef_t *newItem = NULL; editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; + qboolean releaseFocus = qtrue; - if (item->cvar) { + if( item->cvar ) + { + Com_Memset( buff, 0, sizeof( buff ) ); + DC->getCVarString( item->cvar, buff, sizeof( buff ) ); + len = strlen( buff ); - memset(buff, 0, sizeof(buff)); - DC->getCVarString(item->cvar, buff, sizeof(buff)); - len = strlen(buff); - if (editPtr->maxChars && len > editPtr->maxChars) { + if( editPtr->maxChars && len > editPtr->maxChars ) len = editPtr->maxChars; - } - if ( key & K_CHAR_FLAG ) { - key &= ~K_CHAR_FLAG; + if( key & K_CHAR_FLAG ) + { + key &= ~K_CHAR_FLAG; - if (key == 'h' - 'a' + 1 ) { // ctrl-h is backspace - if ( item->cursorPos > 0 ) { + if( key == 'h' - 'a' + 1 ) + { + // ctrl-h is backspace + if ( item->cursorPos > 0 ) + { memmove( &buff[item->cursorPos - 1], &buff[item->cursorPos], len + 1 - item->cursorPos); item->cursorPos--; - if (item->cursorPos < editPtr->paintOffset) { - editPtr->paintOffset--; - } } + DC->setCVar(item->cvar, buff); - return qtrue; } - - - // - // ignore any non printable chars - // - if ( key < 32 || !item->cvar) { - return qtrue; - } - - if (item->type == ITEM_TYPE_NUMERICFIELD) { - if (key < '0' || key > '9') { - return qfalse; - } + else if( key < 32 || !item->cvar ) + { + // Ignore any non printable chars + releaseFocus = qfalse; + goto exit; + } + else if( item->type == ITEM_TYPE_NUMERICFIELD && ( key < '0' || key > '9' ) ) + { + // Ignore non-numeric characters + releaseFocus = qfalse; + goto exit; } + else + { + if (!DC->getOverstrikeMode()) + { + if (( len == MAX_EDITFIELD - 1 ) || (editPtr->maxChars && len >= editPtr->maxChars)) + { + // Reached maximum field length + releaseFocus = qfalse; + goto exit; + } - if (!DC->getOverstrikeMode()) { - if (( len == MAX_EDITFIELD - 1 ) || (editPtr->maxChars && len >= editPtr->maxChars)) { - return qtrue; + memmove( &buff[item->cursorPos + 1], &buff[item->cursorPos], len + 1 - item->cursorPos ); } - memmove( &buff[item->cursorPos + 1], &buff[item->cursorPos], len + 1 - item->cursorPos ); - } else { - if (editPtr->maxChars && item->cursorPos >= editPtr->maxChars) { - return qtrue; + else + { + // Reached maximum field length + if (editPtr->maxChars && item->cursorPos >= editPtr->maxChars) + releaseFocus = qfalse; + goto exit; } - } - buff[item->cursorPos] = key; + buff[ item->cursorPos ] = key; - DC->setCVar(item->cvar, buff); + DC->setCVar( item->cvar, buff ); - if (item->cursorPos < len + 1) { - item->cursorPos++; - if (editPtr->maxPaintChars && item->cursorPos > editPtr->maxPaintChars) { - editPtr->paintOffset++; - } + if( item->cursorPos < len + 1 ) + item->cursorPos++; } + } + else + { + switch( key ) + { + case K_DEL: + case K_KP_DEL: + if( item->cursorPos < len ) + { + memmove( buff + item->cursorPos, buff + item->cursorPos + 1, len - item->cursorPos); + DC->setCVar(item->cvar, buff); + } + break; - } else { - - if ( key == K_DEL || key == K_KP_DEL ) { - if ( item->cursorPos < len ) { - memmove( buff + item->cursorPos, buff + item->cursorPos + 1, len - item->cursorPos); - DC->setCVar(item->cvar, buff); - } - return qtrue; - } + case K_RIGHTARROW: + case K_KP_RIGHTARROW: + if( item->cursorPos < len ) + item->cursorPos++; + break; - if ( key == K_RIGHTARROW || key == K_KP_RIGHTARROW ) - { - if (editPtr->maxPaintChars && item->cursorPos >= editPtr->maxPaintChars && item->cursorPos < len) { - item->cursorPos++; - editPtr->paintOffset++; - return qtrue; - } - if (item->cursorPos < len) { - item->cursorPos++; - } - return qtrue; - } + case K_LEFTARROW: + case K_KP_LEFTARROW: + if( item->cursorPos > 0 ) + item->cursorPos--; + break; - if ( key == K_LEFTARROW || key == K_KP_LEFTARROW ) - { - if ( item->cursorPos > 0 ) { - item->cursorPos--; - } - if (item->cursorPos < editPtr->paintOffset) { - editPtr->paintOffset--; - } - return qtrue; - } + case K_HOME: + case K_KP_HOME: + item->cursorPos = 0; + break; - if ( key == K_HOME || key == K_KP_HOME) {// || ( tolower(key) == 'a' && trap_Key_IsDown( K_CTRL ) ) ) { - item->cursorPos = 0; - editPtr->paintOffset = 0; - return qtrue; - } + case K_END: + case K_KP_END: + item->cursorPos = len; + break; - if ( key == K_END || key == K_KP_END) {// ( tolower(key) == 'e' && trap_Key_IsDown( K_CTRL ) ) ) { - item->cursorPos = len; - if(item->cursorPos > editPtr->maxPaintChars) { - editPtr->paintOffset = len - editPtr->maxPaintChars; - } - return qtrue; - } + case K_INS: + case K_KP_INS: + DC->setOverstrikeMode(!DC->getOverstrikeMode()); + break; - if ( key == K_INS || key == K_KP_INS ) { - DC->setOverstrikeMode(!DC->getOverstrikeMode()); - return qtrue; - } - } + case K_TAB: + case K_DOWNARROW: + case K_KP_DOWNARROW: + case K_UPARROW: + case K_KP_UPARROW: + newItem = Menu_SetNextCursorItem(item->parent); + if( newItem && ( newItem->type == ITEM_TYPE_EDITFIELD || newItem->type == ITEM_TYPE_NUMERICFIELD ) ) + g_editItem = newItem; + break; - if (key == K_TAB || key == K_DOWNARROW || key == K_KP_DOWNARROW) { - newItem = Menu_SetNextCursorItem(item->parent); - if (newItem && (newItem->type == ITEM_TYPE_EDITFIELD || newItem->type == ITEM_TYPE_NUMERICFIELD)) { - g_editItem = newItem; - } - } + case K_ENTER: + case K_KP_ENTER: + case K_ESCAPE: + releaseFocus = qtrue; + goto exit; - if (key == K_UPARROW || key == K_KP_UPARROW) { - newItem = Menu_SetPrevCursorItem(item->parent); - if (newItem && (newItem->type == ITEM_TYPE_EDITFIELD || newItem->type == ITEM_TYPE_NUMERICFIELD)) { - g_editItem = newItem; + default: + break; } } - if ( key == K_ENTER || key == K_KP_ENTER || key == K_ESCAPE) { - return qfalse; - } - - return qtrue; + releaseFocus = qfalse; } - return qfalse; +exit: + Item_TextField_CalcPaintOffset( item, buff ); + + return !releaseFocus; } static void Scroll_ListBox_AutoFunc(void *p) { @@ -2287,7 +2614,7 @@ static void Scroll_Slider_ThumbFunc(void *p) { editFieldDef_t *editDef = si->item->typeData; if (si->item->text) { - x = si->item->textRect.x + si->item->textRect.w + 8; + x = si->item->textRect.x + si->item->textRect.w + ITEM_VALUE_OFFSET; } else { x = si->item->window.rect.x; } @@ -2308,6 +2635,11 @@ static void Scroll_Slider_ThumbFunc(void *p) { void Item_StartCapture(itemDef_t *item, int key) { int flags; + + // Don't allow captureFunc to be overridden + if( captureFunc != voidFunction ) + return; + switch (item->type) { case ITEM_TYPE_EDITFIELD: case ITEM_TYPE_NUMERICFIELD: @@ -2322,16 +2654,14 @@ void Item_StartCapture(itemDef_t *item, int key) { scrollInfo.scrollKey = key; scrollInfo.scrollDir = (flags & WINDOW_LB_LEFTARROW) ? qtrue : qfalse; scrollInfo.item = item; - captureData = &scrollInfo; - captureFunc = &Scroll_ListBox_AutoFunc; + UI_InstallCaptureFunc( Scroll_ListBox_AutoFunc, &scrollInfo, 0 ); itemCapture = item; } else if (flags & WINDOW_LB_THUMB) { scrollInfo.scrollKey = key; scrollInfo.item = item; scrollInfo.xStart = DC->cursorx; scrollInfo.yStart = DC->cursory; - captureData = &scrollInfo; - captureFunc = &Scroll_ListBox_ThumbFunc; + UI_InstallCaptureFunc( Scroll_ListBox_ThumbFunc, &scrollInfo, 0 ); itemCapture = item; } break; @@ -2344,8 +2674,7 @@ void Item_StartCapture(itemDef_t *item, int key) { scrollInfo.item = item; scrollInfo.xStart = DC->cursorx; scrollInfo.yStart = DC->cursory; - captureData = &scrollInfo; - captureFunc = &Scroll_Slider_ThumbFunc; + UI_InstallCaptureFunc( Scroll_Slider_ThumbFunc, &scrollInfo, 0 ); itemCapture = item; } break; @@ -2367,7 +2696,7 @@ qboolean Item_Slider_HandleKey(itemDef_t *item, int key, qboolean down) { rectDef_t testRect; width = SLIDER_WIDTH; if (item->text) { - x = item->textRect.x + item->textRect.w + 8; + x = item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET; } else { x = item->window.rect.x; } @@ -2399,8 +2728,7 @@ qboolean Item_HandleKey(itemDef_t *item, int key, qboolean down) { if (itemCapture) { Item_StopCapture(itemCapture); itemCapture = NULL; - captureFunc = voidFunction; - captureData = NULL; + UI_RemoveCaptureFunc( ); } else { if ( down && ( key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3 ) ) { Item_StartCapture(item, key); @@ -2750,10 +3078,15 @@ void Menu_HandleKey(menuDef_t *menu, int key, qboolean down) { } } else if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) { if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { - item->cursorPos = 0; + char buffer[ MAX_STRING_CHARS ] = { 0 }; + + if( item->cvar ) + DC->getCVarString( item->cvar, buffer, sizeof( buffer ) ); + + item->cursorPos = strlen( buffer ); + Item_TextField_CalcPaintOffset( item, buffer ); g_editingField = qtrue; g_editItem = item; - DC->setOverstrikeMode(qtrue); } } else { if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { @@ -2788,10 +3121,15 @@ void Menu_HandleKey(menuDef_t *menu, int key, qboolean down) { case K_ENTER: if (item) { if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) { - item->cursorPos = 0; + char buffer[ MAX_STRING_CHARS ] = { 0 }; + + if( item->cvar ) + DC->getCVarString( item->cvar, buffer, sizeof( buffer ) ); + + item->cursorPos = strlen( buffer ); + Item_TextField_CalcPaintOffset( item, buffer ); g_editingField = qtrue; g_editItem = item; - DC->setOverstrikeMode(qtrue); } else { Item_Action(item); } @@ -2826,26 +3164,37 @@ void Item_SetTextExtents(itemDef_t *item, int *width, int *height, const char *t // keeps us from computing the widths and heights more than once if (*width == 0 || (item->type == ITEM_TYPE_OWNERDRAW && item->textalignment == ITEM_ALIGN_CENTER)) { - int originalWidth = DC->textWidth(item->text, item->textscale, 0); + int originalWidth; - if (item->type == ITEM_TYPE_OWNERDRAW && (item->textalignment == ITEM_ALIGN_CENTER || item->textalignment == ITEM_ALIGN_RIGHT)) { - originalWidth += DC->ownerDrawWidth(item->window.ownerDraw, item->textscale); - } else if (item->type == ITEM_TYPE_EDITFIELD && item->textalignment == ITEM_ALIGN_CENTER && item->cvar) { + if (item->type == ITEM_TYPE_EDITFIELD && item->textalignment == ITEM_ALIGN_CENTER && item->cvar) { + //FIXME: this will only be called once? char buff[256]; DC->getCVarString(item->cvar, buff, 256); - originalWidth += DC->textWidth(buff, item->textscale, 0); + originalWidth = DC->textWidth(item->text, item->textscale, 0) + + DC->textWidth(buff, item->textscale, 0); + } else { + originalWidth = DC->textWidth(item->text, item->textscale, 0); } *width = DC->textWidth(textPtr, item->textscale, 0); *height = DC->textHeight(textPtr, item->textscale, 0); item->textRect.w = *width; item->textRect.h = *height; - item->textRect.x = item->textalignx; - item->textRect.y = item->textaligny; - if (item->textalignment == ITEM_ALIGN_RIGHT) { - item->textRect.x = item->textalignx - originalWidth; + + if (item->textvalignment == ITEM_VALIGN_BOTTOM) { + item->textRect.y = item->textaligny + item->window.rect.h; + } else if (item->textvalignment == ITEM_VALIGN_CENTER) { + item->textRect.y = item->textaligny + ( ( *height + item->window.rect.h ) / 2.0f ); + } else if (item->textvalignment == ITEM_VALIGN_TOP) { + item->textRect.y = item->textaligny + *height; + } + + if (item->textalignment == ITEM_ALIGN_LEFT) { + item->textRect.x = item->textalignx; } else if (item->textalignment == ITEM_ALIGN_CENTER) { - item->textRect.x = item->textalignx - originalWidth / 2; + item->textRect.x = item->textalignx + ( ( item->window.rect.w - originalWidth ) / 2.0f ); + } else if (item->textalignment == ITEM_ALIGN_RIGHT) { + item->textRect.x = item->textalignx + item->window.rect.w - originalWidth; } ToWindowCoords(&item->textRect.x, &item->textRect.y, &item->window); @@ -2878,140 +3227,215 @@ void Item_TextColor(itemDef_t *item, vec4_t *newColor) { } } -int Item_Text_AutoWrapped_Lines( itemDef_t *item ) +static const char *Item_Text_Wrap( const char *text, float scale, float width ) { - char text[ 1024 ]; - const char *p, *textPtr, *newLinePtr; - char buff[ 1024 ]; - int len, textWidth, newLine; - int lines = 0; + static char out[ 8192 ]; + char *paint = out; + char c[ 3 ]; + const char *p = text; + const char *eol; + const char *q = NULL, *qMinus1 = NULL; + unsigned int testLength; + unsigned int i; - textWidth = 0; - newLinePtr = NULL; + if( strlen( text ) >= sizeof( out ) ) + return NULL; - if( item->text == NULL ) + *paint = '\0'; + + while( *p ) { - if( item->cvar == NULL ) - return 0; - else + Com_Memset( c, 0, sizeof( c ) ); + + // Skip leading whitespace + while( *p ) { - DC->getCVarString( item->cvar, text, sizeof( text ) ); - textPtr = text; + if( Q_IsColorString( p ) ) + { + c[ 0 ] = p[ 0 ]; + c[ 1 ] = p[ 1 ]; + p += 2; + } + else if( *p != '\n' && isspace( *p ) ) + p++; + else + break; } - } - else - textPtr = item->text; - if( *textPtr == '\0' ) - return 0; - - len = 0; - buff[ 0 ] = '\0'; - newLine = 0; - p = textPtr; + if( !*p ) + break; - while( p ) - { - textWidth = DC->textWidth( buff, item->textscale, 0 ); + testLength = 1; + eol = p; - if( *p == ' ' || *p == '\t' || *p == '\n' || *p == '\0' ) + while( DC->textWidth( p, scale, testLength ) < width ) { - newLine = len; - newLinePtr = p + 1; - } + if( testLength >= strlen( p ) ) + { + eol = p + strlen( p ); + break; + } - // forceably split lines that are too long (where normal splitage has failed) - if( textWidth > item->window.rect.w && newLine == 0 && *p != '\n' ) - { - newLine = len; - newLinePtr = p; - } + // Point q at the end of the current testLength + q = p; + for( i = 0; i < testLength; ) + { + // Skip color escapes + while( Q_IsColorString( q ) ) + { + q += 2; + continue; + } - if( ( newLine && textWidth > item->window.rect.w ) || *p == '\n' || *p == '\0' ) - { - if( len ) - buff[ newLine ] = '\0'; + qMinus1 = q; + q++; + i++; + } - if( !( *p == '\n' && !*( p + 1 ) ) ) - lines++; + // Some color escapes might still be present + while( Q_IsColorString( q ) ) + q += 2; - if( *p == '\0' ) + // Manual line break + if( *q == '\n' ) + { + eol = q + 1; break; + } - // - p = newLinePtr; - len = 0; - newLine = 0; + if( !isspace( *qMinus1 ) && isspace( *q ) ) + eol = q; - continue; + testLength++; } - buff[ len++ ] = *p++; - buff[ len ] = '\0'; + // No split has taken place, so just split mid-word + if( eol == p ) + eol = q; + + // Add colour code (might be empty) + Q_strcat( out, sizeof( out ), c ); + paint = out + strlen( out ); + + // Copy text + strncpy( paint, p, eol - p ); + paint[ eol - p ] = '\0'; + + // Add a \n if it's not there already + if( out[ strlen( out ) - 1 ] != '\n' ) + Q_strcat( out, sizeof( out ), "\n" ); + + paint = out + strlen( out ); + p = eol; } - return lines; + return out; } -#define MAX_AUTOWRAP_CACHE 16 -#define MAX_AUTOWRAP_LINES 32 -#define MAX_AUTOWRAP_TEXT 512 +#define MAX_WRAP_CACHE 16 +#define MAX_WRAP_LINES 32 +#define MAX_WRAP_TEXT 512 typedef struct { - //this is used purely for checking for cache hits - char text[ MAX_AUTOWRAP_TEXT * MAX_AUTOWRAP_LINES ]; + char text[ MAX_WRAP_TEXT * MAX_WRAP_LINES ]; //FIXME: augment with hash? rectDef_t rect; - int textWidth, textHeight; - char lines[ MAX_AUTOWRAP_LINES ][ MAX_AUTOWRAP_TEXT ]; - int lineOffsets[ MAX_AUTOWRAP_LINES ][ 2 ]; + float scale; + char lines[ MAX_WRAP_LINES ][ MAX_WRAP_TEXT ]; + float lineCoords[ MAX_WRAP_LINES ][ 2 ]; int numLines; -} autoWrapCache_t; +} wrapCache_t; -static int cacheIndex = 0; -static autoWrapCache_t awc[ MAX_AUTOWRAP_CACHE ]; +static wrapCache_t wrapCache[ MAX_WRAP_CACHE ]; +static int cacheWriteIndex = 0; +static int cacheReadIndex = 0; +static int cacheReadLineNum = 0; -static int checkCache( const char *text, rectDef_t *rect, int width, int height ) +static void UI_CreateCacheEntry( const char *text, rectDef_t *rect, float scale ) +{ + wrapCache_t *cacheEntry = &wrapCache[ cacheWriteIndex ]; + + Q_strncpyz( cacheEntry->text, text, sizeof( cacheEntry->text ) ); + cacheEntry->rect.x = rect->x; + cacheEntry->rect.y = rect->y; + cacheEntry->rect.w = rect->w; + cacheEntry->rect.h = rect->h; + cacheEntry->scale = scale; + cacheEntry->numLines = 0; +} + +static void UI_AddCacheEntryLine( const char *text, float x, float y ) +{ + wrapCache_t *cacheEntry = &wrapCache[ cacheWriteIndex ]; + + if( cacheEntry->numLines >= MAX_WRAP_LINES ) + return; + + Q_strncpyz( cacheEntry->lines[ cacheEntry->numLines ], text, + sizeof( cacheEntry->lines[ 0 ] ) ); + cacheEntry->lineCoords[ cacheEntry->numLines ][ 0 ] = x; + cacheEntry->lineCoords[ cacheEntry->numLines ][ 1 ] = y; + + cacheEntry->numLines++; +} + +static void UI_FinishCacheEntry( void ) +{ + cacheWriteIndex = ( cacheWriteIndex + 1 ) % MAX_WRAP_CACHE; +} + +static qboolean UI_CheckWrapCache( const char *text, rectDef_t *rect, float scale ) { int i; - for( i = 0; i < MAX_AUTOWRAP_CACHE; i++ ) + for( i = 0; i < MAX_WRAP_CACHE; i++ ) { - if( Q_stricmp( text, awc[ i ].text ) ) + wrapCache_t *cacheEntry = &wrapCache[ i ]; + + if( Q_stricmp( text, cacheEntry->text ) ) continue; - if( rect->x != awc[ i ].rect.x || - rect->y != awc[ i ].rect.y || - rect->w != awc[ i ].rect.w || - rect->h != awc[ i ].rect.h ) + if( rect->x != cacheEntry->rect.x || + rect->y != cacheEntry->rect.y || + rect->w != cacheEntry->rect.w || + rect->h != cacheEntry->rect.h ) continue; - if( awc[ i ].textWidth != width || awc[ i ].textHeight != height ) + if( cacheEntry->scale != scale ) continue; - //this is a match - return i; + // This is a match + cacheReadIndex = i; + cacheReadLineNum = 0; + return qtrue; } - //no match - autowrap isn't cached - return -1; + // No match - wrap isn't cached + return qfalse; } -void Item_Text_AutoWrapped_Paint( itemDef_t *item ) +static qboolean UI_NextWrapLine( const char **text, float *x, float *y ) +{ + wrapCache_t *cacheEntry = &wrapCache[ cacheReadIndex ]; + + if( cacheReadLineNum >= cacheEntry->numLines ) + return qfalse; + + *text = cacheEntry->lines[ cacheReadLineNum ]; + *x = cacheEntry->lineCoords[ cacheReadLineNum ][ 0 ]; + *y = cacheEntry->lineCoords[ cacheReadLineNum ][ 1 ]; + + cacheReadLineNum++; + + return qtrue; +} + +void Item_Text_Wrapped_Paint( itemDef_t *item ) { char text[ 1024 ]; - const char *p, *textPtr, *newLinePtr; - char buff[ 1024 ]; - char lastCMod[ 2 ] = { 0, 0 }; - qboolean forwardColor = qfalse; - int width, height, len, textWidth, newLine, newLineWidth; - int skipLines, totalLines, lineNum = 0; - float y, totalY, diffY; + const char *p, *textPtr; + float x, y, w, h; vec4_t color; - int cache, i; - - textWidth = 0; - newLinePtr = NULL; if( item->text == NULL ) { @@ -3030,192 +3454,164 @@ void Item_Text_AutoWrapped_Paint( itemDef_t *item ) return; Item_TextColor( item, &color ); - Item_SetTextExtents( item, &width, &height, textPtr ); - //check if this block is cached - cache = checkCache( textPtr, &item->window.rect, width, height ); - if( cache >= 0 ) + // Check if this block is cached + if( (qboolean)DC->getCVarValue( "ui_textWrapCache" ) && + UI_CheckWrapCache( textPtr, &item->window.rect, item->textscale ) ) { - lineNum = awc[ cache ].numLines; - - for( i = 0; i < lineNum; i++ ) + while( UI_NextWrapLine( &p, &x, &y ) ) { - item->textRect.x = awc[ cache ].lineOffsets[ i ][ 0 ]; - item->textRect.y = awc[ cache ].lineOffsets[ i ][ 1 ]; - - DC->drawText( item->textRect.x, item->textRect.y, item->textscale, color, - awc[ cache ].lines[ i ], 0, 0, item->textStyle ); + DC->drawText( x, y, item->textscale, color, + p, 0, 0, item->textStyle ); } } else { - y = item->textaligny; - len = 0; - buff[ 0 ] = '\0'; - newLine = 0; - newLineWidth = 0; - p = textPtr; + char buff[ 1024 ]; + float fontHeight = DC->textEmHeight( item->textscale ); + const float lineSpacing = fontHeight * 0.4f; + float lineHeight = fontHeight + lineSpacing; + float textHeight; + int textLength; + int paintLines, totalLines, lineNum = 0; + float paintY; + int i; + + UI_CreateCacheEntry( textPtr, &item->window.rect, item->textscale ); + + x = item->window.rect.x + item->textalignx; + y = item->window.rect.y + item->textaligny; + w = item->window.rect.w - ( 2.0f * item->textalignx ); + h = item->window.rect.h - ( 2.0f * item->textaligny ); + + textPtr = Item_Text_Wrap( textPtr, item->textscale, w ); + textLength = strlen( textPtr ); + + // Count lines + totalLines = 0; + for( i = 0; i < textLength; i++ ) + { + if( textPtr[ i ] == '\n' ) + totalLines++; + } - totalLines = Item_Text_AutoWrapped_Lines( item ); + paintLines = (int)floor( ( h + lineSpacing ) / lineHeight ); + if( paintLines > totalLines ) + paintLines = totalLines; - totalY = totalLines * ( height + 5 ); - diffY = totalY - item->window.rect.h; + textHeight = ( paintLines * lineHeight ) - lineSpacing; - if( diffY > 0.0f ) - skipLines = (int)( diffY / ( (float)height + 5.0f ) ); - else - skipLines = 0; - - //set up a cache entry - strcpy( awc[ cacheIndex ].text, textPtr ); - awc[ cacheIndex ].rect.x = item->window.rect.x; - awc[ cacheIndex ].rect.y = item->window.rect.y; - awc[ cacheIndex ].rect.w = item->window.rect.w; - awc[ cacheIndex ].rect.h = item->window.rect.h; - awc[ cacheIndex ].textWidth = width; - awc[ cacheIndex ].textHeight = height; - - while( p ) + switch( item->textvalignment ) { - textWidth = DC->textWidth( buff, item->textscale, 0 ); - - if( *p == '^' ) - { - lastCMod[ 0 ] = p[ 0 ]; - lastCMod[ 1 ] = p[ 1 ]; - } + default: + case ITEM_VALIGN_BOTTOM: + paintY = y + ( h - textHeight ); + break; - if( *p == ' ' || *p == '\t' || *p == '\n' || *p == '\0' ) - { - newLine = len; - newLinePtr = p+1; - newLineWidth = textWidth; + case ITEM_VALIGN_CENTER: + paintY = y + ( ( h - textHeight ) / 2.0f ); + break; - if( *p == '\n' ) //don't forward colours past deilberate \n's - lastCMod[ 0 ] = lastCMod[ 1 ] = 0; - else - forwardColor = qtrue; - } + case ITEM_VALIGN_TOP: + paintY = y; + break; + } - // forceably split lines that are too long (where normal splitage has failed) - if( textWidth > item->window.rect.w && newLine == 0 && *p != '\n' ) - { - newLine = len; - newLinePtr = p; - newLineWidth = textWidth; + p = textPtr; + for( i = 0, lineNum = 0; i < textLength && lineNum < paintLines; i++ ) + { + int lineLength = &textPtr[ i ] - p; - forwardColor = qtrue; - } + if( lineLength >= sizeof( buff ) - 1 ) + break; - if( ( newLine && textWidth > item->window.rect.w ) || *p == '\n' || *p == '\0' ) + if( textPtr[ i ] == '\n' || textPtr[ i ] == '\0' ) { - if( len ) + itemDef_t lineItem; + int width, height; + + strncpy( buff, p, lineLength ); + buff[ lineLength ] = '\0'; + p = &textPtr[ i + 1 ]; + + lineItem.type = ITEM_TYPE_TEXT; + lineItem.textscale = item->textscale; + lineItem.textStyle = item->textStyle; + lineItem.text = buff; + lineItem.textalignment = item->textalignment; + lineItem.textvalignment = ITEM_VALIGN_TOP; + lineItem.textalignx = 0.0f; + lineItem.textaligny = 0.0f; + + lineItem.textRect.w = 0.0f; + lineItem.textRect.h = 0.0f; + lineItem.window.rect.x = x; + lineItem.window.rect.y = paintY + ( lineNum * lineHeight ); + lineItem.window.rect.w = w; + lineItem.window.rect.h = lineHeight; + lineItem.window.border = item->window.border; + lineItem.window.borderSize = item->window.borderSize; + + if( debugMode ) { - if( item->textalignment == ITEM_ALIGN_LEFT ) - item->textRect.x = item->textalignx; - else if( item->textalignment == ITEM_ALIGN_RIGHT ) - item->textRect.x = item->textalignx - newLineWidth; - else if( item->textalignment == ITEM_ALIGN_CENTER ) - item->textRect.x = item->textalignx - newLineWidth / 2; - - item->textRect.y = y; - ToWindowCoords( &item->textRect.x, &item->textRect.y, &item->window ); - // - buff[ newLine ] = '\0'; - - if( !skipLines ) - { - DC->drawText( item->textRect.x, item->textRect.y, item->textscale, color, buff, 0, 0, item->textStyle ); - - strcpy( awc[ cacheIndex ].lines[ lineNum ], buff ); - awc[ cacheIndex ].lineOffsets[ lineNum ][ 0 ] = item->textRect.x; - awc[ cacheIndex ].lineOffsets[ lineNum ][ 1 ] = item->textRect.y; - - lineNum++; - } + vec4_t color; + color[ 0 ] = color[ 2 ] = color[ 3 ] = 1.0f; + color[ 1 ] = 0.0f; + DC->drawRect( lineItem.window.rect.x, lineItem.window.rect.y, + lineItem.window.rect.w, lineItem.window.rect.h, 1, color ); } - if( *p == '\0' ) - break; - // - if( !skipLines ) - y += height + 5; - - if( skipLines ) - skipLines--; - - p = newLinePtr; - len = 0; - newLine = 0; - newLineWidth = 0; - - if( forwardColor && lastCMod[ 0 ] != 0 ) - { - buff[ len++ ] = lastCMod[ 0 ]; - buff[ len++ ] = lastCMod[ 1 ]; - buff[ len ] = '\0'; + Item_SetTextExtents( &lineItem, &width, &height, buff ); + DC->drawText( lineItem.textRect.x, lineItem.textRect.y, + lineItem.textscale, color, buff, 0, 0, + lineItem.textStyle ); + UI_AddCacheEntryLine( buff, lineItem.textRect.x, lineItem.textRect.y ); - forwardColor = qfalse; - } - - continue; + lineNum++; } - - buff[ len++ ] = *p++; - buff[ len ] = '\0'; } - //mark the end of the lines list - awc[ cacheIndex ].numLines = lineNum; - - //increment cacheIndex - cacheIndex = ( cacheIndex + 1 ) % MAX_AUTOWRAP_CACHE; + UI_FinishCacheEntry( ); } } -void Item_Text_Wrapped_Paint(itemDef_t *item) { - char text[1024]; - const char *p, *start, *textPtr; - char buff[1024]; - int width, height; - float x, y; - vec4_t color; - - // now paint the text and/or any optional images - // default to left - - if (item->text == NULL) { - if (item->cvar == NULL) { - return; - } - else { - DC->getCVarString(item->cvar, text, sizeof(text)); - textPtr = text; - } - } - else { - textPtr = item->text; - } - if (*textPtr == '\0') { - return; - } - - Item_TextColor(item, &color); - Item_SetTextExtents(item, &width, &height, textPtr); - - x = item->textRect.x; - y = item->textRect.y; - start = textPtr; - p = strchr(textPtr, '\r'); - while (p && *p) { - strncpy(buff, start, p-start+1); - buff[p-start] = '\0'; - DC->drawText(x, y, item->textscale, color, buff, 0, 0, item->textStyle); - y += height + 5; - start += p - start + 1; - p = strchr(p+1, '\r'); - } - DC->drawText(x, y, item->textscale, color, start, 0, 0, item->textStyle); +/* +============== +UI_DrawTextBlock +============== +*/ +void UI_DrawTextBlock( rectDef_t *rect, float text_x, float text_y, vec4_t color, + float scale, int textalign, int textvalign, + int textStyle, const char *text ) +{ + static menuDef_t dummyParent; + static itemDef_t textItem; + + textItem.text = text; + + textItem.parent = &dummyParent; + memcpy( textItem.window.foreColor, color, sizeof( vec4_t ) ); + textItem.window.flags = 0; + + textItem.window.rect.x = rect->x; + textItem.window.rect.y = rect->y; + textItem.window.rect.w = rect->w; + textItem.window.rect.h = rect->h; + textItem.window.border = 0; + textItem.window.borderSize = 0.0f; + textItem.textRect.x = 0.0f; + textItem.textRect.y = 0.0f; + textItem.textRect.w = 0.0f; + textItem.textRect.h = 0.0f; + textItem.textalignment = textalign; + textItem.textvalignment = textvalign; + textItem.textalignx = text_x; + textItem.textaligny = text_y; + textItem.textscale = scale; + textItem.textStyle = textStyle; + + // Utilise existing wrap code + Item_Text_Wrapped_Paint( &textItem ); } void Item_Text_Paint(itemDef_t *item) { @@ -3228,10 +3624,6 @@ void Item_Text_Paint(itemDef_t *item) { Item_Text_Wrapped_Paint(item); return; } - if (item->window.flags & WINDOW_AUTOWRAPPED) { - Item_Text_AutoWrapped_Paint(item); - return; - } if (item->text == NULL) { if (item->cvar == NULL) { @@ -3290,38 +3682,64 @@ void Item_Text_Paint(itemDef_t *item) { -//float trap_Cvar_VariableValue( const char *var_name ); -//void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); - -void Item_TextField_Paint(itemDef_t *item) { - char buff[1024]; - vec4_t newColor; - int offset; - menuDef_t *parent = (menuDef_t*)item->parent; - editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; - +void Item_TextField_Paint(itemDef_t *item) +{ + char buff[1024]; + vec4_t newColor; + int offset = (item->text && *item->text) ? ITEM_VALUE_OFFSET : 0; + menuDef_t *parent = (menuDef_t*)item->parent; + editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; + char cursor = DC->getOverstrikeMode() ? '|' : '_'; + qboolean editing = ( item->window.flags & WINDOW_HASFOCUS && g_editingField ); + const int cursorWidth = editing ? EDIT_CURSOR_WIDTH : 0; + + //FIXME: causes duplicate printing if item->text is not set (NULL) Item_Text_Paint(item); buff[0] = '\0'; - if (item->cvar) { + if (item->cvar) DC->getCVarString(item->cvar, buff, sizeof(buff)); + + // maxFieldWidth hasn't been set, so use the item's rect + if( editPtr->maxFieldWidth == 0 ) + { + editPtr->maxFieldWidth = item->window.rect.w - + ( item->textRect.w + offset + ( item->textRect.x - item->window.rect.x ) ); + + if( editPtr->maxFieldWidth < MIN_FIELD_WIDTH ) + editPtr->maxFieldWidth = MIN_FIELD_WIDTH; } + if( !editing ) + editPtr->paintOffset = 0; + + // Shorten string to max viewable + while( DC->textWidth( buff + editPtr->paintOffset, item->textscale, 0 ) > + ( editPtr->maxFieldWidth - cursorWidth ) && strlen( buff ) > 0 ) + buff[ strlen( buff ) - 1 ] = '\0'; + parent = (menuDef_t*)item->parent; - if (item->window.flags & WINDOW_HASFOCUS) { + if (item->window.flags & WINDOW_HASFOCUS) memcpy(newColor, &parent->focusColor, sizeof(vec4_t)); - } else { + else memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); - } - offset = (item->text && *item->text) ? 8 : 0; - if (item->window.flags & WINDOW_HASFOCUS && g_editingField) { - char cursor = DC->getOverstrikeMode() ? '_' : '|'; - DC->drawTextWithCursor(item->textRect.x + item->textRect.w + offset, item->textRect.y, item->textscale, newColor, buff + editPtr->paintOffset, item->cursorPos - editPtr->paintOffset , cursor, editPtr->maxPaintChars, item->textStyle); - } else { - DC->drawText(item->textRect.x + item->textRect.w + offset, item->textRect.y, item->textscale, newColor, buff + editPtr->paintOffset, 0, editPtr->maxPaintChars, item->textStyle); + if( editing ) + { + DC->drawTextWithCursor( item->textRect.x + item->textRect.w + offset, + item->textRect.y, item->textscale, newColor, + buff + editPtr->paintOffset, + item->cursorPos - editPtr->paintOffset, + cursor, editPtr->maxPaintChars, item->textStyle ); + } + else + { + DC->drawText( item->textRect.x + item->textRect.w + offset, + item->textRect.y, item->textscale, newColor, + buff + editPtr->paintOffset, 0, + editPtr->maxPaintChars, item->textStyle ); } } @@ -3329,6 +3747,7 @@ void Item_TextField_Paint(itemDef_t *item) { void Item_YesNo_Paint(itemDef_t *item) { vec4_t newColor; float value; + int offset; menuDef_t *parent = (menuDef_t*)item->parent; value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; @@ -3339,9 +3758,10 @@ void Item_YesNo_Paint(itemDef_t *item) { memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); } + offset = (item->text && *item->text) ? ITEM_VALUE_OFFSET : 0; if (item->text) { Item_Text_Paint(item); - DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, (value != 0) ? "Yes" : "No", 0, 0, item->textStyle); + DC->drawText(item->textRect.x + item->textRect.w + offset, item->textRect.y, item->textscale, newColor, (value != 0) ? "Yes" : "No", 0, 0, item->textStyle); } else { DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, (value != 0) ? "Yes" : "No", 0, 0, item->textStyle); } @@ -3362,7 +3782,7 @@ void Item_Multi_Paint(itemDef_t *item) { if (item->text) { Item_Text_Paint(item); - DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle); + DC->drawText(item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle); } else { DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle); } @@ -3614,6 +4034,7 @@ void Item_Slider_Paint(itemDef_t *item) { vec4_t newColor; float x, y, value; menuDef_t *parent = (menuDef_t*)item->parent; + float vScale = Item_Slider_VScale( item ); value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; @@ -3623,18 +4044,25 @@ void Item_Slider_Paint(itemDef_t *item) { memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); } - y = item->window.rect.y; if (item->text) { Item_Text_Paint(item); - x = item->textRect.x + item->textRect.w + 8; + x = item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET; + y = item->textRect.y - item->textRect.h + + ( ( item->textRect.h - ( SLIDER_HEIGHT * vScale ) ) / 2.0f ); } else { x = item->window.rect.x; + y = item->window.rect.y; } + DC->setColor(newColor); - DC->drawHandlePic( x, y, SLIDER_WIDTH, SLIDER_HEIGHT, DC->Assets.sliderBar ); + DC->drawHandlePic( x, y, SLIDER_WIDTH, SLIDER_HEIGHT * vScale, DC->Assets.sliderBar ); + + y = item->textRect.y - item->textRect.h + + ( ( item->textRect.h - ( SLIDER_THUMB_HEIGHT * vScale ) ) / 2.0f ); x = Item_Slider_ThumbPosition(item); - DC->drawHandlePic( x - (SLIDER_THUMB_WIDTH / 2), y - 2, SLIDER_THUMB_WIDTH, SLIDER_THUMB_HEIGHT, DC->Assets.sliderThumb ); + DC->drawHandlePic( x - (SLIDER_THUMB_WIDTH / 2), y, + SLIDER_THUMB_WIDTH, SLIDER_THUMB_HEIGHT * vScale, DC->Assets.sliderThumb ); } @@ -3671,7 +4099,7 @@ void Item_Bind_Paint(itemDef_t *item) { if (item->text) { Item_Text_Paint(item); BindingFromName(item->cvar); - DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, g_nameBind1, 0, maxChars, item->textStyle); + DC->drawText(item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET, item->textRect.y, item->textscale, newColor, g_nameBind1, 0, maxChars, item->textStyle); } else { DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, (value != 0) ? "FIXME" : "FIXME", 0, maxChars, item->textStyle); } @@ -3891,24 +4319,28 @@ void Item_ListBox_Paint(itemDef_t *item) { count = DC->feederCount(item->special); // default is vertical if horizontal flag is not here if (item->window.flags & WINDOW_HORIZONTAL) { - // draw scrollbar in bottom of the window - // bar - x = item->window.rect.x + 1; - y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE - 1; - DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowLeft); - x += SCROLLBAR_SIZE - 1; - size = item->window.rect.w - (SCROLLBAR_SIZE * 2); - DC->drawHandlePic(x, y, size+1, SCROLLBAR_SIZE, DC->Assets.scrollBar); - x += size - 1; - DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowRight); - // thumb - thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); - if (thumb > x - SCROLLBAR_SIZE - 1) { - thumb = x - SCROLLBAR_SIZE - 1; - } - DC->drawHandlePic(thumb, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); - // - listPtr->endPos = listPtr->startPos; + if( !listPtr->notselectable ) + { + // draw scrollbar in bottom of the window + // bar + x = item->window.rect.x + 1; + y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE - 1; + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowLeft); + x += SCROLLBAR_SIZE - 1; + size = item->window.rect.w - (SCROLLBAR_SIZE * 2); + DC->drawHandlePic(x, y, size+1, SCROLLBAR_SIZE, DC->Assets.scrollBar); + x += size - 1; + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowRight); + // thumb + thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); + if (thumb > x - SCROLLBAR_SIZE - 1) { + thumb = x - SCROLLBAR_SIZE - 1; + } + DC->drawHandlePic(thumb, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); + // + listPtr->endPos = listPtr->startPos; + } + size = item->window.rect.w - 2; // items // size contains max available space @@ -3941,23 +4373,26 @@ void Item_ListBox_Paint(itemDef_t *item) { // } } else { - // draw scrollbar to right side of the window - x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE - 1; - y = item->window.rect.y + 1; - DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowUp); - y += SCROLLBAR_SIZE - 1; - - listPtr->endPos = listPtr->startPos; - size = item->window.rect.h - (SCROLLBAR_SIZE * 2); - DC->drawHandlePic(x, y, SCROLLBAR_SIZE, size+1, DC->Assets.scrollBar); - y += size - 1; - DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowDown); - // thumb - thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); - if (thumb > y - SCROLLBAR_SIZE - 1) { - thumb = y - SCROLLBAR_SIZE - 1; - } - DC->drawHandlePic(x, thumb, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); + if( !listPtr->notselectable ) + { + // draw scrollbar to right side of the window + x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE - 1; + y = item->window.rect.y + 1; + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowUp); + y += SCROLLBAR_SIZE - 1; + + listPtr->endPos = listPtr->startPos; + size = item->window.rect.h - (SCROLLBAR_SIZE * 2); + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, size+1, DC->Assets.scrollBar); + y += size - 1; + DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowDown); + // thumb + thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); + if (thumb > y - SCROLLBAR_SIZE - 1) { + thumb = y - SCROLLBAR_SIZE - 1; + } + DC->drawHandlePic(x, thumb, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); + } // adjust size for item painting size = item->window.rect.h - 2; @@ -3987,6 +4422,7 @@ void Item_ListBox_Paint(itemDef_t *item) { // fit++; } } else { + float m = DC->textEmHeight( item->textscale ); x = item->window.rect.x + 1; y = item->window.rect.y + 1; for (i = listPtr->startPos; i < count; i++) { @@ -3999,14 +4435,16 @@ void Item_ListBox_Paint(itemDef_t *item) { for (j = 0; j < listPtr->numColumns; j++) { Q_strncpyz( text, DC->feederItemText(item->special, i, j, &optionalImage), sizeof( text ) ); if (optionalImage >= 0) { - DC->drawHandlePic(x + 4 + listPtr->columnInfo[j].pos, y - 1 + listPtr->elementHeight / 2, listPtr->columnInfo[j].width, listPtr->columnInfo[j].width, optionalImage); + DC->drawHandlePic(x + 4 + listPtr->columnInfo[j].pos, + y + ( ( listPtr->elementHeight - listPtr->columnInfo[ j ].width ) / 2.0f ), + listPtr->columnInfo[j].width, listPtr->columnInfo[j].width, optionalImage); } else if (text) { int alignOffset = 0.0f, tw; tw = DC->textWidth( text, item->textscale, 0 ); // Shorten the string if it's too long - while( tw > listPtr->columnInfo[ j ].width ) + while( tw > listPtr->columnInfo[ j ].width && strlen( text ) > 0 ) { text[ strlen( text ) - 1 ] = '\0'; tw = DC->textWidth( text, item->textscale, 0 ); @@ -4030,7 +4468,8 @@ void Item_ListBox_Paint(itemDef_t *item) { alignOffset = 0.0f; } - DC->drawText( x + 4 + listPtr->columnInfo[j].pos + alignOffset, y + listPtr->elementHeight, + DC->drawText( x + 4 + listPtr->columnInfo[j].pos + alignOffset, + y + m + ( ( listPtr->elementHeight - m ) / 2.0f ), item->textscale, item->window.foreColor, text, 0, 0, item->textStyle ); } @@ -4038,14 +4477,17 @@ void Item_ListBox_Paint(itemDef_t *item) { } else { Q_strncpyz( text, DC->feederItemText(item->special, i, 0, &optionalImage), sizeof( text ) ); if (optionalImage >= 0) { - //DC->drawHandlePic(x + 4 + listPtr->elementHeight, y, listPtr->columnInfo[j].width, listPtr->columnInfo[j].width, optionalImage); + DC->drawHandlePic(x + 4, y, listPtr->elementHeight, listPtr->elementHeight, optionalImage); } else if (text) { - DC->drawText(x + 4, y + listPtr->elementHeight, item->textscale, item->window.foreColor, text, 0, 0, item->textStyle); + DC->drawText( x + 4, y + m + ( ( listPtr->elementHeight - m ) / 2.0f ), + item->textscale, item->window.foreColor, text, 0, + 0, item->textStyle ); } } if (i == item->cursorPos) { - DC->fillRect(x + 2, y + 2, item->window.rect.w - SCROLLBAR_SIZE - 4, listPtr->elementHeight, item->window.outlineColor); + DC->fillRect( x, y, item->window.rect.w - SCROLLBAR_SIZE - ( 2 * item->window.borderSize ), + listPtr->elementHeight, item->window.outlineColor); } listPtr->endPos++; @@ -4067,6 +4509,7 @@ void Item_ListBox_Paint(itemDef_t *item) { void Item_OwnerDraw_Paint(itemDef_t *item) { menuDef_t *parent; + const char *text; if (item == NULL) { return; @@ -4104,16 +4547,26 @@ void Item_OwnerDraw_Paint(itemDef_t *item) { Com_Memcpy(color, parent->disableColor, sizeof(vec4_t)); } - if (item->text) { - Item_Text_Paint(item); - if (item->text[0]) { - // +8 is an offset kludge to properly align owner draw items that have text combined with them - DC->ownerDrawItem(item->textRect.x + item->textRect.w + 8, item->window.rect.y, item->window.rect.w, item->window.rect.h, 0, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); - } else { - DC->ownerDrawItem(item->textRect.x + item->textRect.w, item->window.rect.y, item->window.rect.w, item->window.rect.h, 0, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); - } + if( DC->ownerDrawText && ( text = DC->ownerDrawText( item->window.ownerDraw ) ) ) { + if (item->text && *item->text) { + Item_Text_Paint(item); + + DC->drawText(item->textRect.x + item->textRect.w + ITEM_VALUE_OFFSET, + item->textRect.y, item->textscale, + color, text, 0, 0, item->textStyle); } else { - DC->ownerDrawItem(item->window.rect.x, item->window.rect.y, item->window.rect.w, item->window.rect.h, item->textalignx, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); + item->text = text; + Item_Text_Paint(item); + item->text = NULL; + } + } else { + DC->ownerDrawItem(item->window.rect.x, item->window.rect.y, + item->window.rect.w, item->window.rect.h, + item->textalignx, item->textaligny, + item->window.ownerDraw, item->window.ownerDrawFlags, + item->alignment, item->textalignment, item->textvalignment, + item->special, item->textscale, color, + item->window.background, item->textStyle ); } } } @@ -4255,7 +4708,6 @@ void Item_Paint(itemDef_t *item) { return; } - // paint the rect first.. Window_Paint(&item->window, parent->fadeAmount , parent->fadeClamp, parent->fadeCycle); if (debugMode) { @@ -4266,8 +4718,6 @@ void Item_Paint(itemDef_t *item) { DC->drawRect(r->x, r->y, r->w, r->h, 1, color); } - //DC->drawRect(item->window.rect.x, item->window.rect.y, item->window.rect.w, item->window.rect.h, 1, red); - switch (item->type) { case ITEM_TYPE_OWNERDRAW: Item_OwnerDraw_Paint(item); @@ -4311,6 +4761,7 @@ void Item_Paint(itemDef_t *item) { break; } + Border_Paint(&item->window); } void Menu_Init(menuDef_t *menu) { @@ -4521,13 +4972,11 @@ void Menu_Paint(menuDef_t *menu, qboolean forcePaint) { // implies a background shader // FIXME: make sure we have a default shader if fullscreen is set with no background DC->drawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, menu->window.background ); - } else if (menu->window.background) { - // this allows a background shader without being full screen - //UI_DrawHandlePic(menu->window.rect.x, menu->window.rect.y, menu->window.rect.w, menu->window.rect.h, menu->backgroundShader); } // paint the background and or border Window_Paint(&menu->window, menu->fadeAmount, menu->fadeClamp, menu->fadeCycle ); + Border_Paint(&menu->window); for (i = 0; i < menu->itemCount; i++) { Item_Paint(menu->items[i]); @@ -4788,18 +5237,12 @@ qboolean ItemParse_notselectable( itemDef_t *item, int handle ) { return qtrue; } -// manually wrapped +// auto wrapped qboolean ItemParse_wrapped( itemDef_t *item, int handle ) { item->window.flags |= WINDOW_WRAPPED; return qtrue; } -// auto wrapped -qboolean ItemParse_autowrapped( itemDef_t *item, int handle ) { - item->window.flags |= WINDOW_AUTOWRAPPED; - return qtrue; -} - // horizontalscroll qboolean ItemParse_horizontalscroll( itemDef_t *item, int handle ) { @@ -4947,6 +5390,13 @@ qboolean ItemParse_textalign( itemDef_t *item, int handle ) { return qtrue; } +qboolean ItemParse_textvalign( itemDef_t *item, int handle ) { + if (!PC_Int_Parse(handle, &item->textvalignment)) { + return qfalse; + } + return qtrue; +} + qboolean ItemParse_textalignx( itemDef_t *item, int handle ) { if (!PC_Float_Parse(handle, &item->textalignx)) { return qfalse; @@ -5166,6 +5616,27 @@ qboolean ItemParse_maxPaintChars( itemDef_t *item, int handle ) { return qtrue; } +qboolean ItemParse_maxFieldWidth( itemDef_t *item, int handle ) { + editFieldDef_t *editPtr; + int maxFieldWidth; + + Item_ValidateTypeData(item); + if (!item->typeData) + return qfalse; + + if (!PC_Int_Parse(handle, &maxFieldWidth)) { + return qfalse; + } + editPtr = (editFieldDef_t*)item->typeData; + + if( maxFieldWidth < MIN_FIELD_WIDTH ) + editPtr->maxFieldWidth = MIN_FIELD_WIDTH; + else + editPtr->maxFieldWidth = maxFieldWidth; + + return qtrue; +} + qboolean ItemParse_cvarFloat( itemDef_t *item, int handle ) { @@ -5353,7 +5824,6 @@ keywordHash_t itemParseKeywords[] = { {"decoration", ItemParse_decoration, NULL}, {"notselectable", ItemParse_notselectable, NULL}, {"wrapped", ItemParse_wrapped, NULL}, - {"autowrapped", ItemParse_autowrapped, NULL}, {"horizontalscroll", ItemParse_horizontalscroll, NULL}, {"type", ItemParse_type, NULL}, {"elementwidth", ItemParse_elementwidth, NULL}, @@ -5367,6 +5837,7 @@ keywordHash_t itemParseKeywords[] = { {"ownerdraw", ItemParse_ownerdraw, NULL}, {"align", ItemParse_align, NULL}, {"textalign", ItemParse_textalign, NULL}, + {"textvalign", ItemParse_textvalign, NULL}, {"textalignx", ItemParse_textalignx, NULL}, {"textaligny", ItemParse_textaligny, NULL}, {"textscale", ItemParse_textscale, NULL}, @@ -5387,6 +5858,7 @@ keywordHash_t itemParseKeywords[] = { {"cvar", ItemParse_cvar, NULL}, {"maxChars", ItemParse_maxChars, NULL}, {"maxPaintChars", ItemParse_maxPaintChars, NULL}, + {"maxFieldWidth", ItemParse_maxFieldWidth, NULL}, {"focusSound", ItemParse_focusSound, NULL}, {"cvarFloat", ItemParse_cvarFloat, NULL}, {"cvarStrList", ItemParse_cvarStrList, NULL}, @@ -5885,8 +6357,12 @@ int Menu_Count( void ) { void Menu_PaintAll( void ) { int i; - if (captureFunc) { - captureFunc(captureData); + if ( captureFunc != voidFunction ) { + if( captureFuncExpiry > 0 && DC->realTime > captureFuncExpiry ) { + UI_RemoveCaptureFunc( ); + } else { + captureFunc(captureData); + } } for (i = 0; i < Menu_Count(); i++) { diff --git a/src/ui/ui_shared.h b/src/ui/ui_shared.h index 2fd396d0..28b24482 100644 --- a/src/ui/ui_shared.h +++ b/src/ui/ui_shared.h @@ -59,8 +59,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define WINDOW_LB_PGDN 0x00008000 // mouse is over page down #define WINDOW_ORBITING 0x00010000 // item is in orbit #define WINDOW_OOB_CLICK 0x00020000 // close on out of bounds click -#define WINDOW_WRAPPED 0x00040000 // manually wrap text -#define WINDOW_AUTOWRAPPED 0x00080000 // auto wrap text +#define WINDOW_WRAPPED 0x00080000 // wrap text #define WINDOW_FORCED 0x00100000 // forced open #define WINDOW_POPUP 0x00200000 // popup #define WINDOW_BACKCOLORSET 0x00400000 // backcolor was explicitly set @@ -81,6 +80,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define MAX_SCRIPT_ARGS 12 #define MAX_EDITFIELD 256 +#define ITEM_VALUE_OFFSET 8 #define ART_FX_BASE "menu/art/fx_base" #define ART_FX_BLUE "menu/art/fx_blue" @@ -105,7 +105,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define SLIDER_HEIGHT 16.0 #define SLIDER_THUMB_WIDTH 12.0 #define SLIDER_THUMB_HEIGHT 20.0 -#define NUM_CROSSHAIRS 10 +#define NUM_CROSSHAIRS 10 typedef struct { const char *command; @@ -194,6 +194,7 @@ typedef struct editFieldDef_s { float range; // int maxChars; // for edit fields int maxPaintChars; // for edit fields + int maxFieldWidth; // for edit fields int paintOffset; // } editFieldDef_t; @@ -226,6 +227,7 @@ typedef struct itemDef_s { int type; // text, button, radiobutton, checkbox, textfield, listbox, combo int alignment; // left center right int textalignment; // ( optional ) alignment for text within rect based on text width + int textvalignment; // ( optional ) alignment for text within rect based on text width float textalignx; // ( optional ) text alignment x coord float textaligny; // ( optional ) text alignment x coord float textscale; // scale percentage from 72pts @@ -318,8 +320,10 @@ typedef struct { void (*drawHandlePic) (float x, float y, float w, float h, qhandle_t asset); void (*drawStretchPic) (float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void (*drawText) (float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style ); - int (*textWidth) (const char *text, float scale, int limit); - int (*textHeight) (const char *text, float scale, int limit); + float (*textWidth) (const char *text, float scale, int limit); + float (*textHeight) (const char *text, float scale, int limit); + float (*textEmWidth) (float scale); + float (*textEmHeight) (float scale); qhandle_t (*registerModel) (const char *p); void (*modelBounds) (qhandle_t model, vec3_t min, vec3_t max); void (*fillRect) ( float x, float y, float w, float h, const vec4_t color); @@ -330,7 +334,7 @@ typedef struct { void (*addRefEntityToScene) (const refEntity_t *re ); void (*renderScene) ( const refdef_t *fd ); void (*registerFont) (const char *pFontname, int pointSize, fontInfo_t *font); - void (*ownerDrawItem) (float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle); + void (*ownerDrawItem) (float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, int textalign, int textvalign, float special, float scale, vec4_t color, qhandle_t shader, int textStyle); float (*getValue) (int ownerDraw); qboolean (*ownerDrawVisible) (int flags); void (*runScript)(char **p); @@ -355,6 +359,7 @@ typedef struct { void (*Print)(const char *msg, ...); void (*Pause)(qboolean b); int (*ownerDrawWidth)(int ownerDraw, float scale); + const char *(*ownerDrawText)(int ownerDraw); sfxHandle_t (*registerSound)(const char *name, qboolean compressed); void (*startBackgroundTrack)( const char *intro, const char *loop); void (*stopBackgroundTrack)( void ); @@ -428,6 +433,11 @@ void Menu_Paint(menuDef_t *menu, qboolean forcePaint); void Menu_SetFeederSelection(menuDef_t *menu, int feeder, int index, const char *name); void Display_CacheAll( void ); +typedef void (CaptureFunc) (void *p); + +void UI_InstallCaptureFunc( CaptureFunc *f, void *data, int timeout ); +void UI_RemoveCaptureFunc( void ); + void *UI_Alloc( int size ); void UI_InitMemory( void ); qboolean UI_OutOfMemory( void ); @@ -437,7 +447,10 @@ void Controls_SetConfig(qboolean restart); void Controls_SetDefaults( void ); //for cg_draw.c -void Item_Text_AutoWrapped_Paint( itemDef_t *item ); +void Item_Text_Wrapped_Paint( itemDef_t *item ); +void UI_DrawTextBlock( rectDef_t *rect, float text_x, float text_y, vec4_t color, + float scale, int textalign, int textvalign, + int textStyle, const char *text ); int trap_Parse_AddGlobalDefine( char *define ); int trap_Parse_LoadSource( const char *filename ); -- cgit