st/code/ui/ui_shared.c
2008-04-04 00:00:00 +00:00

7439 lines
180 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2007 HermitWorks Entertainment Corporation
This file is part of the Space Trader source code.
The Space Trader source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
The Space Trader source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Space Trader source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "ui_local.h"
#include "ui_anim.h"
#ifdef _MSC_VER
#pragma warning( disable : 4100 )
#pragma warning( disable : 4244 )
#pragma warning( disable : 4702 )
#endif
// string allocation/managment
#define SCROLL_TIME_START 500
#define SCROLL_TIME_ADJUST 150
#define SCROLL_TIME_ADJUSTOFFSET 40
#define SCROLL_TIME_FLOOR 20
#define SCROLL_DEBUGMODE_MOVE 1
#define SCROLL_DEBUGMODE_LEFT 2
#define SCROLL_DEBUGMODE_RIGHT 4
#define SCROLL_DEBUGMODE_BOTTOM 8
#define SCROLL_DEBUGMODE_TOP 16
typedef struct scrollInfo_s
{
int nextScrollTime;
int nextAdjustTime;
int adjustValue;
int scrollKey;
float xStart;
float yStart;
float offset;
itemDef_t *item;
qboolean scrollDir;
} scrollInfo_t;
static scrollInfo_t scrollInfo;
static void (*captureFunc)(void *p);
static void *captureData;
static itemDef_t *itemCapture; // item that has the mouse captured ( if any )
displayContextDef_t *DC = NULL;
static qboolean g_waitingForKey = qfalse;
static itemDef_t *g_bindItem = NULL;
menuDef_t Menus[MAX_MENUS]; // defined menus
int menuCount = 0; // how many
static menuStackDef_t * menuStack;
static qboolean debugMode = qfalse;
#ifdef DEVELOPER
static struct
{
menuDef_t * menu;
qboolean dontsnap;
} debug;
#endif
int debug_cgmenu;
#define DOUBLE_CLICK_DELAY 300
void Item_RunScript(itemDef_t *item, const char *s);
void Menu_RunScript( menuDef_t *menu, const char *s );
int BindingIDFromName(const char *name);
qboolean Item_Bind_HandleKey(itemDef_t *item, int key );
itemDef_t *Menu_SetPrevCursorItem(menuDef_t *menu);
itemDef_t *Menu_SetNextCursorItem(menuDef_t *menu);
static void Menu_ClearFocusItem( menuDef_t * menu, itemDef_t * item );
static qboolean Menu_SetFocusItem( menuDef_t * menu, itemDef_t * item );
void Item_ListBox_UpdateColumns( itemDef_t * item );
extern void trap_R_GetFonts( char * buffer, int size );
void Item_ValidateTypeData(itemDef_t *item);
extern qboolean trap_Key_IsDown( int keynum );
extern void trap_Key_SetCatcher( int catcher );
extern int trap_Key_GetCatcher( void );
static void Menus_SetVisible();
static void Item_Slider_SetValue( itemDef_t * item, float value );
#ifdef CGAME
#define MEM_POOL_SIZE 128 * 1024
#else
#define MEM_POOL_SIZE 1024 * 1024
#endif
static char memoryPool[MEM_POOL_SIZE];
static int allocPoint, outOfMemory;
typedef enum {
FIELD_INTEGER,
FIELD_FLOAT,
FIELD_STRING,
FIELD_FONT,
FIELD_SHADER,
FIELD_COLOR,
FIELD_BIT,
FIELD_MODEL,
FIELD_RECT,
FIELD_KEY,
FIELD_SCRIPT,
FIELD_FUNC,
FIELD_STATE,
FIELD_ITEMDEF,
FIELD_WINDOW,
FIELD_ITEM,
FIELD_MENU,
FIELD_EDITFIELD,
FIELD_LISTBOX,
FIELD_PROGRESS,
FIELD_COLUMNS,
FIELD_EFFECT,
} fieldType_e;
#define FIELD_DONTSAVE 0x00000001
#define FIELD_DONTEDIT 0x00000002
#define FIELD_SECTION 0x00000004
typedef struct fieldEnum_s{
const char * shortname;
const char * name;
float value;
} fieldEnum_t;
typedef struct {
char * name;
int offset;
int bit;
qboolean (*func)(itemDef_t *item, int handle);
fieldType_e type;
int flags;
fieldEnum_t fieldEnum[ MAX_MULTI_CVARS ];
float minVal;
float maxVal;
} field_t;
field_t * fields;
int fieldCount;
#define BIND_ALLOW_IN_UI 1
typedef struct {
char *command;
int id;
int defaultbind1;
int defaultbind2;
int bind1;
int bind2;
int flags;
} bind_t;
static bind_t g_bindings[] =
{
{"+button2", K_ENTER, -1, -1, -1},
{"+speed", K_SHIFT, -1, -1, -1},
{"+forward", K_UPARROW, -1, -1, -1},
{"+back", K_DOWNARROW, -1, -1, -1},
{"+moveleft", ',', -1, -1, -1},
{"+moveright", '.', -1, -1, -1},
{"+moveup", K_SPACE, -1, -1, -1},
{"+movedown", 'c', -1, -1, -1},
{"+left", K_LEFTARROW, -1, -1, -1},
{"+right", K_RIGHTARROW, -1, -1, -1},
{"+strafe", K_ALT, -1, -1, -1},
{"+lookup", K_PGDN, -1, -1, -1},
{"+lookdown", K_DEL, -1, -1, -1},
{"+mlook", '/', -1, -1, -1},
{"centerview", K_END, -1, -1, -1},
{"+zoom", -1, -1, -1, -1},
{"weapon 1", '1', -1, -1, -1},
{"weapon 2", '2', -1, -1, -1},
{"weapon 3", '3', -1, -1, -1},
{"weapon 4", '4', -1, -1, -1},
{"weapon 5", '5', -1, -1, -1},
{"weapon 6", '6', -1, -1, -1},
{"weapon 7", '7', -1, -1, -1},
{"weapon 8", '8', -1, -1, -1},
{"weapon 9", '9', -1, -1, -1},
{"weapon 10", '0', -1, -1, -1},
{"weapon 11", -1, -1, -1, -1},
{"weapon 12", -1, -1, -1, -1},
{"weapon 13", -1, -1, -1, -1},
{"+attack", K_CTRL, -1, -1, -1},
{"weapprev", '[', -1, -1, -1},
{"weapnext", ']', -1, -1, -1},
{"+button2", 'g', -1, -1, -1},
{"+button3", K_MOUSE3, -1, -1, -1},
{"+button4", K_MOUSE4, -1, -1, -1},
{"+button5", K_MOUSE2, -1, -1, -1},
{"scoresUp", K_KP_PGUP, -1, -1, -1},
{"scoresDown", K_KP_PGDN, -1, -1, -1},
{"reload", 'r', -1, -1, -1},
{"mode", 'e', -1, -1, -1},
{"st_ready", K_F3, -1, -1, -1, -1, BIND_ALLOW_IN_UI},
// bk001205 - this one below was: '-1'
{"messagemode", 't', -1, -1, -1, -1, BIND_ALLOW_IN_UI},
};
static qboolean isMouseKey( int key )
{
return key == K_MOUSE1
|| key == K_MOUSE2
|| key == K_MOUSE3;
}
void UI_SetMenuStack( menuStackDef_t *stack )
{
int i;
if ( stack != menuStack ) {
if ( menuStack ) {
for ( i=0; i<menuStack->count; i++ ) {
Menu_ClearFocusItem( menuStack->m[ i ], 0 );
}
}
menuStack = stack;
}
}
static itemDef_t *g_editItem = NULL;
static void UI_SetEditItem( itemDef_t *item )
{
if( item == g_editItem )
return;
if( item )
{
if( item->cvar )
{
//have to test like this since CVAR_PASSWORD includes other bits (for some reason...)
if( (item->cvarFlags & CVAR_PASSWORD) == CVAR_PASSWORD )
{
DC->setCVar( item->cvar, "" );
item->cursorPos = 0;
}
else
{
char str[1024];
DC->getCVarString( item->cvar, str, sizeof( str ) );
item->cursorPos = strlen( str );
}
}
}
g_editItem = item;
}
/*
===============
UI_Alloc
===============
*/
void *UI_Alloc( int size ) {
char *p;
if ( allocPoint + size > MEM_POOL_SIZE ) {
outOfMemory = qtrue;
if (DC->Print) {
DC->Print("UI_Alloc: Failure. Out of memory!\n");
}
//DC->trap_Print(S_COLOR_YELLOW"WARNING: UI Out of Memory!\n");
return NULL;
}
p = &memoryPool[allocPoint];
allocPoint += ( size + 15 ) & ~15;
return p;
}
/*
===============
UI_InitMemory
===============
*/
void UI_InitMemory( void ) {
allocPoint = 0;
outOfMemory = qfalse;
}
qboolean UI_OutOfMemory() {
return outOfMemory;
}
#define HASH_TABLE_SIZE 2048
/*
================
return a hash value for the string
================
*/
static long hashForString(const char *str) {
int i;
long hash;
char letter;
hash = 0;
i = 0;
while (str[i] != '\0') {
letter = tolower(str[i]);
hash+=(long)(letter)*(i+119);
i++;
}
hash &= (HASH_TABLE_SIZE-1);
return hash;
}
typedef struct stringDef_s {
struct stringDef_s *next;
const char *str;
} stringDef_t;
static int strPoolIndex = 0;
static char strPool[STRING_POOL_SIZE];
static int strHandleCount = 0;
static stringDef_t *strHandle[HASH_TABLE_SIZE];
const char *String_Alloc(const char *p) {
int len;
long hash;
stringDef_t *str, *last;
static const char *staticNULL = "";
if (p == NULL) {
return NULL;
}
if (*p == 0) {
return staticNULL;
}
hash = hashForString(p);
str = strHandle[hash];
while (str) {
if (strcmp(p, str->str) == 0) {
return str->str;
}
str = str->next;
}
len = strlen(p);
if (len + strPoolIndex + 1 < STRING_POOL_SIZE) {
int ph = strPoolIndex;
strcpy(&strPool[strPoolIndex], p);
strPoolIndex += len + 1;
str = strHandle[hash];
last = str;
while (str && str->next) {
last = str;
str = str->next;
}
str = UI_Alloc(sizeof(stringDef_t));
str->next = NULL;
str->str = &strPool[ph];
if (last) {
last->next = str;
} else {
strHandle[hash] = str;
}
return &strPool[ph];
}
return NULL;
}
void String_Report() {
float f;
Com_Printf("Memory/String Pool Info\n");
Com_Printf("----------------\n");
f = strPoolIndex;
f /= STRING_POOL_SIZE;
f *= 100;
Com_Printf("String Pool is %.1f%% full, %i bytes out of %i used.\n", f, strPoolIndex, STRING_POOL_SIZE);
f = allocPoint;
f /= MEM_POOL_SIZE;
f *= 100;
Com_Printf("Memory Pool is %.1f%% full, %i bytes out of %i used.\n", f, allocPoint, MEM_POOL_SIZE);
}
/*
=================
String_Init
=================
*/
void String_Init() {
int i;
for (i = 0; i < HASH_TABLE_SIZE; i++) {
strHandle[i] = 0;
}
strHandleCount = 0;
strPoolIndex = 0;
menuCount = 0;
UI_InitMemory();
if (DC && DC->getBindingBuf) {
Controls_GetConfig();
}
}
/*
=================
PC_SourceWarning
=================
*/
void PC_SourceWarning(int handle, char *format, ...) {
int line;
char filename[128];
va_list argptr;
char string[4096];
va_start (argptr, format);
vsnprintf (string, sizeof(string), format, argptr);
va_end (argptr);
filename[0] = '\0';
line = 0;
trap_PC_SourceFileAndLine(handle, filename, &line);
Com_Printf(S_COLOR_YELLOW "WARNING: %s, line %d: %s\n", filename, line, string);
}
/*
=================
PC_SourceError
=================
*/
void PC_SourceError(int handle, char *format, ...) {
int line;
char filename[128];
va_list argptr;
char string[4096];
va_start (argptr, format);
vsnprintf (string, sizeof(string), format, argptr);
va_end (argptr);
filename[0] = '\0';
line = 0;
trap_PC_SourceFileAndLine(handle, filename, &line);
Com_Printf(S_COLOR_RED "ERROR: %s, line %d: %s\n", filename, line, string);
}
/*
=================
LerpColor
=================
*/
void LerpColor(vec4_t a, vec4_t b, vec4_t c, float t)
{
int i;
// lerp and clamp each component
for (i=0; i<4; i++)
{
c[i] = a[i] + t*(b[i]-a[i]);
if (c[i] < 0)
c[i] = 0;
else if (c[i] > 1.0f)
c[i] = 1.0f;
}
}
static void Item_Color_Pulse( vec4_t dst, vec4_t src )
{
vec4_t lowLight;
lowLight[0] = 0.8f * src[0];
lowLight[1] = 0.8f * src[1];
lowLight[2] = 0.8f * src[2];
lowLight[3] = 0.8f * src[3];
LerpColor( src, lowLight, dst, 0.5f+0.5f*sinf( ((float)DC->realTime) / 100.0f ) );
}
/*
=================
Float_Parse
=================
*/
qboolean Float_Parse(const char **p, float *f) {
char *token;
token = COM_ParseExt(p, qfalse);
if (token && token[0] != 0) {
*f = fatof(token);
return qtrue;
} else {
return qfalse;
}
}
/*
=================
PC_Float_Parse
=================
*/
qboolean PC_Float_Parse(int handle, float *f) {
pc_token_t token;
int negative = qfalse;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (token.string[0] == '-') {
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
negative = qtrue;
}
if (token.type != TT_NUMBER) {
PC_SourceError(handle, "expected float but found %s\n", token.string);
return qfalse;
}
if (negative)
*f = -token.floatvalue;
else
*f = token.floatvalue;
if ( token.subtype & TT_HEX )
{
*f /= 255.0f;
}
return qtrue;
}
/*
=================
Color_Parse
=================
*/
qboolean Color_Parse(const char **p, vec4_t *c) {
int i;
float f;
for (i = 0; i < 4; i++) {
if (!Float_Parse(p, &f)) {
return qfalse;
}
(*c)[i] = f;
}
return qtrue;
}
/*
=================
PC_Color_Parse
=================
*/
qboolean PC_Color_Parse(int handle, vec4_t *c) {
int i;
float f;
for (i = 0; i < 4; i++) {
if (!PC_Float_Parse(handle, &f)) {
return qfalse;
}
(*c)[i] = f;
}
return qtrue;
}
/*
=================
Int_Parse
=================
*/
qboolean Int_Parse(const char **p, int *i) {
char *token;
token = COM_ParseExt(p, qfalse);
if (token && token[0] != 0) {
*i = atoi(token);
return qtrue;
} else {
return qfalse;
}
}
/*
=================
PC_Int_Parse
=================
*/
qboolean PC_Int_Parse(int handle, int *i) {
pc_token_t token;
int negative = qfalse;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (token.string[0] == '-') {
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
negative = qtrue;
}
if (token.type != TT_NUMBER) {
PC_SourceError(handle, "expected integer but found %s\n", token.string);
return qfalse;
}
*i = token.intvalue;
if (negative)
*i = - *i;
return qtrue;
}
/*
=================
Rect_Parse
=================
*/
qboolean Rect_Parse(const char **p, rectDef_t *r) {
if (Float_Parse(p, &r->x)) {
if (Float_Parse(p, &r->y)) {
if (Float_Parse(p, &r->w)) {
if (Float_Parse(p, &r->h)) {
return qtrue;
}
}
}
}
return qfalse;
}
/*
=================
PC_Rect_Parse
=================
*/
qboolean PC_Rect_Parse(int handle, rectDef_t *r) {
if (PC_Float_Parse(handle, &r->x)) {
if (PC_Float_Parse(handle, &r->y)) {
if (PC_Float_Parse(handle, &r->w)) {
if (PC_Float_Parse(handle, &r->h)) {
return qtrue;
}
}
}
}
return qfalse;
}
/*
=================
String_Parse
=================
*/
qboolean String_Parse(const char **p, const char **out) {
char *token;
token = COM_ParseExt(p, qfalse);
if (token && token[0] != 0) {
*(out) = String_Alloc(token);
return qtrue;
}
return qfalse;
}
/*
=================
PC_String_Parse
=================
*/
qboolean PC_String_Parse(int handle, const char **out) {
pc_token_t token;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
*(out) = String_Alloc(token.string);
return qtrue;
}
#define MAX_SCRIPT_LEN 2048
/*
=================
PC_Script_Parse
=================
*/
qboolean PC_Script_Parse(int handle, const char **out) {
char script[MAX_SCRIPT_LEN];
pc_token_t token;
memset(script, 0, sizeof(script));
// scripts start with { and have ; separated command lists.. commands are command, arg..
// basically we want everything between the { } as it will be interpreted at run time
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (Q_stricmp(token.string, "{") != 0) {
return qfalse;
}
for( ; ; )
{
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (Q_stricmp(token.string, "}") == 0) {
*out = String_Alloc(script);
return qtrue;
}
if (token.string[1] != '\0') {
Q_strcat(script, sizeof( script ), va("\"%s\"", token.string));
} else {
Q_strcat(script, sizeof( script ), token.string);
}
Q_strcat(script, sizeof( script ), " ");
}
}
// display, window, menu, item code
//
/*
==================
Init_Display
Initializes the display with a structure to all the drawing routines
==================
*/
void Init_Display( displayContextDef_t *dc )
{
DC = dc;
}
/*
==================
Window_Init
Initializes a window structure ( windowDef_t ) with defaults
==================
*/
void Window_Init(Window *w) {
memset(w, 0, sizeof(windowDef_t));
w->borderSize = 1;
w->foreColor[0] = w->foreColor[1] = w->foreColor[2] = w->foreColor[3] = 1.0;
w->cinematic = -1;
}
void Fade(int *flags, float *f, float clamp, int *nextTime, int offsetTime, qboolean bFlags, float fadeAmount) {
if (*flags & (WINDOW_FADINGOUT | WINDOW_FADINGIN)) {
if (DC->realTime > *nextTime) {
*nextTime = DC->realTime + offsetTime;
if (*flags & WINDOW_FADINGOUT) {
*f -= fadeAmount;
if (bFlags && *f <= 0.0f) {
*f = 0.0f;
*flags &= ~(WINDOW_FADINGOUT | WINDOW_VISIBLE);
}
} else {
*f += fadeAmount;
if (*f >= clamp) {
*f = clamp;
if (bFlags) {
*flags &= ~WINDOW_FADINGIN;
}
}
}
}
}
}
void border( rectDef_t * r, float size, const float *color, qhandle_t shader ) {
float rx,ry;
float cw,ch;
float x,y,w,h;
x = (r->x-(size*0.5f))*DC->glconfig.xscale + DC->glconfig.xbias;
y = (r->y-(size*0.5f))*DC->glconfig.yscale;
w = (r->w+size)*DC->glconfig.xscale;
h = (r->h+size)*DC->glconfig.yscale;
rx = size * DC->glconfig.xscale;
ry = size * DC->glconfig.yscale;
// find corner size
if ( rx * 2.0f >= w )
rx = w*0.5f;
if ( ry * 2.0f >= h )
ry = h*0.5f;
if ( rx < ry )
ry = rx;
if ( ry < rx )
rx = ry;
if ( color )
DC->setColor( color );
cw = w-rx*2.0f;
ch = h-ry*2.0f;
// corners
DC->drawStretchPic( x, y, rx+cw, ry, 0.0f, 0.0f, (rx+cw)/rx, 1.0f, shader );
DC->drawStretchPic( x, y+ry, rx, ry+ch, 0.0f, (ry+ch)/ry, 1.0f, 0.0f, shader );
DC->drawStretchPic( x+w-rx, y, rx, ry+ch, 1.0f, 0.0f, 0.0f, (ry+ch)/ry, shader );
DC->drawStretchPic( x+rx, y+h-ry, rx+cw, ry, (rx+cw)/rx, 1.0f, 0.0f, 0.0f, shader );
}
void Window_Paint( Window *w, int focus, int focusTime )
{
float fade = 0.0f;
float width;
if( !w )
return;
width = (w->flags & WINDOW_HASSCROLLBAR)?w->rect.w - (SCROLLBAR_SIZE+4.0f):w->rect.w;
// fade hi-lighter in and out
if ( !(w->flags & (WINDOW_NOFOCUS|WINDOW_DECORATION)) )
{
fade = min( 1.0f, (DC->realTime - focusTime)* ( 1.0f / (float)ITEM_FADE_TIME ) );
if ( !focus )
fade = 1.0f - fade;
}
LerpColor( w->backColor, w->outlineColor, w->color, fade );
if ( w->flags&WINDOW_DIMBACKGROUND ) {
const vec4_t backColor = { 0x15/255.0f, 0x27/255.0f, 0x59/255.0f, 0.7f };
float x = DC->glconfig.xbias / DC->glconfig.xscale;
DC->setColor( backColor );
DC->drawHandlePic( -x,0.0f,640.0f + x*2.0f,480.0f,DC->whiteShader );
}
switch( w->style )
{
case WINDOW_STYLE_EMPTY:
if ( fade > 0.0f )
{
vec4_t color;
color[ 0 ] = w->outlineColor[ 0 ];
color[ 1 ] = w->outlineColor[ 1 ];
color[ 2 ] = w->outlineColor[ 2 ];
color[ 3 ] = w->outlineColor[ 3 ] * fade;
if ( color[ 3 ] > 0.0f ) {
DC->fillRect( w->rect.x, w->rect.y, width, w->rect.h, color, (w->background)?w->background:DC->Assets.menu );
}
}
break;
case WINDOW_STYLE_FILLED:
{
DC->fillRect( w->rect.x, w->rect.y, width, w->rect.h, w->color, w->background );
} break;
case WINDOW_STYLE_SHADER:
{
float b = 0.0f;
if( w->flags & WINDOW_HASBORDER )
b = w->borderSize*0.5f;
if (w->flags & (WINDOW_FADINGOUT | WINDOW_FADINGIN)) {
Fade( &w->flags, &w->backColor[3], DC->Assets.fadeClamp, &w->nextTime, DC->Assets.fadeCycle, qtrue, DC->Assets.fadeAmount );
DC->setColor( w->backColor );
} else {
DC->setColor( w->color );
}
DC->drawHandlePic( w->rect.x+b, w->rect.y+b, width-b*2.0f, w->rect.h-b*2.0f, w->background );
DC->setColor( NULL );
} break;
case WINDOW_STYLE_CINEMATIC:
if( w->cinematic == -1 )
{
w->cinematic = DC->playCinematic( w->cinematicName, w->rect.x, w->rect.y, width, w->rect.h );
if( w->cinematic == -1 )
w->cinematic = -2;
}
if( w->cinematic >= 0 )
{
DC->runCinematicFrame( w->cinematic );
DC->drawCinematic( w->cinematic, w->rect.x, w->rect.y, width, w->rect.h );
}
break;
}
if( w->flags & WINDOW_HASBORDER )
border( &w->rect, w->borderSize, w->borderColor, DC->Assets.menu );
}
void Item_SetScreenCoords(itemDef_t *item, rectDef_t * r ) {
menuDef_t * m;
if ( !item )
return;
m = (menuDef_t*)item->parent;
item->window.rect = item->window.rectClient;
item->window.rect.y += r->y;
if ( item->window.flags & WINDOW_ALIGNWIDTH ) {
if ( m->window.flags & WINDOW_FULLSCREEN ) {
item->window.rect.x = -uiInfo.uiDC.glconfig.xbias;
item->window.rect.w = 640.0f + uiInfo.uiDC.glconfig.xbias*2.0f;
} else {
item->window.rect.x = r->x;
item->window.rect.w = r->w;
}
} else if ( item->window.flags & WINDOW_ALIGNRIGHT ) {
float offset = m->window.rectClient.w - (item->window.rect.x + item->window.rect.w);
item->window.rect.x = r->x + r->w - (item->window.rect.w + offset);
} else {
item->window.rect.x += r->x;
}
if ( item->snapleft ) {
item->window.rect.x = (item->snapleft->window.rect.x + item->snapleft->window.rect.w) - (0.5f/DC->glconfig.xscale);
}
if ( item->snapright ) {
item->window.rect.w = item->snapright->window.rect.x - item->window.rect.x + (0.5f/DC->glconfig.xscale);
}
// force the text rects to recompute
item->textRect.w = 0;
item->textRect.h = 0;
Item_ListBox_UpdateColumns( item );
}
// FIXME: consolidate this with nearby stuff
void Item_UpdatePosition(itemDef_t *item) {
menuDef_t *menu;
if (item == NULL || item->parent == NULL) {
return;
}
menu = item->parent;
Item_SetScreenCoords(item, &menu->window.rect );
}
// menus
void Menu_UpdatePosition(menuDef_t *menu) {
int i;
if (menu == NULL) {
return;
}
for (i = 0; i < menu->itemCount; i++) {
Item_SetScreenCoords(menu->items[i], &menu->window.rect);
}
}
void Menu_PostParse(menuDef_t *menu) {
int i;
float x = DC->glconfig.xbias / DC->glconfig.xscale;
if (menu == NULL) {
return;
}
menu->window.rect = menu->window.rectClient;
if (menu->window.flags & WINDOW_FULLSCREEN) {
menu->window.rect.x = 0.0f;
menu->window.rect.y = 0.0f;
menu->window.rect.w = 640.0f;
menu->window.rect.h = 480.0f;
}
if (menu->window.flags & WINDOW_ALIGNLEFT ) {
menu->window.rect.x -= x;
} else if (menu->window.flags & WINDOW_ALIGNRIGHT ) {
menu->window.rect.x += x;
} else if (menu->window.flags & WINDOW_ALIGNWIDTH ) {
float right = x + (menu->window.rect.x + menu->window.rect.w);
menu->window.rect.x -= x;
menu->window.rect.w = right - menu->window.rect.x;
}
Menu_UpdatePosition(menu);
// stretch full screen windows to fill widescreen
if ( menu->window.flags & WINDOW_FULLSCREEN )
{
menu->window.rect.x = -x;
menu->window.rect.w = 640.0f + (x*2.0f);
}
if ( !menu->font )
menu->font = DC->Assets.font;
if ( !menu->window.background && menu->window.style == WINDOW_STYLE_FILLED )
menu->window.background = DC->Assets.menu;
trap_SQL_Prepare( "UPDATE OR INSERT feeders SET name=$ SEARCH name $;" );
for ( i=0; i<menu->itemCount; i++ )
{
itemDef_t * item = menu->items[ i ];
if ( item->text ) {
sql( "SELECT index FROM strings SEARCH name $;", "tsId", item->text, &item->ownerText );
}
if ( !item->font )
item->font = menu->font;
if ( !item->window.background && item->window.style == WINDOW_STYLE_FILLED )
item->window.background = DC->Assets.menu;
if ( item->window.ownerDraw )
item->type = ITEM_TYPE_OWNERDRAW;
if ( (item->type == ITEM_TYPE_LISTBOX || item->type == ITEM_TYPE_SLIDER || item->window.ownerDraw == UI_TRAVELSCREEN) && item->window.name )
{
trap_SQL_BindText( 1, item->window.name );
trap_SQL_Step();
}
if ( item->type == ITEM_TYPE_LISTBOX ) {
int j;
listBoxDef_t * listBox = (listBoxDef_t*)item->typeData;
for ( j=0; j<listBox->numColumns; j++ ) {
if ( listBox->columnInfo[ j ].text ) {
sql( "SELECT index FROM strings SEARCH name $", "tsId", listBox->columnInfo[ j ].text, &listBox->columnInfo[ j ].ownerText );
}
}
}
// stretch full screen windows to fill widescreen
if ( item->window.flags & WINDOW_FULLSCREEN )
{
float x = DC->glconfig.xbias / DC->glconfig.xscale;
item->window.rect.x = -x;
item->window.rect.y = 0.0f;
item->window.rect.w = 640.0f + x*2.0f;
item->window.rect.h = 480.0f;
}
}
trap_SQL_Done();
menu->window.flags &= ~WINDOW_VISIBLE;
}
qboolean Item_HasFocus( itemDef_t * item )
{
return ((menuDef_t*)item->parent)->focusItem == item;
}
qboolean IsVisible(int flags) {
return (flags & WINDOW_VISIBLE && !(flags & WINDOW_FADINGOUT));
}
qboolean Rect_ContainsPoint(const rectDef_t *rect, float x, float y) {
if (rect) {
if (x > rect->x && x < rect->x + rect->w && y > rect->y && y < rect->y + rect->h) {
return qtrue;
}
}
return qfalse;
}
int Menu_ItemsMatchingGroup(menuDef_t *menu, const char *name) {
int i;
int count = 0;
for (i = 0; i < menu->itemCount; i++) {
if (Q_stricmp(menu->items[i]->window.name, name) == 0 || (menu->items[i]->window.group && Q_stricmp(menu->items[i]->window.group, name) == 0)) {
count++;
}
}
return count;
}
itemDef_t *Menu_GetMatchingItemByNumber(menuDef_t *menu, int index, const char *name) {
int i;
int count = 0;
for (i = 0; i < menu->itemCount; i++) {
if (Q_stricmp(menu->items[i]->window.name, name) == 0 || (menu->items[i]->window.group && Q_stricmp(menu->items[i]->window.group, name) == 0)) {
if (count == index) {
return menu->items[i];
}
count++;
}
}
return NULL;
}
void Script_SetColor(itemDef_t *item, const char **args) {
const char *name;
int i;
float f;
vec4_t *out;
// expecting type of color to set and 4 args for the color
if (String_Parse(args, &name)) {
out = NULL;
if (Q_stricmp(name, "backcolor") == 0) {
out = &item->window.backColor;
item->window.flags |= WINDOW_BACKCOLORSET;
} else if (Q_stricmp(name, "forecolor") == 0) {
out = &item->window.foreColor;
item->window.flags |= WINDOW_FORECOLORSET;
} else if (Q_stricmp(name, "bordercolor") == 0) {
out = &item->window.borderColor;
}
if (out) {
for (i = 0; i < 4; i++) {
if (!Float_Parse(args, &f)) {
return;
}
(*out)[i] = f;
}
}
}
}
void Script_SetAsset(itemDef_t *item, const char **args) {
const char *name;
// expecting name to set asset to
if (String_Parse(args, &name)) {
// check for a model
if (item->type == ITEM_TYPE_MODEL) {
}
}
}
void Script_SetBackground(itemDef_t *item, const char **args) {
const char *name;
// expecting name to set asset to
if (String_Parse(args, &name)) {
item->window.background = DC->registerShaderNoMip(name);
}
}
itemDef_t *Menu_FindItemByName(menuDef_t *menu, const char *p) {
int i;
if (menu == NULL || p == NULL) {
return NULL;
}
for (i = 0; i < menu->itemCount; i++) {
if (Q_stricmp(p, menu->items[i]->window.name) == 0) {
return menu->items[i];
}
}
return NULL;
}
void Script_SetItemColor(itemDef_t *item, const char **args) {
const char *itemname;
const char *name;
vec4_t color;
int i;
vec4_t *out;
// expecting type of color to set and 4 args for the color
if (String_Parse(args, &itemname) && String_Parse(args, &name)) {
itemDef_t *item2;
int j;
int count = Menu_ItemsMatchingGroup(item->parent, itemname);
if (!Color_Parse(args, &color)) {
return;
}
for (j = 0; j < count; j++) {
item2 = Menu_GetMatchingItemByNumber(item->parent, j, itemname);
if (item2 != NULL) {
out = NULL;
if (Q_stricmp(name, "backcolor") == 0) {
out = &item2->window.backColor;
} else if (Q_stricmp(name, "forecolor") == 0) {
out = &item2->window.foreColor;
item2->window.flags |= WINDOW_FORECOLORSET;
} else if (Q_stricmp(name, "bordercolor") == 0) {
out = &item2->window.borderColor;
}
if (out) {
for (i = 0; i < 4; i++) {
(*out)[i] = color[i];
}
}
}
}
}
}
void Menu_ShowItem( itemDef_t *item, qboolean bShow )
{
if( bShow )
{
item->window.flags |= WINDOW_VISIBLE;
}
else
{
item->window.flags &= ~WINDOW_VISIBLE;
// stop cinematics playing in the window
if( item->window.cinematic >= 0 )
{
DC->stopCinematic( item->window.cinematic );
item->window.cinematic = -1;
}
}
}
void Menu_ShowItemByName( menuDef_t *menu, const char *p, qboolean bShow )
{
itemDef_t *item;
int i;
int count = Menu_ItemsMatchingGroup( menu, p );
for( i = 0; i < count; i++ )
{
item = Menu_GetMatchingItemByNumber( menu, i, p );
if( item != NULL )
Menu_ShowItem( item, bShow );
}
}
void Menu_FadeItemByName(menuDef_t *menu, const char *p, qboolean fadeOut) {
itemDef_t *item;
int i;
int count = Menu_ItemsMatchingGroup(menu, p);
for (i = 0; i < count; i++) {
item = Menu_GetMatchingItemByNumber(menu, i, p);
if (item != NULL) {
if (fadeOut) {
item->window.flags |= (WINDOW_FADINGOUT | WINDOW_VISIBLE);
item->window.flags &= ~WINDOW_FADINGIN;
} else {
item->window.flags |= (WINDOW_VISIBLE | WINDOW_FADINGIN);
item->window.flags &= ~WINDOW_FADINGOUT;
}
}
}
}
menuDef_t *Menus_FindByName(const char *p) {
int i;
for (i = 0; i < menuCount; i++) {
if (Q_stricmp(Menus[i].window.name, p) == 0) {
return &Menus[i];
}
}
return NULL;
}
void Menus_ShowByName(const char *p) {
Menus_ActivateByName(p);
}
void Menus_OpenByName(const char *p) {
Menus_ActivateByName(p);
}
void Menus_FadeAllOutAndFadeIn( const char * p ) {
if ( p ) {
menuStack->openQueue[ menuStack->openQueueCount++ ] = Menus_FindByName( p );
}
menuStack->openTime = DC->realTime;
trap_Key_SetCatcher( KEYCATCH_UI );
}
static void Menu_RunCloseScript(menuDef_t *menu) {
if( menu && menu->onClose )
Menu_RunScript( menu, menu->onClose );
}
void Menus_Close( menuDef_t *menu )
{
if ( menu )
{
int i,n;
for ( n=0; n<menuStack->count; n++ )
{
if ( menuStack->m[ n ] == menu )
break;
}
if ( n == menuStack->count )
return; // didn't find menu
for ( i=n; i<menuStack->count; i++ )
{
menu = menuStack->m[ i ];
Menu_RunCloseScript(menu);
Menu_ClearFocusItem( menu, 0 );
}
menuStack->count = n;
Menus_SetVisible();
if ( menu->window.flags & WINDOW_PAUSEGAME ) {
trap_Cvar_Set( "cl_paused", "0" );
}
// focus goes to the menu on top of the stack
if ( menuStack->count > 0 )
{
menu = menuStack->m[ menuStack->count - 1 ];
if ( menu->onFocus )
Menu_RunScript( menu, menu->onFocus );
} else
{
#ifndef CGAME
trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
#endif
}
}
}
void Menus_CloseByName( const char *p )
{
menuDef_t *menu = Menus_FindByName(p);
if ( menu )
Menus_Close( menu );
}
void Menus_CloseAll()
{
int i;
for ( i=menuStack->count-1; i>=0; i-- )
{
menuDef_t * menu = menuStack->m[ i ];
Menu_RunCloseScript( menu );
menu->window.flags &= ~WINDOW_VISIBLE;
}
menuStack->count = 0;
#ifndef CGAME
trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
#endif
}
void Script_Show( itemDef_t *item, const char **args )
{
const char *name;
if( String_Parse( args, &name ) )
{
if( Q_stricmp( name, "this" ) == 0 )
Menu_ShowItem( item, qtrue );
else
Menu_ShowItemByName( item->parent, name, qtrue );
}
}
void Script_Hide(itemDef_t *item, const char **args) {
const char *name;
if( String_Parse( args, &name ) )
{
if( Q_stricmp( name, "this" ) == 0 )
Menu_ShowItem( item, qfalse );
else
Menu_ShowItemByName( item->parent, name, qfalse );
}
}
void Script_FadeIn(itemDef_t *item, const char **args) {
const char *name;
if (String_Parse(args, &name)) {
Menu_FadeItemByName(item->parent, name, qfalse);
}
}
void Script_FadeOut(itemDef_t *item, const char **args) {
const char *name;
if (String_Parse(args, &name)) {
Menu_FadeItemByName(item->parent, name, qtrue);
}
}
void Script_Open(itemDef_t *item, const char **args) {
const char *name;
if (String_Parse(args, &name)) {
Menus_OpenByName(name);
}
}
void Script_ConditionalOpen(itemDef_t *item, const char **args) {
const char *cvar;
const char *name1;
const char *name2;
float val;
if ( String_Parse(args, &cvar) && String_Parse(args, &name1) && String_Parse(args, &name2) ) {
val = DC->getCVarValue( cvar );
if ( val == 0.f ) {
Menus_OpenByName(name2);
} else {
Menus_OpenByName(name1);
}
}
}
void Script_Close(itemDef_t *item, const char **args) {
const char *name;
if ( String_Parse(args, &name) )
{
if ( !Q_stricmp( name, "all" ) )
Menus_CloseAll();
else
Menus_CloseByName(name);
}
}
void Menu_OrbitItemByName(menuDef_t *menu, const char *p, float x, float y, float cx, float cy, int time) {
itemDef_t *item;
int i;
int count = Menu_ItemsMatchingGroup(menu, p);
for (i = 0; i < count; i++) {
item = Menu_GetMatchingItemByNumber(menu, i, p);
if (item != NULL) {
item->window.flags |= (WINDOW_ORBITING | WINDOW_VISIBLE);
item->window.offsetTime = time;
item->window.rectEffects.x = cx;
item->window.rectEffects.y = cy;
item->window.rectClient.x = x;
item->window.rectClient.y = y;
Item_UpdatePosition(item);
}
}
}
void Script_Orbit(itemDef_t *item, const char **args) {
const char *name;
float cx, cy, x, y;
int time;
if (String_Parse(args, &name)) {
if ( Float_Parse(args, &x) && Float_Parse(args, &y) && Float_Parse(args, &cx) && Float_Parse(args, &cy) && Int_Parse(args, &time) ) {
Menu_OrbitItemByName(item->parent, name, x, y, cx, cy, time);
}
}
}
void Script_SetFocus( itemDef_t *item, const char **args )
{
const char* name;
itemDef_t* focusItem;
if( String_Parse( args, &name ) )
{
if( Q_stricmp( name, "this" ) == 0 )
focusItem = item;
else
focusItem = Menu_FindItemByName( item->parent, name );
Menu_SetFocusItem( item->parent, focusItem );
}
}
void Script_SetPlayerModel(itemDef_t *item, const char **args) {
const char *name;
if (String_Parse(args, &name)) {
DC->setCVar("model", name);
}
}
void Script_SetPlayerHead(itemDef_t *item, const char **args) {
const char *name;
if (String_Parse(args, &name)) {
DC->setCVar("headmodel", name);
}
}
void Script_SetCvar(itemDef_t *item, const char **args) {
const char *cvar, *val;
if (String_Parse(args, &cvar) && String_Parse(args, &val)) {
DC->setCVar(cvar, val);
}
}
void Script_Exec(itemDef_t *item, const char **args) {
const char *val;
if (String_Parse(args, &val)) {
DC->executeText(EXEC_APPEND, va("%s ; ", val));
}
}
static void Script_Sql(itemDef_t * item, const char **args) {
const char *val;
if (String_Parse(args, &val)) {
trap_SQL_Exec(val);
}
}
static void Script_Set(itemDef_t * item, const char **args) {
const char *cvar;
const char *sql;
if (String_Parse(args, &cvar)) {
if (String_Parse(args, &sql)) {
trap_SQL_Prepare( sql );
if ( trap_SQL_Step() ) {
char t[ MAX_INFO_STRING ];
trap_SQL_ColumnAsText( t, sizeof(t), 0 );
DC->setCVar( cvar, t );
}
trap_SQL_Done();
}
}
}
void Script_Play(itemDef_t *item, const char **args) {
const char *val;
if (String_Parse(args, &val)) {
DC->startLocalSound(DC->registerSound(val, qfalse), CHAN_LOCAL_SOUND);
}
}
void Script_playLooped(itemDef_t *item, const char **args) {
const char *val;
if (String_Parse(args, &val)) {
DC->stopBackgroundTrack();
DC->startBackgroundTrack(val, val);
}
}
void Script_EditField( itemDef_t * item, const char ** args )
{
const char *ctrl;
if( !String_Parse( args, &ctrl ) )
return;
if( Q_stricmp( ctrl, "this" ) != 0 )
item = Menu_FindItemByName( item->parent, ctrl );
if ( item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD )
{
UI_SetEditItem( item );
}
}
void Script_EditClear( itemDef_t * item, const char ** args )
{
const char *ctrl;
if( !String_Parse( args, &ctrl ) )
return;
if( Q_stricmp( ctrl, "this" ) != 0 )
item = Menu_FindItemByName( item->parent, ctrl );
if ( item->type == ITEM_TYPE_EDITFIELD )
{
editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData;
DC->setCVar( item->cvar, "" );
item->cursorPos = 0;
editPtr->paintOffset = 0;
}
}
void Item_RunScript( itemDef_t *, const char * );
void Script_RunHandler( itemDef_t *item, const char **args )
{
const char *ctrl, *script;
itemDef_t *targetItem;
if ( !item )
return;
if( !String_Parse( args, &ctrl ) )
return;
if( !String_Parse( args, &script ) )
return;
if( Q_stricmp( ctrl, "null" ) == 0 )
targetItem = item;
else if( Q_stricmp( ctrl, "this" ) == 0 )
targetItem = item;
else
targetItem = Menu_FindItemByName( item->parent, ctrl );
if( Q_stricmp( script, "action" ) == 0 )
{
if( targetItem->action )
Item_RunScript( targetItem, targetItem->action );
}
else if( Q_stricmp( script, "hotkey" ) == 0 )
{
int i, key;
if( !Int_Parse( args, &key ) )
return;
for( i = 0; i < MAX_KEY_ACTIONS; i++ )
if( targetItem->onHotKey[ i ].key == key )
{
Item_RunScript( targetItem, targetItem->onHotKey[ i ].action );
break;
}
}
else if( Q_stricmp( script, "focuskey" ) == 0 )
{
int i, key;
if( !Int_Parse( args, &key ) )
return;
for( i = 0; i < MAX_KEY_ACTIONS; i++ )
if( targetItem->onFocusKey[ i ].key == key )
{
Item_RunScript( targetItem, targetItem->onFocusKey[ i ].action );
break;
}
}
else if( Q_stricmp( script, "menukey" ) == 0 )
{
int i, key;
menuDef_t *menu;
if( !Int_Parse( args, &key ) )
return;
menu = item->parent;
for( i = 0; i < MAX_KEY_ACTIONS; i++ )
if( menu->onMenuKey[ i ].key == key )
{
Menu_RunScript( menu, menu->onMenuKey[ i ].action );
break;
}
}
}
void Script_SetItemBackground( itemDef_t *item, const char **args )
{
const char *itemName, *shaderName;
if( !String_Parse( args, &itemName ) )
return;
if( !String_Parse( args, &shaderName ) )
return;
item = Menu_FindItemByName( item->parent, itemName );
if( !item )
return;
item->window.background = DC->registerShaderNoMip( shaderName );
}
static const commandDef_t commandList[] =
{
{ "fadein", &Script_FadeIn }, // group/name
{ "fadeout", &Script_FadeOut }, // group/name
{ "show", &Script_Show }, // group/name
{ "hide", &Script_Hide }, // group/name
{ "setcolor", &Script_SetColor }, // works on this
{ "open", &Script_Open }, // menu
{ "conditionalopen", &Script_ConditionalOpen }, // menu
{ "close", &Script_Close }, // menu
{ "setasset", &Script_SetAsset }, // works on this
{ "setbackground", &Script_SetBackground }, // works on this
{ "setitembackground", &Script_SetItemBackground },
{ "setitemcolor", &Script_SetItemColor }, // group/name
{ "setfocus", &Script_SetFocus }, // sets this background color to team color
{ "setplayermodel", &Script_SetPlayerModel }, // sets this background color to team color
{ "setplayerhead", &Script_SetPlayerHead }, // sets this background color to team color
{ "setcvar", &Script_SetCvar }, // group/name
{ "exec", &Script_Exec }, // group/name
{ "sql", &Script_Sql },
{ "set", &Script_Set },
{ "play", &Script_Play }, // group/name
{ "playlooped", &Script_playLooped }, // group/name
{ "orbit", &Script_Orbit }, // group/name
{ "runhandler", &Script_RunHandler },
{ "editfield", &Script_EditField },
{ "editclear", &Script_EditClear }
};
static const int scriptCommandCount = sizeof(commandList) / sizeof(commandDef_t);
typedef struct
{
itemDef_t *item;
int runTime;
qboolean isParentHack;
char script[MAX_SCRIPT_LEN - 8]; //script is max MAX_SCRIPT_LEN,
//less 5 chars for 'sleep', less a space,
//less at least one digit of timeout, less a semicolon
} delayScript_t;
#define DELAY_SCRIPT_COUNT 3
static delayScript_t delayScriptList[DELAY_SCRIPT_COUNT] = { {0},{0},{0} };
//horrible, HORRIBLE, hackery to get around cases where a menuDef_t is "cast" into an itemDef_t...sort of - PD
static qboolean g_Item_RunScriptParentHack = qfalse;
static qboolean Item_AddDelayedScript( itemDef_t *item, int delay, qboolean isParentHack, const char *s )
{
int i, l;
for( i = 0; i < DELAY_SCRIPT_COUNT; i++ )
if( delayScriptList[ i ].item == 0 )
{
delayScriptList[ i ].item = (itemDef_t*)item->parent;
delayScriptList[ i ].runTime = DC->realTime + delay;
delayScriptList[ i ].isParentHack = isParentHack;
l = strlen( s );
memcpy( delayScriptList[ i ].script, s, l );
delayScriptList[ i ].script[ l ] = '\0';
return qtrue;
}
return qfalse;
}
static void Item_RunDelayedScripts()
{
int i;
for( i = 0; i < DELAY_SCRIPT_COUNT; i++ )
if( delayScriptList[ i ].item && delayScriptList[ i ].runTime <= DC->realTime )
{
itemDef_t hackItem;
itemDef_t *item = delayScriptList[ i ].item;
qboolean saveIsHack = qfalse;
delayScriptList[ i ].item = 0;
if( delayScriptList[ i ].isParentHack )
{
hackItem.parent = item;
item = &hackItem;
saveIsHack = g_Item_RunScriptParentHack;
g_Item_RunScriptParentHack = qtrue;
}
Item_RunScript( item, delayScriptList[ i ].script );
if( delayScriptList[i].isParentHack )
g_Item_RunScriptParentHack = saveIsHack;
}
}
void Item_RunScript( itemDef_t *item, const char * s )
{
const char * p = s;
int i;
qboolean bRan;
if ( debugMode )
return;
if( item && s && s[0] )
{
for( ; ; )
{
const char *command;
// expect command then arguments, ; ends command, NULL ends script
if( !String_Parse( &p, &command ) )
{
return;
}
if( command[0] == ';' && command[1] == '\0' )
{
continue;
}
if( Q_stricmp( command, "sleep" ) == 0 )
{
int delay;
const char *tmp;
const char *s;
if( !String_Parse( &p, &s ) )
return;
delay = atoi( s );
//do a little dance to skip the following ;
tmp = p;
if( !String_Parse( &tmp, &s ) )
return;
if( s[0] == ';' )
p = tmp;
else if( s[0] == '\0' )
return;
Item_AddDelayedScript( item, delay, g_Item_RunScriptParentHack, p );
//stop processing this, the rest of the script will run on a refresh
return;
}
bRan = qfalse;
for( i = 0; i < scriptCommandCount; i++ )
{
if ( Q_stricmp( command, commandList[i].name ) == 0 )
{
commandList[i].handler( item, &p );
bRan = qtrue;
break;
}
}
// not in our auto list, pass to handler
if( !bRan )
{
DC->runScript( &p );
}
}
}
}
void Menu_RunScript( menuDef_t *menu, const char *s )
{
itemDef_t item;
item.parent = menu;
g_Item_RunScriptParentHack = qtrue;
Item_RunScript( &item, s );
g_Item_RunScriptParentHack = qfalse;
}
qboolean Item_EnableShowViaCvar(itemDef_t *item, int flag)
{
if ( !item ) {
return qtrue;
}
if ( item->showif && *item->showif ) {
int r = 0;
trap_SQL_Prepare( item->showif );
if ( trap_SQL_Step() ) {
r = trap_SQL_ColumnAsInt(0);
}
trap_SQL_Done();
return r;
}
if ( item->enableCvar && *item->enableCvar && item->cvarTest && *item->cvarTest )
{
const char *p;
char script [ MAX_SCRIPT_LEN ];
char buff [ MAX_CVAR_VALUE_STRING ];
DC->getCVarString(item->cvarTest, buff, sizeof(buff));
Q_strncpyz( script, item->enableCvar, sizeof( script ) );
p = script;
for( ; ; )
{
const char *val;
// expect value then ; or NULL, NULL ends list
if (!String_Parse(&p, &val))
{
return (item->cvarFlags & flag) ? qfalse : qtrue;
}
if ( val[0] == ';' && val[1] == '\0' )
{
continue;
}
// enable it if any of the values are true
if ( item->cvarFlags & flag )
{
if ( Q_stricmp(buff, val) == 0 )
{
return qtrue;
}
} else
{
// disable it if any of the values are true
if (Q_stricmp(buff, val) == 0)
{
return qfalse;
}
}
}
}
return qtrue;
}
static void Item_SetFocusTime( itemDef_t * item, int time )
{
if ( item )
{
int t = time-item->focusTime;
// this item is losing the focus before it was completely gained
if ( t < ITEM_FADE_TIME )
{
item->focusTime = time - ( ITEM_FADE_TIME - t );
} else
item->focusTime = time;
}
}
static void Menu_ClearFocusItem( menuDef_t * menu, itemDef_t * item )
{
if ( menu->focusItem && menu->focusItem->leaveFocus )
Item_RunScript( menu->focusItem, menu->focusItem->leaveFocus );
// if the focus item has changed record the time it did
if ( menu->focusItem != item )
{
Item_SetFocusTime( item, DC->realTime );
Item_SetFocusTime( menu->focusItem, DC->realTime );
DC->toolTip( item );
}
menu->focusItem = item;
}
static qboolean canHaveFocus( itemDef_t * item ) {
if ( !item )
return qfalse;
// items can be enabled and disabled based on cvars
if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) {
return qfalse;
}
if (item->cvarFlags & (CVAR_SHOW | CVAR_HIDE) && !Item_EnableShowViaCvar(item, CVAR_SHOW)) {
return qfalse;
}
return !(item->window.flags & (WINDOW_DECORATION | WINDOW_NOFOCUS)) && (item->window.flags & WINDOW_VISIBLE);
}
static itemDef_t * Menu_GetNextFocusItem( menuDef_t * menu )
{
int i,start;
for ( start=0; start<menu->itemCount; start++ ) {
if ( menu->items[ start ] == menu->focusItem )
break;
}
for ( i=start+1; i<menu->itemCount; i++ ) {
if ( canHaveFocus( menu->items[ i ] ) )
return menu->items[ i ];
}
for ( i=0; i<=start; i++ ) {
if ( canHaveFocus( menu->items[ i ] ) )
return menu->items[ i ];
}
return 0;
}
static itemDef_t * Menu_GetPrevFocusItem( menuDef_t * menu )
{
int i,start;
for ( start=0; start<menu->itemCount; start++ ) {
if ( menu->items[ start ] == menu->focusItem )
break;
}
for ( i=start-1; i>=0; i-- ) {
if ( canHaveFocus( menu->items[ i ] ) ) {
return menu->items[ i ];
}
}
for ( i=menu->itemCount-1; i>=start; i-- ) {
if ( canHaveFocus( menu->items[ i ] ) ) {
return menu->items[ i ];
}
}
return 0;
}
static qboolean Menu_SetFocusItem( menuDef_t * menu, itemDef_t * item )
{
if ( item == menu->focusItem )
return qtrue;
if ( !canHaveFocus( item ) )
return qfalse;
//
// unfocus old item
//
Menu_ClearFocusItem( menu, item );
if ( item && item->onFocus )
Item_RunScript( item, item->onFocus );
return qtrue;
}
static void Item_Text_GetDataRect( itemDef_t *item, Rectangle * r, int offset )
{
if ( item->textdivx )
{
r->x = item->window.rect.x + item->textdivx + offset;
r->w = item->window.rect.w - item->textdivx - item->window.borderSize - offset;
r->y = item->window.rect.y;
r->h = item->window.rect.h;
} else if ( item->textdivy )
{
r->x = item->window.rect.x;
r->y = item->window.rect.y + item->textdivy + offset;
r->w = item->window.rect.w;
r->h = item->window.rect.h - item->textdivy - item->window.borderSize - offset;
} else
{
r->x = item->textRect.x + item->textRect.w + offset;
r->w = item->window.rect.w;
r->y = item->textRect.y;
r->h = item->textRect.h;
}
}
static void Item_Slider_Incr( itemDef_t * item ) {
editFieldDef_t *editDef = item->typeData;
float v = DC->getCVarValue( item->cvar );
float s = (editDef->flags&EDITFIELD_INTEGER)?1.0f:0.1f;
if ( v < editDef->maxVal )
Item_Slider_SetValue( item, min( v+s, editDef->maxVal ) );
}
static void Item_Slider_Decr( itemDef_t * item ) {
editFieldDef_t *editDef = item->typeData;
float v = DC->getCVarValue( item->cvar );
float s = (editDef->flags&EDITFIELD_INTEGER)?1.0f:0.1f;
if ( v > editDef->minVal )
Item_Slider_SetValue( item, max( v-s, editDef->minVal ) );
}
float Item_Slider_ThumbPosition(itemDef_t *item) {
float value, range;
editFieldDef_t *editDef = item->typeData;
rectDef_t * r = &item->textRect;
if (editDef == NULL && item->cvar) {
return r->x;
}
value = min( editDef->maxVal, max( editDef->minVal, DC->getCVarValue(item->cvar) ) );
if ( editDef->maxVal == editDef->minVal )
range = 0.5f;
else
range = (value - editDef->minVal) / (editDef->maxVal-editDef->minVal);
return r->x + SLIDER_BUTTON_WIDTH + (r->w - (SLIDER_THUMB_WIDTH+SLIDER_BUTTON_WIDTH*2.0f)) * range;
}
int Item_Slider_OverSlider(itemDef_t *item, float x, float y) {
rectDef_t * r = &item->textRect;
if (Rect_ContainsPoint(r, x, y)) {
float thumbX = Item_Slider_ThumbPosition( item );
if ( x >= thumbX && x <= thumbX + SLIDER_THUMB_WIDTH )
return WINDOW_LB_THUMB;
if ( x-r->x <= SLIDER_BUTTON_WIDTH )
return WINDOW_LB_LEFTARROW;
if ( x >= r->x + r->w - SLIDER_BUTTON_WIDTH )
return WINDOW_LB_RIGHTARROW;
return WINDOW_HORIZONTAL;
}
return 0;
}
void Item_ListBox_UpdateColumns( itemDef_t * item )
{
if ( item->type == ITEM_TYPE_LISTBOX )
{
int i;
listBoxDef_t * listPtr = (listBoxDef_t*)item->typeData;
for ( i=0; i<listPtr->numColumns; i++ )
{
if ( listPtr->flags & LISTBOX_ROTATE ) {
listPtr->columnInfo[ i ].width = listPtr->columnInfo[ i ].widthPerCent*0.01f*(item->window.rect.h-listPtr->titlebar);
listPtr->columnInfo[ i ].pos = (i==0)?0:listPtr->columnInfo[ i-1 ].pos + listPtr->columnInfo[ i-1 ].width;
} else {
listPtr->columnInfo[ i ].width = listPtr->columnInfo[ i ].widthPerCent*0.01f*item->window.rect.w;
listPtr->columnInfo[ i ].pos = (i==0)?0:listPtr->columnInfo[ i-1 ].pos + listPtr->columnInfo[ i-1 ].width;
}
}
}
}
void Item_ListBox_GetScrollBar( itemDef_t * item, listBoxDef_t * listPtr, rectDef_t * bar, rectDef_t * thumb, int rows, int count )
{
float ty = ((float)listPtr->startPos / (float)count); // thumbs relative position in entire list
float th = ((float)rows / (float)count); // relative amount that is being viewed ( thumb height )
bar->x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; // left side of scroll bar
bar->y = item->window.rect.y + listPtr->titlebar + 2.0f; // top of scroll bar
bar->h = item->window.rect.h - listPtr->titlebar - 4.0f; // height of scroll bar
bar->w = SCROLLBAR_SIZE - 4.0f;
thumb->x = bar->x + 2.0f;
thumb->y = bar->y + ty*bar->h;
thumb->w = bar->w - 4.0f;
thumb->h = th * bar->h;
}
static int Item_ListBox_NumRows( itemDef_t * item, listBoxDef_t * listPtr )
{
rectDef_t r;
Item_TableCell_Rect( item, 0, -1, qfalse, &r );
if ( listPtr->flags & LISTBOX_ROTATE ) {
return (int)(r.w / listPtr->elementHeight);
} else {
return (int)(r.h / listPtr->elementHeight);
}
}
int Item_ListBox_OverLB( itemDef_t *item, float x, float y )
{
listBoxDef_t* listPtr = (listBoxDef_t*)item->typeData;
int count = listPtr->numRows;
int rows = Item_ListBox_NumRows( item, listPtr );
if( count <= rows )
return 0;
if (item->window.flags & WINDOW_HORIZONTAL)
{
return 0; // fix me
} else
{
rectDef_t bar, thumb;
Item_ListBox_GetScrollBar( item, listPtr, &bar, &thumb, rows, count );
if ( Rect_ContainsPoint( &bar, x, y ) )
{
if ( y < thumb.y )
return WINDOW_LB_PGUP;
if ( y < thumb.y + thumb.h )
return WINDOW_LB_THUMB;
return WINDOW_LB_PGDN;
}
}
return 0;
}
void Item_MouseEnter(itemDef_t *item, float x, float y)
{
Item_RunScript(item, item->mouseEnter);
item->window.flags |= WINDOW_MOUSEOVER;
}
void Item_MouseLeave(itemDef_t *item)
{
Item_RunScript(item, item->mouseExit);
item->window.flags &= ~(WINDOW_MOUSEOVER);
}
itemDef_t *Menu_HitTest(menuDef_t *menu, float x, float y) {
int i;
for (i = 0; i < menu->itemCount; i++) {
if (Rect_ContainsPoint(&menu->items[i]->window.rect, x, y)) {
return menu->items[i];
}
}
return NULL;
}
qboolean Item_OwnerDraw_HandleMouseMove( itemDef_t *item, float x, float y )
{
if( item && DC->ownerDrawHandleMouseMove )
return DC->ownerDrawHandleMouseMove( item, x,y );
return qfalse;
}
qboolean Item_ListBox_SingleSelect_HandleKey( itemDef_t *item, int key )
{
int count, rows, scroll, sel, cursel;
listBoxDef_t *lp = (listBoxDef_t*)item->typeData;
count = lp->numRows;
rows = Item_ListBox_NumRows( item, lp );
scroll = max( 0, count-rows );
trap_SQL_Prepare ( "SELECT sel FROM feeders SEARCH name $;" );
trap_SQL_BindText ( 1, item->window.name );
if ( !trap_SQL_Step() ) {
return qfalse;
}
cursel =
sel = trap_SQL_ColumnAsInt(0);
trap_SQL_Done();
switch( key )
{
case K_MOUSE1:
case K_MOUSE2:
{
static int lastClickTime = 0;
int mouseItem = -1;
int i;
float y = DC->cursory - lp->titlebar - item->window.rect.y;
if ( y < 0.0f )
return qfalse; // clicked above list
for ( i=0; i<count; i++ ) {
rectDef_t r;
Item_TableCell_Rect( item, -1, i, qfalse, &r );
r.y -= 1.0f;
r.h += 2.0f;
if ( Rect_ContainsPoint( &r, DC->cursorx, DC->cursory ) ) {
mouseItem = lp->startPos+i;
break;
}
}
if ( mouseItem == -1 )
return qfalse; // click off the end of the list
if( DC->realTime < lastClickTime && item->action && sel == mouseItem )
{
Item_RunScript( item, item->action );
lastClickTime = 0; //no chaining double clicks
}
else
{
lastClickTime = DC->realTime + DOUBLE_CLICK_DELAY;
sel = mouseItem;
}
}
break;
case K_UPARROW:
case K_KP_UPARROW:
sel--;
break;
case K_MWHEELUP:
{
lp->startPos--;
if ( lp->startPos < 0 )
lp->startPos = 0;
} return qtrue;
case K_MWHEELDOWN:
{
lp->startPos++;
if ( lp->startPos > scroll )
lp->startPos = scroll;
} return qtrue;
case K_DOWNARROW:
case K_KP_DOWNARROW:
sel++;
break;
case K_PGUP:
case K_KP_PGUP:
return qtrue;
case K_PGDN:
case K_KP_PGDN:
return qtrue;
case K_HOME:
case K_KP_HOME:
return qtrue;
case K_END:
case K_KP_END:
return qtrue;
default:
return qfalse;
}
if( sel < 0 )
sel = 0;
if( sel >= count )
sel = count - 1;
if( sel < lp->startPos )
lp->startPos = sel;
if( sel >= lp->startPos + rows )
lp->startPos = sel - rows + 1;
if( lp->startPos < 0 )
lp->startPos = 0;
if( lp->startPos > scroll )
lp->startPos = scroll;
if ( cursel != sel && lp->buffer ) {
cell_t * cells = (cell_t*)lp->buffer;
trap_SQL_Prepare ( "UPDATE feeders SET sel=?1,id=?2,enabled=?3 SEARCH name $4;" );
trap_SQL_BindInt ( 1, sel );
trap_SQL_BindInt ( 2, cells[ sel * (lp->numColumns+2) ].integer );
trap_SQL_BindInt ( 3, cells[ sel * (lp->numColumns+2) + 1 ].integer );
trap_SQL_BindText ( 4, item->window.name );
trap_SQL_Step ();
trap_SQL_Done ();
lp->timeSel = DC->realTime;
}
if ( lp->select && ((cursel != sel) || (lp->flags&LISTBOX_ALWAYSSELECT)) ) {
Item_RunScript( item, lp->select );
}
return qtrue;
}
qboolean Item_ListBox_HandleKey( itemDef_t *item, int key, qboolean force )
{
listBoxDef_t *lp = (listBoxDef_t*)item->typeData;
int count = lp->numRows;
int rows = Item_ListBox_NumRows( item, lp );
int scroll = max( 0, count-rows );
if ( !force && !Item_HasFocus(item) )
return qfalse;
if( isMouseKey( key ) )
{
//handle the scroll bar
if( item->window.flags & WINDOW_LB_LEFTARROW )
{
if( --lp->startPos < 0 )
lp->startPos = 0;
return qtrue;
}
else if( item->window.flags & WINDOW_LB_RIGHTARROW )
{
if( ++lp->startPos > scroll )
lp->startPos = scroll;
return qtrue;
}
else if( item->window.flags & WINDOW_LB_PGUP )
{
if( (lp->startPos -= rows) < 0 )
lp->startPos = 0;
return qtrue;
}
else if( item->window.flags & WINDOW_LB_PGDN )
{
if( (lp->startPos += rows) > scroll )
lp->startPos = scroll;
return qtrue;
}
else if( item->window.flags & WINDOW_LB_THUMB )
{
return qtrue;
}
}
if ( key == K_ENTER && item->action )
{
Item_RunScript(item, item->action);
return qtrue;
}
return Item_ListBox_SingleSelect_HandleKey( item, key );
}
int Item_Multi_CountSettings(itemDef_t *item) {
multiDef_t *multiPtr = (multiDef_t*)item->typeData;
if (multiPtr == NULL) {
return 0;
}
return multiPtr->count;
}
int Item_Multi_FindCvarByValue(itemDef_t *item) {
char buff[1024];
float value = 0;
int i;
multiDef_t *multiPtr = (multiDef_t*)item->typeData;
if (multiPtr) {
if (multiPtr->strDef) {
DC->getCVarString(item->cvar, buff, sizeof(buff));
} else {
value = DC->getCVarValue(item->cvar);
}
for (i = 0; i < multiPtr->count; i++) {
if (multiPtr->strDef) {
if (Q_stricmp(buff, multiPtr->cvarStr[i]) == 0) {
return i;
}
} else {
if (multiPtr->cvarValue[i] == value) {
return i;
}
}
}
}
return 0;
}
const char *Item_Multi_Setting(itemDef_t *item) {
char buff[1024];
float value = 0;
int i;
multiDef_t *multiPtr = (multiDef_t*)item->typeData;
if (multiPtr) {
if (multiPtr->strDef) {
DC->getCVarString(item->cvar, buff, sizeof(buff));
} else {
value = DC->getCVarValue(item->cvar);
}
for (i = 0; i < multiPtr->count; i++) {
if (multiPtr->strDef) {
if (Q_stricmp(buff, multiPtr->cvarStr[i]) == 0) {
return multiPtr->cvarList[i];
}
} else {
if (multiPtr->cvarValue[i] == value) {
return multiPtr->cvarList[i];
}
}
}
}
return "";
}
static void setnumericfield( itemDef_t * item, editFieldDef_t * editPtr, int v )
{
if ( v < editPtr->minVal )
v = editPtr->minVal;
if ( v > editPtr->maxVal )
v = editPtr->maxVal;
DC->setCVar( item->cvar, va( "%i", v ) );
}
void Item_TextField_HandleKey(itemDef_t *item, int key) {
char buff[1024];
int len;
//itemDef_t *newItem = NULL;
editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData;
if ( !item->cvar )
return;
memset(buff, 0, sizeof(buff));
DC->getCVarString(item->cvar, buff, sizeof(buff));
len = strlen(buff);
if (editPtr->maxChars && len > editPtr->maxChars) {
len = editPtr->maxChars;
}
if ( item->cursorPos > len )
item->cursorPos = len;
if ( key & K_CHAR_FLAG ) {
key &= ~K_CHAR_FLAG;
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;
}
//
// ignore any non printable chars
//
if ( key < 32 || !item->cvar) {
return;
}
if (item->type == ITEM_TYPE_NUMERICFIELD) {
if (key < '0' || key > '9') {
return;
}
}
if (!DC->getOverstrikeMode()) {
if (( len == MAX_EDITFIELD - 1 ) || (editPtr->maxChars && len >= editPtr->maxChars)) {
return;
}
memmove( &buff[item->cursorPos + 1], &buff[item->cursorPos], len + 1 - item->cursorPos );
} else {
if (editPtr->maxChars && item->cursorPos >= editPtr->maxChars) {
return;
}
}
buff[item->cursorPos] = key;
if (item->type == ITEM_TYPE_NUMERICFIELD) {
setnumericfield( item, editPtr, atoi(buff) );
DC->getCVarString(item->cvar, buff, sizeof(buff));
len = strlen(buff);
} else
DC->setCVar(item->cvar, buff);
if (item->cursorPos < len + 1) {
item->cursorPos++;
if (editPtr->maxPaintChars && item->cursorPos > editPtr->maxPaintChars) {
editPtr->paintOffset++;
}
}
} else {
switch ( key )
{
case K_DEL:
if ( item->cursorPos < len ) {
memmove( buff + item->cursorPos, buff + item->cursorPos + 1, len - item->cursorPos);
DC->setCVar(item->cvar, buff);
}
break;
case K_RIGHTARROW:
if ( item->cursorPos < len )
{
item->cursorPos++;
if ( editPtr->maxPaintChars && (item->cursorPos-editPtr->paintOffset) >= editPtr->maxPaintChars )
editPtr->paintOffset++;
}
break;
case K_LEFTARROW:
if ( item->cursorPos > 0 ) item->cursorPos--;
if ( item->cursorPos < editPtr->paintOffset ) editPtr->paintOffset--;
break;
case K_HOME:
if ( item->type == ITEM_TYPE_NUMERICFIELD )
{
setnumericfield( item, editPtr, editPtr->minVal );
} else
{
item->cursorPos = 0;
editPtr->paintOffset = 0;
}
break;
case K_END:
if ( item->type == ITEM_TYPE_NUMERICFIELD )
{
setnumericfield( item, editPtr, editPtr->maxVal );
} else
{
item->cursorPos = len;
if ( item->cursorPos > editPtr->maxPaintChars )
editPtr->paintOffset = len - editPtr->maxPaintChars;
}
break;
case K_INS:
DC->setOverstrikeMode(!DC->getOverstrikeMode());
break;
case K_UPARROW:
case K_MWHEELUP:
if ( item->type == ITEM_TYPE_NUMERICFIELD )
{
setnumericfield( item, editPtr, atoi( buff )+1 );
}
break;
case K_DOWNARROW:
case K_MWHEELDOWN:
if ( item->type == ITEM_TYPE_NUMERICFIELD )
{
setnumericfield( item, editPtr, atoi( buff )-1 );
}
break;
case K_PGUP:
if ( item->type == ITEM_TYPE_NUMERICFIELD )
{
setnumericfield( item, editPtr, atoi( buff )-10 );
}
break;
case K_PGDN:
if ( item->type == ITEM_TYPE_NUMERICFIELD )
{
setnumericfield( item, editPtr, atoi( buff )+10 );
}
break;
case K_MOUSE1:
{
// find cursor position
//trap_R_RenderText
} break;
case K_ENTER:
case K_KP_ENTER:
Item_Action( item );
return;
}
}
}
static void Scroll_ListBox_AutoFunc(void *p) {
scrollInfo_t *si = (scrollInfo_t*)p;
if (DC->realTime > si->nextScrollTime) {
// need to scroll which is done by simulating a click to the item
// this is done a bit sideways as the autoscroll "knows" that the item is a listbox
// so it calls it directly
Item_ListBox_HandleKey(si->item, si->scrollKey, qfalse);
si->nextScrollTime = DC->realTime + si->adjustValue;
}
if (DC->realTime > si->nextAdjustTime) {
si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST;
if (si->adjustValue > SCROLL_TIME_FLOOR) {
si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET;
}
}
}
static void Scroll_ListBox_ThumbFunc(void *p)
{
scrollInfo_t *si = (scrollInfo_t*)p;
listBoxDef_t *listPtr = (listBoxDef_t*)si->item->typeData;
if (si->item->window.flags & WINDOW_HORIZONTAL)
return; // fix me
if ( si->item->window.flags & WINDOW_LB_THUMB )
{
if (DC->cursory != si->yStart)
{
rectDef_t b,t;
int count = listPtr->numRows;
int rows = Item_ListBox_NumRows( si->item, listPtr );
int move;
Item_ListBox_GetScrollBar( si->item, listPtr, &b, &t, rows, count );
move = (DC->cursory-si->yStart) / ((b.h-t.h) / (float)count);
if ( move != 0 )
{
int scroll = max( 0, count-rows );
listPtr->startPos += move;
if ( listPtr->startPos < 0 ) listPtr->startPos = 0;
if ( listPtr->startPos > scroll) listPtr->startPos = scroll;
si->yStart = DC->cursory;
}
}
} else
{
if (DC->realTime > si->nextScrollTime)
{
// need to scroll which is done by simulating a click to the item
// this is done a bit sideways as the autoscroll "knows" that the item is a listbox
// so it calls it directly
Item_ListBox_HandleKey(si->item, si->scrollKey, qfalse);
si->nextScrollTime = DC->realTime + si->adjustValue;
}
if (DC->realTime > si->nextAdjustTime)
{
si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST;
if (si->adjustValue > SCROLL_TIME_FLOOR) {
si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET;
}
}
}
}
static float Item_Slider_ValueFromCursor( itemDef_t * item, float x )
{
editFieldDef_t * editDef = item->typeData;
rectDef_t * r = &item->textRect;
float value;
float left;
float width;
left = r->x + SLIDER_BUTTON_WIDTH;
width = r->w - (SLIDER_THUMB_WIDTH + SLIDER_BUTTON_WIDTH*2.0f);
value = (x-left) / (width);
if ( value < 0.0f )
value = 0.0f;
if ( value > 1.0f )
value = 1.0f;
// scale and bias to slider value
value *= (editDef->maxVal - editDef->minVal);
value += editDef->minVal;
return value;
}
static void Item_Slider_SetValue( itemDef_t * item, float value ) {
editFieldDef_t* editDef = item->typeData;
DC->setCVar(item->cvar, (editDef->flags&EDITFIELD_INTEGER)?va("%d",(int)value):va("%f", value));
}
static void Scroll_Slider_ThumbFunc(void *p) {
scrollInfo_t* si = (scrollInfo_t*)p;
float value = Item_Slider_ValueFromCursor( si->item, DC->cursorx + si->offset );
Item_Slider_SetValue( si->item, value );
Item_RunScript( si->item, si->item->action );
}
static void Item_UpdateData( itemDef_t * item, field_t * field, byte ** data )
{
switch( field->type )
{
case FIELD_WINDOW: *data = (byte*)&item->window; break;
case FIELD_ITEM: *data = (byte*)item; break;
case FIELD_MENU: *data = 0; break;
case FIELD_EDITFIELD:
{
switch ( item->type )
{
case ITEM_TYPE_EDITFIELD:
case ITEM_TYPE_NUMERICFIELD:
case ITEM_TYPE_YESNO:
case ITEM_TYPE_BIND:
case ITEM_TYPE_SLIDER:
case ITEM_TYPE_TEXT:
Item_ValidateTypeData(item);
*data = (byte*)item->typeData;
break;
default:
*data = 0;
break;
}
} break;
case FIELD_LISTBOX:
{
if ( item->type == ITEM_TYPE_LISTBOX )
{
Item_ValidateTypeData(item);
*data = (byte*)item->typeData;
} else
*data = 0;
} break;
case FIELD_PROGRESS:
Item_ValidateTypeData(item);
*data = (byte*)item->typeData;
break;
case FIELD_COLUMNS:
case FIELD_EFFECT:
*data = 0; break;
}
}
static void Menu_UpdateData( menuDef_t * menu, field_t * field, byte ** data )
{
switch( field->type )
{
case FIELD_WINDOW:
*data = (byte*)&menu->window;
break;
case FIELD_MENU:
*data = (byte*)menu;
break;
case FIELD_ITEM:
case FIELD_EDITFIELD:
case FIELD_LISTBOX:
case FIELD_COLUMNS:
case FIELD_EFFECT:
*data = 0;
break;
}
}
#ifdef DEVELOPER
static void snaptorect( rectDef_t * r, int * x, int * y, int w, int h, int box )
{
int X1 = (int)r->x;
int X2 = (int)(r->x + r->w );
int Y1 = (int)r->y;
int Y2 = (int)(r->y + r->h );
int X = *x;
int Y = *y;
if ( abs( X-X1 ) < 4 )
*x = X1;
else if ( abs( X-X2 ) < 4 )
*x = X2;
else if ( box && (abs( X+w-X1 ) < 4) )
*x = X1-w;
else if ( box && (abs( X+w-X2 ) < 4) )
*x = X2-w;
if ( abs( Y-Y1 ) < 4 )
*y = Y1;
else if ( abs( Y-Y2 ) < 4 )
*y = Y2;
else if ( box && (abs( Y+h-Y1 ) < 4) )
*y = Y1-h;
else if ( box && (abs( Y+h-Y2 ) < 4) )
*y = Y2-h;
}
static void DebugMode_Snap( itemDef_t * item, int * x, int * y, int box )
{
int i;
menuDef_t * menu = (menuDef_t *)item->parent;
for ( i=0; i<menu->itemCount; i++ )
{
if ( menu->items[ i ] == item )
continue;
snaptorect( &menu->items[ i ]->window.rect, x,y, item->window.rect.w, item->window.rect.h, box );
}
snaptorect( &menu->window.rect, x,y, item->window.rect.w, item->window.rect.h, box );
}
static void DebugMode_MoveControl_AutoFunc(void *p) {
scrollInfo_t *si = (scrollInfo_t*)p;
rectDef_t * r = &si->item->window.rect;
menuDef_t * menu = (menuDef_t *)si->item->parent;
int x = (DC->cursorx - (int)si->xStart);
int y = (DC->cursory - (int)si->yStart);
if ( debug.dontsnap == 0 ) {
x = (x >> 1) << 1;
y = (y >> 1) << 1;
DebugMode_Snap( si->item, &x, &y, si->scrollKey & SCROLL_DEBUGMODE_MOVE );
}
if ( si->scrollKey & SCROLL_DEBUGMODE_MOVE )
{
r->x = x;
r->y = y;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_LEFT )
{
int right = r->x + r->w;
r->x = x;
r->w = right - r->x;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_RIGHT )
{
r->w = x - r->x;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_TOP )
{
int bottom = r->y + r->h;
r->y = y;
r->h = bottom - r->y;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_BOTTOM )
{
r->h = y - r->y;
}
si->item->window.rectClient.x = r->x - menu->window.rect.x;
si->item->window.rectClient.y = r->y - menu->window.rect.y;
si->item->window.rectClient.w = r->w;
si->item->window.rectClient.h = r->h;
Item_ListBox_UpdateColumns( si->item );
}
static void DebugMode_MoveMenu_AutoFunc(void *p) {
scrollInfo_t *si = (scrollInfo_t*)p;
menuDef_t * menu = (menuDef_t*)si->item;
rectDef_t * r = &menu->window.rect;
int x = (DC->cursorx - (int)si->xStart);
int y = (DC->cursory - (int)si->yStart);
int i;
x = (x >> 1) << 1;
y = (y >> 1) << 1;
if ( debug.dontsnap == 0 )
{
float sx = DC->glconfig.xbias / DC->glconfig.xscale;
float sw = 640.0f + (2.0f*x);
rectDef_t screen;
screen.x = 0.0f;
screen.y = 0.0f;
screen.w = 640.0f;
screen.h = 480.0f;
for ( i=0; i<menuStack->count; i++ )
{
if ( menuStack->m[ i ] == menu )
continue;
snaptorect( &menuStack->m[ i ]->window.rect, &x,&y, menu->window.rect.w, menu->window.rect.h, ( si->scrollKey & SCROLL_DEBUGMODE_MOVE ) );
}
snaptorect( &screen, &x,&y, menu->window.rect.w, menu->window.rect.h, ( si->scrollKey & SCROLL_DEBUGMODE_MOVE ) );
screen.x = -sx;
screen.w = sw;
snaptorect( &screen, &x,&y, menu->window.rect.w, menu->window.rect.h, ( si->scrollKey & SCROLL_DEBUGMODE_MOVE ) );
}
if ( si->scrollKey & SCROLL_DEBUGMODE_MOVE )
{
r->x = x;
r->y = y;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_LEFT )
{
int right = r->x + r->w;
r->x = x;
r->w = right - r->x;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_RIGHT )
{
r->w = x - r->x;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_TOP )
{
int bottom = r->y + r->h;
r->y = y;
r->h = bottom - r->y;
}
if ( si->scrollKey & SCROLL_DEBUGMODE_BOTTOM )
{
r->h = y - r->y;
}
Menu_UpdatePosition( menu );
}
int DebugMode_GetScroll( rectDef_t * r, int x, int y )
{
int s = 0;
const float b = 3.0f;
if ( Rect_ContainsPoint( r, (float)x,(float)y ) )
{
if ( x - r->x < b )
s |= SCROLL_DEBUGMODE_LEFT;
if ( r->x + r->w - x < b )
s |= SCROLL_DEBUGMODE_RIGHT;
if ( y - r->y < b )
s |= SCROLL_DEBUGMODE_TOP;
if ( r->y + r->h - y < b )
s |= SCROLL_DEBUGMODE_BOTTOM;
if ( s == 0 )
s = SCROLL_DEBUGMODE_MOVE;
}
return s;
}
static itemDef_t * ed_item;
static char * ed_cvar( const char * section, const char * field_name ) {
return va( "ed_%s_%s", section, field_name );
}
void UI_Editor_UpdateItem()
{
byte * data = 0;
byte * b;
char * section = 0;
char * cvar;
int i;
for ( i=0; i<fieldCount; i++ )
{
if ( fields[ i ].flags & FIELD_DONTEDIT || !fields[ i ].name )
continue;
if ( fields[ i ].flags & FIELD_SECTION )
section = fields[ i ].name;
Item_UpdateData( ed_item, fields + i, &data );
if ( data == 0 )
continue;
b = data + fields[ i ].offset;
cvar = ed_cvar( section, fields[ i ].name );
switch( fields[ i ].type )
{
//case FIELD_SCRIPT:
case FIELD_STRING:
{
char tmp[ MAX_INFO_STRING ];
DC->getCVarString( cvar, tmp, sizeof(tmp) );
*(const char**)( b ) = (tmp[0]=='\0')?0:String_Alloc( tmp );
} break;
case FIELD_INTEGER:
{
*(int*)( b ) = (int)DC->getCVarValue( cvar );
} break;
case FIELD_FLOAT:
{
*(float*)( b ) = DC->getCVarValue( cvar );
} break;
case FIELD_FONT:
{
char tmp[ MAX_INFO_STRING ];
DC->getCVarString( cvar, tmp, sizeof(tmp) );
*(qhandle_t*)( b ) = atoi(tmp);
} break;
case FIELD_BIT:
{
int v = (int)DC->getCVarValue( cvar );
int * d = (int*)( b );
if ( v )
*d |= fields[ i ].bit;
else
*d &= ~fields[ i ].bit;
} break;
default:
{
//return;
} break;
}
}
}
void UI_Editor_EditItem( itemDef_t * item )
{
byte * data = 0;
byte * b;
char * section = 0;
char cvar[ 256 ];
int i;
ed_item = item;
for ( i=0; i<fieldCount; i++ )
{
field_t * f = fields + i;
if ( f->flags & FIELD_DONTEDIT || !f->name)
continue;
if ( f->flags & FIELD_SECTION )
section = f->name;
Q_strncpyz( cvar, ed_cvar( section, f->name ), sizeof(cvar) );
DC->setCVar( cvar, "" );
Item_UpdateData( item, f, &data );
if ( !data )
continue;
b = data + f->offset;
switch( f->type )
{
//case FIELD_SCRIPT:
case FIELD_STRING:
{
DC->setCVar( cvar, (b)?*(const char**)(b):"" );
} break;
case FIELD_FONT:
case FIELD_INTEGER:
{
DC->setCVar( cvar, va("%d", *(int*)(b) ) );
} break;
case FIELD_FLOAT:
{
DC->setCVar( cvar, va("%f", *(float*)(b) ) );
} break;
case FIELD_BIT:
{
DC->setCVar( cvar, va("%d", *(int*)(b) & f->bit ) );
} break;
}
}
Menus_OpenByName( "editor" );
}
static char * getfontname( qhandle_t font )
{
char info[ MAX_INFO_STRING ];
char * s = info;
trap_R_GetFonts( info, sizeof(info) );
for ( ;; )
{
int h = atoi( COM_ParseExt( &s, qfalse ) );
char * n = COM_ParseExt( &s, qfalse );
if ( h == font )
return n;
}
return 0;
}
extern void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
static void escapesequence( char * buffer, int size, const char * src )
{
int i,j;
for ( i=0,j=0; i<size && src[j]; i++, j++ )
{
if ( src[ j ] == 9 )
{
buffer[ i++ ] = '\\';
buffer[ i ] = 't';
} else if ( src[ j ] == 10 )
{
buffer[ i++ ] = '\\';
buffer[ i ] = 'n';
} else
buffer[ i ] = src[ j ];
}
buffer[ i ] = '\0';
}
void UI_Editor_SaveField( fileHandle_t f, itemDef_t * item, byte * data, field_t * field )
{
const char * t = 0;
data += field->offset;
switch( field->type )
{
case FIELD_SCRIPT:
{
//const char * s = *(const char**)( (byte*)item + itemFields[ i ].offset );
//t = (!s)?0:va("\t\t\t%s\t\t{ %s }\n", itemFields[ i ].name, s );
} break;
case FIELD_STRING:
{
const char * s = *(const char**)(data);
if ( s && s[ 0 ] != '\0' )
{
char buffer[ MAX_INFO_STRING ];
escapesequence( buffer, sizeof(buffer), s );
t = va("\t\t\t%s\t\t\"%s\"\n", field->name, buffer );
}
} break;
case FIELD_FLOAT:
{
t = va("\t\t\t%s\t\t%f\n", field->name, *(float*)(data) );
} break;
case FIELD_RECT:
{
rectDef_t * r = (rectDef_t*)( data );
t = va("\t\t\t%s\t\t%d %d %d %d\n", field->name, (int)r->x, (int)r->y, (int)r->w, (int)r->h );
} break;
case FIELD_BIT:
{
if ( *(int*)( data ) & field->bit )
t = va("\t\t\t%s\n", field->name );
} break;
case FIELD_FONT:
{
if ( item->font != ((menuDef_t*)item->parent)->font )
t = va("\t\t\t%s\t\t\"%s\"\n", field->name, getfontname( *(int*)( data ) ) );
} break;
case FIELD_FUNC:
break;
case FIELD_INTEGER:
{
int v = *(int*)( data );
if ( v != 0 )
{
int i;
for ( i=0; i<MAX_MULTI_CVARS; i++ )
{
if ( field->fieldEnum[ i ].name && field->fieldEnum[ i ].value == v )
{
t = va("\t\t\t%s\t\t%s\n", field->name, field->fieldEnum[ i ].name );
break;
}
}
if ( !t )
t = va("\t\t\t%s\t\t%d\n", field->name, v );
}
} break;
}
if ( t )
trap_FS_Write( t, strlen(t), f );
}
void Save_Item( fileHandle_t f, itemDef_t * item )
{
int i;
byte * data = 0;
for ( i=0; i<fieldCount; i++ )
{
field_t * field = fields + i;
Item_UpdateData( item, field, &data );
if ( !data )
continue;
if ( !(field->flags & FIELD_DONTSAVE) )
UI_Editor_SaveField( f, item, data, field );
}
}
#endif
void Item_StartCapture(itemDef_t *item, int key)
{
#ifdef DEVELOPER
if ( debugMode )
{
if ( !debug.menu || ((menuDef_t*)item->parent) == debug.menu )
{
if ( key == K_MOUSE1 || key == K_MOUSE3 )
{
scrollInfo.xStart = DC->cursorx - item->window.rect.x;
scrollInfo.yStart = DC->cursory - item->window.rect.y;
scrollInfo.scrollKey = DebugMode_GetScroll( &item->window.rect, DC->cursorx, DC->cursory );
if ( scrollInfo.scrollKey & SCROLL_DEBUGMODE_RIGHT )
scrollInfo.xStart -= item->window.rect.w;
if ( scrollInfo.scrollKey & SCROLL_DEBUGMODE_BOTTOM )
scrollInfo.yStart -= item->window.rect.h;
scrollInfo.item = item;
captureData = &scrollInfo;
captureFunc = &DebugMode_MoveControl_AutoFunc;
itemCapture = item;
debug.dontsnap = trap_Key_IsDown( K_SHIFT );
} else if ( key == K_MOUSE2 )
{
UI_Editor_EditItem( item );
itemCapture = item;
}
}
return;
}
#endif
switch( item->type )
{
case ITEM_TYPE_EDITFIELD:
case ITEM_TYPE_NUMERICFIELD:
case ITEM_TYPE_LISTBOX:
{
item->window.flags &= ~(WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN);
item->window.flags |= Item_ListBox_OverLB(item, DC->cursorx, DC->cursory);
if( item->window.flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW) )
{
scrollInfo.nextScrollTime = DC->realTime + SCROLL_TIME_START;
scrollInfo.nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST;
scrollInfo.adjustValue = SCROLL_TIME_START;
scrollInfo.scrollKey = key;
scrollInfo.scrollDir = (item->window.flags & WINDOW_LB_LEFTARROW) ? qtrue : qfalse;
scrollInfo.item = item;
captureData = &scrollInfo;
captureFunc = &Scroll_ListBox_AutoFunc;
itemCapture = item;
}
else if( item->window.flags & WINDOW_LB_THUMB )
{
scrollInfo.scrollKey = key;
scrollInfo.item = item;
scrollInfo.xStart = DC->cursorx;
scrollInfo.yStart = DC->cursory;
captureData = &scrollInfo;
captureFunc = &Scroll_ListBox_ThumbFunc;
itemCapture = item;
}
break;
}
case ITEM_TYPE_SLIDER:
{
int flags = Item_Slider_OverSlider( item, DC->cursorx, DC->cursory );
if( flags )
{
if ( flags & WINDOW_LB_LEFTARROW ) {
Item_Slider_Decr( item );
} else if ( flags & WINDOW_LB_RIGHTARROW ) {
Item_Slider_Incr( item );
} else
{
scrollInfo.scrollKey = key;
scrollInfo.item = item;
scrollInfo.xStart = DC->cursorx;
scrollInfo.yStart = DC->cursory;
captureData = &scrollInfo;
captureFunc = &Scroll_Slider_ThumbFunc;
itemCapture = item;
// if the click missed the thumb, snap the thumb there and then start the drag
if ( flags & WINDOW_HORIZONTAL )
{
float value = Item_Slider_ValueFromCursor( item, DC->cursorx );
Item_Slider_SetValue( item, value );
scrollInfo.offset = 0.0f;
} else
scrollInfo.offset = Item_Slider_ThumbPosition( item ) - DC->cursorx;
}
} else
{
}
break;
}
}
}
void Item_StopCapture( itemDef_t *item )
{
item->window.flags &= ~(WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN);
}
void Item_Action(itemDef_t *item) {
if (item) {
Item_RunScript(item, item->action);
}
}
itemDef_t * Menu_NearItem( menuDef_t * menu, itemDef_t * focus, int direction ) {
int i;
itemDef_t * best = NULL;
// scan the items from top to bottom
for ( i=0; i<menu->itemCount; i++ ) {
itemDef_t* item = menu->items[ i ];
if ( item == focus )
continue;
if( !(item->window.flags & (WINDOW_VISIBLE) ) )
continue;
if ( (item->window.flags & (WINDOW_DECORATION | WINDOW_FADINGOUT)) )
continue;
// items can be enabled and disabled based on cvars
if( item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE)
&& !Item_EnableShowViaCvar( item, CVAR_ENABLE ) )
continue;
if( item->cvarFlags & (CVAR_SHOW | CVAR_HIDE)
&& !Item_EnableShowViaCvar( item, CVAR_SHOW ) )
continue;
if ( !focus ) {
return item;
}
switch ( direction ) {
case K_UPARROW:
if ( item->window.rect.y > focus->window.rect.y )
continue;
if ( !best || item->window.rect.y > best->window.rect.y )
best = item;
break;
case K_RIGHTARROW:
if ( item->window.rect.x < focus->window.rect.x )
continue;
if ( !best || item->window.rect.x < best->window.rect.x )
best = item;
break;
case K_DOWNARROW:
if ( item->window.rect.y < focus->window.rect.y )
continue;
if ( !best || item->window.rect.y < best->window.rect.y )
best = item;
break;
case K_LEFTARROW:
if ( item->window.rect.x > focus->window.rect.x )
continue;
if ( !best || item->window.rect.x > best->window.rect.x )
best = item;
break;
}
}
return best;
}
itemDef_t* Menu_SetPrevCursorItem( menuDef_t *menu )
{
itemDef_t * item = Menu_GetPrevFocusItem( menu );
if ( item )
{
Menu_SetFocusItem( menu, item );
item = menu->focusItem;
if ( item && (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) )
{
UI_SetEditItem( item );
}
}
return 0;
}
itemDef_t *Menu_SetNextCursorItem(menuDef_t *menu)
{
itemDef_t * item = Menu_GetNextFocusItem( menu );
if ( item )
{
Menu_SetFocusItem( menu, item );
item = menu->focusItem;
if ( item && (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) )
{
UI_SetEditItem( item );
}
}
return 0;
}
// set all windows on the menu stack to visible, all menus on the stack and UNDER a fullscreen menu
// are set to invisible
static void Menus_SetVisible() {
int i;
for ( i=0; i<menuCount; i++ )
Menus[ i ].window.flags &= ~WINDOW_VISIBLE;
for ( i=menuStack->count-1; i>=0; i-- ) {
menuStack->m[ i ]->window.flags |= WINDOW_VISIBLE;
if( menuStack->m[ i ]->window.flags&WINDOW_FULLSCREEN )
break;
}
}
void Menus_Activate( menuDef_t *menu )
{
if ( !menu )
return;
// if this is the first menu then enable the keycatcher
if ( menuStack->count == 0 )
{
#ifndef CGAME
trap_Key_SetCatcher( KEYCATCH_UI );
#endif
// was originially added to try to fix the double warp to npc bug
// but the caused menus not to show up after a vid restart in some
// situations
//trap_Key_ClearStates();
}
if ( menu->window.flags & WINDOW_PAUSEGAME ) {
trap_Cvar_Set( "cl_paused", "1" );
}
menu->time = DC->realTime;
// push this window onto the stack
menuStack->m[ menuStack->count++ ] = menu;
Menus_SetVisible();
if ( menu->onOpen )
Menu_RunScript( menu, menu->onOpen );
// this window has the focus
if ( menu->onFocus )
Menu_RunScript( menu, menu->onFocus );
if( menu->soundName && *menu->soundName )
{
DC->startBackgroundTrack( menu->soundName, menu->soundName );
}
}
static itemDef_t * Menu_GetItemUnderCursor( menuDef_t * menu, float x, float y )
{
int i;
// scan the items from top to bottom
for ( i=menu->itemCount-1; i>=0; i-- )
{
itemDef_t* item = menu->items[ i ];
if ( !debugMode )
{
if( !(item->window.flags & (WINDOW_VISIBLE) ) )
continue;
if ( (item->window.flags & (WINDOW_DECORATION | WINDOW_FADINGOUT)) )
continue;
// items can be enabled and disabled based on cvars
if( item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE)
&& !Item_EnableShowViaCvar( item, CVAR_ENABLE ) )
continue;
if( item->cvarFlags & (CVAR_SHOW | CVAR_HIDE)
&& !Item_EnableShowViaCvar( item, CVAR_SHOW ) )
continue;
}
// first item hit, this one get the focus, really there shouldn't be overlaping items in well designed gui's
if ( Rect_ContainsPoint( &item->window.rect, x, y ) )
return item;
}
return 0;
}
// key is assumed to be down and mouse is assumed to over item
qboolean Item_HandleKeyDown( itemDef_t * item, int key )
{
int i;
qboolean actionKey;
// see if it's a focus key
for( i=0; i<MAX_KEY_ACTIONS; i++ )
{
if( item->onFocusKey[ i ].key == key )
{
Item_RunScript( item, item->onFocusKey[ i ].action );
return qtrue;
}
}
actionKey = (key == K_MOUSE1) || (key==K_MOUSE2) || (key==K_MOUSE3) || (key==K_ENTER);
// play click sound
if( actionKey && DC->Assets.itemFocusSound )
{
switch( item->type )
{
case ITEM_TYPE_LISTBOX:
{
listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData;
if( listPtr->flags & LISTBOX_NOTSELECTABLE )
break;
}
case ITEM_TYPE_YESNO:
case ITEM_TYPE_MULTI:
case ITEM_TYPE_BUTTON:
case ITEM_TYPE_OWNERDRAW:
DC->startLocalSound( DC->Assets.itemFocusSound, CHAN_LOCAL_SOUND );
break;
}
}
switch( item->type )
{
case ITEM_TYPE_EDITFIELD:
case ITEM_TYPE_NUMERICFIELD:
{
if ( actionKey )
{
UI_SetEditItem( item );
DC->setOverstrikeMode(qfalse);
return qtrue;
}
} break;
case ITEM_TYPE_LISTBOX:
if ( Item_ListBox_HandleKey( item, key, qfalse ) )
return qtrue;
break;
case ITEM_TYPE_YESNO:
if ( item->cvar && actionKey )
{
DC->setCVar(item->cvar, va("%i", !DC->getCVarValue(item->cvar)));
Item_RunScript(item, item->action);
return qtrue;
}
break;
case ITEM_TYPE_MULTI:
{
multiDef_t *multiPtr = (multiDef_t*)item->typeData;
if ( multiPtr && item->cvar && actionKey ) {
int current = Item_Multi_FindCvarByValue(item);
int max = Item_Multi_CountSettings (item);
if ( key == K_MOUSE2 ) {
current--;
if ( current < 0 ) {
current = max-1;
}
} else {
current++;
if ( current >= max ) {
current = 0;
}
}
if (multiPtr->strDef) {
DC->setCVar(item->cvar, multiPtr->cvarStr[current]);
} else {
float value = multiPtr->cvarValue[current];
if (((float)((int) value)) == value) {
DC->setCVar(item->cvar, va("%i", (int) value ));
} else {
DC->setCVar(item->cvar, va("%f", value ));
}
}
Item_RunScript(item, item->action);
return qtrue;
}
} break;
case ITEM_TYPE_OWNERDRAW:
{
if ( DC->ownerDrawHandleKey( item->window.ownerDraw, item->window.ownerDrawFlags, key ) || actionKey )
{
Item_RunScript(item, item->action);
return qtrue;
}
} break;
case ITEM_TYPE_BIND:
{
if ( actionKey )
{
DC->setCVar( "ui_waitingForKey", "1" );
g_waitingForKey = qtrue;
g_bindItem = item;
return qtrue;
} else if ( key == K_BACKSPACE )
{
int id = BindingIDFromName(item->cvar);
if (id != -1) {
g_bindings[id].bind1 = -1;
g_bindings[id].bind2 = -1;
}
Controls_SetConfig(qtrue);
DC->setCVar( "ui_waitingForKey", "0" );
g_waitingForKey = qfalse;
g_bindItem = NULL;
return qtrue;
}
} break;
case ITEM_TYPE_SLIDER:
{
if ( key == K_MWHEELDOWN )
Item_Slider_Decr( item );
else if ( key == K_MWHEELUP )
Item_Slider_Incr( item );
} break;
}
return qfalse;
}
//
// sends input into a menu. returns false if input should be passed on to the next menu.
//
qboolean Menu_HandleKeyDown( menuDef_t *menu, int key )
{
int i;
itemDef_t *item = menu->focusItem;
// key bind
if( g_waitingForKey )
return Item_Bind_HandleKey(g_bindItem, key );
// edit field
if( g_editItem )
{
Item_TextField_HandleKey(g_editItem, key);
if ( (isMouseKey( key ) && !Rect_ContainsPoint( &g_editItem->window.rect, DC->cursorx, DC->cursory )) || // clicked out side edit field
(key == K_TAB) || // tabbed to next control
(key == K_ESCAPE) || // escaped out of edit field
(key == K_ENTER) || // finalized input
(key == K_KP_ENTER) )
{
if( key != K_KP_ENTER && key != K_ENTER )
UI_SetEditItem( NULL );
// all focus has been starved because this field has been capturing the focus, now
// that the lock has been released let the menu reset under the cursor
Display_MouseMove( DC->cursorx, DC->cursory );
// all keys in this case are allowed to continue outside of this control except for
// escape. escape just loses focus. to escape from the menu you have to press twice.
if ( key == K_ESCAPE )
return qtrue;
} else
return qtrue; // input consumed.
}
// get the item with focus or the item being clicked
if( isMouseKey( key ) )
{
itemDef_t *tmp;
if ( !Rect_ContainsPoint( &menu->window.rect, DC->cursorx, DC->cursory ) ) {
return qfalse; // click outside window
}
tmp = Menu_GetItemUnderCursor( menu, DC->cursorx, DC->cursory );
if ( tmp )
{
item = tmp;
}
}
if ( item )
{
if ( isMouseKey( key ) )
{
if( !itemCapture )
{
Item_StartCapture( item, key );
}
}
if ( debugMode )
return qfalse;
if ( Item_HandleKeyDown( item, key ) )
return qtrue;
}
#ifdef DEVELOPER
else if ( debugMode && !itemCapture && key == K_MOUSE1 )
{
// hack to edit menus
if ( !debug.menu || debug.menu == menu ) {
scrollInfo.xStart = DC->cursorx - menu->window.rect.x;
scrollInfo.yStart = DC->cursory - menu->window.rect.y;
scrollInfo.scrollKey = DebugMode_GetScroll( &menu->window.rect, DC->cursorx, DC->cursory );
if ( scrollInfo.scrollKey & SCROLL_DEBUGMODE_RIGHT )
scrollInfo.xStart -= menu->window.rect.w;
if ( scrollInfo.scrollKey & SCROLL_DEBUGMODE_BOTTOM )
scrollInfo.yStart -= menu->window.rect.h;
scrollInfo.item = (itemDef_t *)menu;
captureData = &scrollInfo;
captureFunc = &DebugMode_MoveMenu_AutoFunc;
itemCapture = (itemDef_t*)menu;
}
}
#endif
// see if it's a menu hot key
for( i = 0; i < menu->itemCount; i++ )
{
int j;
if( !(menu->items[ i ]->window.flags & WINDOW_VISIBLE ) )
continue;
for( j = 0; j < MAX_KEY_ACTIONS; j++ )
if( menu->items[ i ]->onHotKey[ j ].key == key )
{
const char * script = menu->items[ i ]->onHotKey[ j ].action;
if ( script && script[0] ) {
Item_RunScript( menu->items[ i ], script );
} else {
Item_RunScript( menu->items[ i ], menu->items[ i ]->action );
}
return qtrue;
}
}
// check menu key scripts
for( i = 0; i < MAX_KEY_ACTIONS; i++ )
if( menu->onMenuKey[i].key == key )
{
if( menu->onMenuKey[i].action )
Menu_RunScript( menu, menu->onMenuKey[i].action );
return qtrue;
}
// check for tabbing
if ( key == K_TAB ) {
(( trap_Key_IsDown( K_SHIFT ) )?Menu_SetPrevCursorItem:Menu_SetNextCursorItem)( menu );
return qtrue;
}
#if 0
// this was added for rudimentary gamepad support. although it messes with lists and keying up and down
switch ( key ) {
case K_UPARROW:
case K_DOWNARROW:
case K_LEFTARROW:
case K_RIGHTARROW:
Menu_SetFocusItem( menu, Menu_NearItem( menu, menu->focusItem, key ) );
return qtrue;
case K_JOY15:
if ( menu->focusItem ) {
itemDef_t * item = menu->focusItem;
if ( (item->type == ITEM_TYPE_BUTTON || item->type == ITEM_TYPE_TEXT) ) {
Item_RunScript(item, item->action);
} else {
Item_HandleKeyDown( menu->focusItem, K_MOUSE1 );
}
}
return qtrue;
}
#endif
return qfalse;
}
void Item_TextColor( itemDef_t *item, vec4_t *newColor )
{
menuDef_t *parent = (menuDef_t*)item->parent;
Fade( &item->window.flags, &item->window.foreColor[3], parent->fadeClamp,
&item->window.nextTime, parent->fadeCycle, qtrue, parent->fadeAmount );
if( Item_HasFocus( item ) && item->window.flags & WINDOW_FOCUSFLASH )
{
Item_Color_Pulse( *newColor, parent->focusColor );
}
else if( item->textStyle & ITEM_TEXTSTYLE_BLINK )
{
Item_Color_Pulse( *newColor, item->window.foreColor );
}
else
{
memcpy( newColor, &item->window.foreColor, sizeof( vec4_t ) );
// items can be enabled and disabled based on cvars
}
if( item->enableCvar && *item->enableCvar && item->cvarTest && *item->cvarTest )
{
if( item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar( item, CVAR_ENABLE ) )
{
memcpy( newColor, &parent->disableColor, sizeof( vec4_t ) );
}
}
}
void Item_Text_PaintMore( itemDef_t *item, float offset, const char* text, vec4_t color )
{
Rectangle r;
Item_Text_GetDataRect( item, &r, offset );
trap_R_RenderText ( &r,
item->textscale,
color,
text,
-1,
TEXT_ALIGN_LEFT,
item->textaligny,
item->textStyle,
item->font,
-1,
&item->textRect
);
}
void Item_TextRect( itemDef_t * item, rectDef_t * r )
{
r->x = item->window.rect.x + item->window.borderSize;
r->y = item->window.rect.y + item->window.borderSize;
r->w = item->window.rect.w - item->window.borderSize*2;
r->h = item->window.rect.h - item->window.borderSize*2;
if ( item->textdivx )
r->w = item->textdivx - item->window.borderSize;
else if ( item->textdivy )
r->h = item->textdivy - item->window.borderSize;
}
void Item_Text_Paint( itemDef_t *item )
{
char tmp[ MAX_INFO_STRING ];
const char* text = 0;
vec4_t color;
Rectangle r;
if ( item->ownerText && DC->getOwnerText )
{
text = DC->getOwnerText( item->ownerText );
} else if ( item->text )
{
text = item->text;
} else if ( item->cvar && item->type != ITEM_TYPE_NUMERICFIELD && item->type != ITEM_TYPE_EDITFIELD )
{
DC->getCVarString( item->cvar, tmp, sizeof(tmp) );
text = tmp;
}
if ( !text || *text == '\0' )
{
item->textRect = item->window.rect;
item->textRect.w = 0;
return;
}
Item_TextColor( item, &color );
Item_TextRect( item, &r );
if ( item->window.flags&WINDOW_TEXTSCROLL) {
menuDef_t * menu = item->parent;
float t = min( r.h*3.1f, (DC->realTime - menu->time) * 0.03f ) - item->window.rect.h;
r.y -= t;
}
trap_R_SetColor( item->window.backColor );
trap_R_RenderText( &r,
item->textscale,
color,
text,
-1,
item->textalignx,
item->textaligny,
item->textStyle,
item->font,
-1,
&item->textRect
);
}
void AdjustFrom640(float *x, float *y, float *w, float *h);
void Item_TextField_Paint(itemDef_t *item)
{
editFieldDef_t* editPtr = (editFieldDef_t*)item->typeData;
Rectangle r; // edit region
Item_Text_Paint(item);
if (item->cvar)
{
char tmp[ MAX_INFO_STRING ];
int cursor = -1;
DC->getCVarString( item->cvar, tmp, sizeof(tmp) );
if (item->cvarFlags & CVAR_PASSWORD)
{
int i;
for( i=0; tmp[i]; i++)
tmp[i]='*';
}
if ( item == g_editItem )
{
cursor = item->cursorPos - editPtr->paintOffset;
if ( cursor < 0 )
cursor = 0;
}
if (editPtr->flags&EDITFIELD_CDKEY) {
char t[ 32 ];
int i;
for ( i=0; i<16; i++ ) {
if ( tmp[i] >= 'a' && tmp[i] <= 'z' ) {
t[ i ] = (tmp[i]-'a') + 'A';
} else {
t[ i ] = tmp[ i ];
}
}
Com_Memcpy( tmp, t, 4 );
tmp[ 4 ] = '-';
Com_Memcpy( tmp+5, t+4, 4 );
tmp[ 9 ] = '-';
Com_Memcpy( tmp+10, t+8, 4 );
tmp[ 14 ] = '-';
Com_Memcpy( tmp+15, t+12, 4 );
cursor += cursor/4;
}
Item_Text_GetDataRect( item, &r, 8 );
if ( item->window.flags & WINDOW_NOFOCUS ) {
float b = item->window.borderSize;
DC->setColor( item->window.backColor );
DC->drawHandlePic( r.x-b, r.y, r.w+b*2.0f, r.h, uiInfo.uiDC.whiteShader );
DC->drawRect( r.x-b, r.y, r.w+b*2.0f, r.h, 2.0f, item->window.outlineColor );
}
trap_R_RenderText ( &r,
item->textscale,
item->window.foreColor,
((editPtr->flags&EDITFIELD_MONEY)?fn( atoi(tmp), FN_CURRENCY ):
(tmp + editPtr->paintOffset)),
editPtr->maxPaintChars,
ITEM_ALIGN_LEFT,
item->textaligny,
item->textStyle,
item->font,
cursor,
&item->textRect
);
}
}
void Item_YesNo_Paint(itemDef_t *item)
{
float value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0.0f;
if ( item->text )
Item_Text_Paint( item );
Item_Text_PaintMore( item, (item->text)?8:0, DC->getOwnerText((value!=0.0f)?uiInfo.T_Yes:uiInfo.T_No), item->window.foreColor );
}
void Item_Multi_Paint(itemDef_t *item) {
if ( item->values ) {
multiDef_t *multiPtr;
char tmp[ 8192 ];
cell_t* cells;
int i;
Item_ValidateTypeData(item);
multiPtr = (multiDef_t*)item->typeData;
multiPtr->count = trap_SQL_Select( item->values, tmp, sizeof(tmp) );
cells = (cell_t*)tmp;
for ( i=0; i<multiPtr->count; i++ ) {
multiPtr->cvarStr[ i ] = String_Alloc(cells[i*2+0].string);
multiPtr->cvarList[ i ] = String_Alloc(cells[i*2+1].string);
}
multiPtr->strDef = qtrue;
}
if (item->text)
Item_Text_Paint(item);
Item_Text_PaintMore( item, (item->text)?8:0, Item_Multi_Setting(item), item->window.foreColor );
}
static void drawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader )
{
AdjustFrom640( &x,&y,&w,&h );
DC->drawStretchPic( x,y,w,h, s1,t1, s2,t2, hShader );
}
void Item_Progress_Paint( itemDef_t * item )
{
progressDef_t * pd = (progressDef_t*)item->typeData;
float x = item->window.rect.x + item->window.borderSize;
float y = item->window.rect.y;
float w = item->window.rect.w - item->window.borderSize*2.0f;
float h = item->window.rect.h;
float u;
int used = 0;
int total = 0;
if ( item->values ) {
if ( trap_SQL_Prepare( item->values ) ) {
trap_SQL_Step();
used = trap_SQL_ColumnAsInt( 0 );
total = trap_SQL_ColumnAsInt( 1 );
trap_SQL_Done();
}
}
if ( total <= 0 ) {
return;
}
DC->setColor( item->window.foreColor );
u = (w*used)/total;
if ( u > 0.0f ) {
drawStretchPic( x,y,u,h, 0.0f, 0.0f, (float)used, 1.0f, pd->full );
}
drawStretchPic( x+u,y,w-u,h, 0.0f, 0.0f, (float)(total-used), 1.0f, pd->empty );
Item_Text_Paint( item );
}
typedef struct
{
char* name;
float defaultvalue;
float value;
} configcvar_t;
/*
=================
Controls_GetKeyAssignment
=================
*/
static void Controls_GetKeyAssignment (char *command, int *twokeys)
{
int count;
int j;
char b[256];
twokeys[0] = twokeys[1] = -1;
count = 0;
for ( j = 0; j < 256; j++ )
{
DC->getBindingBuf( j, b, 256 );
if ( *b == 0 ) {
continue;
}
if ( !Q_stricmp( b, command ) ) {
twokeys[count] = j;
count++;
if (count == 2) {
break;
}
}
}
}
/*
=================
Controls_GetConfig
=================
*/
void Controls_GetConfig( void )
{
int i;
int twokeys[2];
// iterate each command, get its numeric binding
for (i=0; i < lengthof( g_bindings ); i++)
{
Controls_GetKeyAssignment(g_bindings[i].command, twokeys);
g_bindings[i].bind1 = twokeys[0];
g_bindings[i].bind2 = twokeys[1];
}
//s_controls.invertmouse.curvalue = DC->getCVarValue( "m_pitch" ) < 0;
//s_controls.smoothmouse.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "m_filter" ) );
//s_controls.alwaysrun.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_run" ) );
//s_controls.autoswitch.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cg_autoswitch" ) );
//s_controls.sensitivity.curvalue = UI_ClampCvar( 2, 30, Controls_GetCvarValue( "sensitivity" ) );
//s_controls.joyenable.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "in_joystick" ) );
//s_controls.joythreshold.curvalue = UI_ClampCvar( 0.05, 0.75, Controls_GetCvarValue( "joy_threshold" ) );
//s_controls.freelook.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_freelook" ) );
}
/*
=================
Controls_SetConfig
=================
*/
void Controls_SetConfig(qboolean restart)
{
int i;
// iterate each command, get its numeric binding
for (i=0; i < lengthof( g_bindings ); i++)
{
if (g_bindings[i].bind1 != -1)
{
DC->setBinding( g_bindings[i].bind1, g_bindings[i].command );
if (g_bindings[i].bind2 != -1)
DC->setBinding( g_bindings[i].bind2, g_bindings[i].command );
}
}
//if ( s_controls.invertmouse.curvalue )
// DC->setCVar("m_pitch", va("%f),-fabs( DC->getCVarValue( "m_pitch" ) ) );
//else
// trap_Cvar_SetValue( "m_pitch", fabs( trap_Cvar_VariableValue( "m_pitch" ) ) );
//trap_Cvar_SetValue( "m_filter", s_controls.smoothmouse.curvalue );
//trap_Cvar_SetValue( "cl_run", s_controls.alwaysrun.curvalue );
//trap_Cvar_SetValue( "cg_autoswitch", s_controls.autoswitch.curvalue );
//trap_Cvar_SetValue( "sensitivity", s_controls.sensitivity.curvalue );
//trap_Cvar_SetValue( "in_joystick", s_controls.joyenable.curvalue );
//trap_Cvar_SetValue( "joy_threshold", s_controls.joythreshold.curvalue );
//trap_Cvar_SetValue( "cl_freelook", s_controls.freelook.curvalue );
DC->executeText(EXEC_APPEND, "in_restart\n");
//trap_Cmd_ExecuteText( EXEC_APPEND, "in_restart\n" );
}
/*
=================
Controls_SetDefaults
=================
*/
void Controls_SetDefaults( void )
{
int i;
// iterate each command, set its default binding
for (i=0; i < lengthof( g_bindings ); i++)
{
g_bindings[i].bind1 = g_bindings[i].defaultbind1;
g_bindings[i].bind2 = g_bindings[i].defaultbind2;
}
//s_controls.invertmouse.curvalue = Controls_GetCvarDefault( "m_pitch" ) < 0;
//s_controls.smoothmouse.curvalue = Controls_GetCvarDefault( "m_filter" );
//s_controls.alwaysrun.curvalue = Controls_GetCvarDefault( "cl_run" );
//s_controls.autoswitch.curvalue = Controls_GetCvarDefault( "cg_autoswitch" );
//s_controls.sensitivity.curvalue = Controls_GetCvarDefault( "sensitivity" );
//s_controls.joyenable.curvalue = Controls_GetCvarDefault( "in_joystick" );
//s_controls.joythreshold.curvalue = Controls_GetCvarDefault( "joy_threshold" );
//s_controls.freelook.curvalue = Controls_GetCvarDefault( "cl_freelook" );
}
int BindingIDFromName(const char *name) {
int i;
for (i=0; i < lengthof( g_bindings ); i++)
{
if (Q_stricmp(name, g_bindings[i].command) == 0) {
return i;
}
}
return -1;
}
char g_nameBind1[32];
char g_nameBind2[32];
void BindingFromName(const char *cvar) {
int i, b1, b2;
// iterate each command, set its default binding
for (i=0; i < lengthof( g_bindings ); i++)
{
if (Q_stricmp(cvar, g_bindings[i].command) == 0) {
b1 = g_bindings[i].bind1;
if (b1 == -1) {
break;
}
DC->keynumToStringBuf( b1, g_nameBind1, 32 );
Q_strupr(g_nameBind1);
b2 = g_bindings[i].bind2;
if (b2 != -1)
{
DC->keynumToStringBuf( b2, g_nameBind2, 32 );
Q_strupr(g_nameBind2);
strcat( g_nameBind1, " or " );
strcat( g_nameBind1, g_nameBind2 );
}
return;
}
}
strcpy(g_nameBind1, "???");
}
void Item_PaintModel( qhandle_t model, qhandle_t shader,
float fovx, float fovy, float angle,
const rectDef_t *rc )
{
float x, y, w, h;
refdef_t refdef;
refEntity_t ent;
vec3_t mins, maxs, origin;
vec3_t angles;
float mW, mH, mD, l;
// setup the refdef
memset( &refdef, 0, sizeof( refdef ) );
refdef.rdflags = RDF_NOWORLDMODEL;
AxisClear( refdef.viewaxis );
x = rc->x;
y = rc->y;
w = rc->w;
h = rc->h;
AdjustFrom640( &x, &y, &w, &h );
refdef.x = (int)x;
refdef.y = (int)y;
refdef.width = (int)w;
refdef.height = (int)h;
DC->modelBounds( model, mins, maxs );
mW = fabsf( maxs[0] - mins[0] );
mD = fabsf( maxs[1] - mins[1] );
mH = fabsf( maxs[2] - mins[2] );
if ( mW > mD )
{
if ( mW > mH )
l = mW;
else
l = mH;
} else
{
if ( mD > mH )
l = mD;
else
l = mH;
}
origin[2] = -0.5f * (mins[2] + maxs[2]);
origin[1] = 0.5f * (mins[1] + maxs[1]);
// calculate distance so the model nearly fills the box
origin[0] = l*0.7f / tanf( 45.0f * 0.5f );
refdef.fov_x = 45.0f;
refdef.fov_y = 45.0f;
DC->clearScene();
refdef.time = DC->realTime;
// add the model
memset( &ent, 0, sizeof(ent) );
VectorSet( angles, 0, angle, 0 );
AnglesToAxis( angles, ent.axis );
ent.hModel = model;
ent.customShader = shader;
VectorCopy( origin, ent.origin );
VectorCopy( origin, ent.lightingOrigin );
ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
VectorCopy( ent.origin, ent.oldorigin );
DC->addRefEntityToScene( &ent );
DC->renderScene( &refdef );
}
void Item_Slider_Paint(itemDef_t *item) {
float x;
editFieldDef_t* editDef = item->typeData;
float value, range;
// draw label
if (item->text) {
Item_Text_Paint(item);
}
if ( editDef == NULL && item->cvar) {
value = 0.0f;
range = 0.0f;
} else
{
value = min( editDef->maxVal, max( editDef->minVal, DC->getCVarValue(item->cvar) ) );
range = (value - editDef->minVal) / (editDef->maxVal-editDef->minVal);
}
if ( item->window.name ) {
trap_SQL_Prepare ( "UPDATE feeders SET sel=? SEARCH name $;" );
trap_SQL_BindInt ( 1, (int)value );
trap_SQL_BindText ( 2, item->window.name );
trap_SQL_Step();
trap_SQL_Done();
}
Item_Text_GetDataRect( item, &item->textRect, (item->text)?8.0f:0.0f );
// draw slider background
DC->fillRect(
item->textRect.x, item->textRect.y + 6.0F,
item->textRect.w, item->textRect.h - 12.0F,
item->window.backColor, DC->Assets.menu );
// draw + - buttons
trap_R_RenderText( &item->textRect, item->textscale, item->window.foreColor, "<<", -1, ITEM_ALIGN_LEFT, ITEM_ALIGN_CENTER, item->textStyle, item->font, -1, 0 );
trap_R_RenderText( &item->textRect, item->textscale, item->window.foreColor, ">>", -1, ITEM_ALIGN_RIGHT, ITEM_ALIGN_CENTER, item->textStyle, item->font, -1, 0 );
// draw thumb
x = Item_Slider_ThumbPosition( item );
DC->setColor( g_color_table[ ColorIndex( COLOR_WHITE ) ] );
drawStretchPic( x, item->textRect.y + 2.0f, SLIDER_THUMB_WIDTH, item->textRect.h - 4.0f,
0, 0, 1, 1, DC->Assets.sliderThumb );
// draw value
if ( editDef )
{
rectDef_t r;
char * t;
r.x = x-16.0f;
r.y = item->textRect.y;
r.h = item->textRect.h;
r.w = SLIDER_THUMB_WIDTH + 32.0f;
if ( editDef->flags&EDITFIELD_PERCENT )
t = va( "%d", (int)(range*100.0f) );
else if ( editDef->flags&EDITFIELD_MONEY )
t = fn( (int)value, FN_CURRENCY | FN_SHORT );
else if ( editDef->flags&EDITFIELD_INTEGER )
t = fn( (int)value, FN_NORMAL );
else
t = va( "%.2f", value );
trap_R_RenderText( &r, item->textscale*0.75f, item->window.foreColor, t, -1, ITEM_ALIGN_CENTER, ITEM_ALIGN_CENTER, 3, item->font, -1, 0 );
}
}
void Item_Bind_Paint(itemDef_t *item)
{
if ( item->text )
{
Item_Text_Paint(item);
BindingFromName(item->cvar);
Item_Text_PaintMore( item, 8, g_nameBind1, item->window.foreColor );
}
}
qboolean Display_KeyBindPending( void )
{
return g_waitingForKey;
}
qboolean Item_Bind_HandleKey(itemDef_t *item, int key ) {
int id;
int i;
if ( g_waitingForKey )
{
if (!g_waitingForKey || g_bindItem == NULL) {
return qtrue;
}
if (key & K_CHAR_FLAG) {
return qtrue;
}
switch (key)
{
case K_ESCAPE:
DC->setCVar( "ui_waitingForKey", "0" );
g_waitingForKey = qfalse;
return qtrue;
case '`':
return qtrue;
}
}
if (key != -1)
{
for (i=0; i < lengthof( g_bindings ); i++)
{
if (g_bindings[i].bind2 == key) {
g_bindings[i].bind2 = -1;
}
if (g_bindings[i].bind1 == key)
{
g_bindings[i].bind1 = g_bindings[i].bind2;
g_bindings[i].bind2 = -1;
}
}
}
id = BindingIDFromName(item->cvar);
if (id != -1) {
if (key == -1) {
if( g_bindings[id].bind1 != -1 ) {
DC->setBinding( g_bindings[id].bind1, "" );
g_bindings[id].bind1 = -1;
}
if( g_bindings[id].bind2 != -1 ) {
DC->setBinding( g_bindings[id].bind2, "" );
g_bindings[id].bind2 = -1;
}
}
else if (g_bindings[id].bind1 == -1) {
g_bindings[id].bind1 = key;
}
else if (g_bindings[id].bind1 != key && g_bindings[id].bind2 == -1) {
g_bindings[id].bind2 = key;
}
else {
DC->setBinding( g_bindings[id].bind1, "" );
DC->setBinding( g_bindings[id].bind2, "" );
g_bindings[id].bind1 = key;
g_bindings[id].bind2 = -1;
}
}
Controls_SetConfig(qtrue);
DC->setCVar( "ui_waitingForKey", "0" );
g_waitingForKey = qfalse;
return qtrue;
}
void AdjustFrom640(float *x, float *y, float *w, float *h) {
//*x = *x * DC->scale + DC->bias;
*x = *x * DC->glconfig.xscale + DC->glconfig.xbias;
*y *= DC->glconfig.yscale;
*w *= DC->glconfig.xscale;
*h *= DC->glconfig.yscale;
}
void Item_Model_Paint( itemDef_t *item )
{
modelDef_t *md = (modelDef_t*)item->typeData;
// use item storage to track
if( md->rotationSpeed )
{
if( DC->realTime > item->window.nextTime )
{
item->window.nextTime = DC->realTime + md->rotationSpeed;
md->angle = (int)(md->angle + 1) % 360;
}
}
Item_PaintModel( item->asset, item->assetShader, md->fov_x,
md->fov_y, md->angle, &item->window.rect );
}
void Item_Image_Paint(itemDef_t *item) {
if (item == NULL) {
return;
}
DC->drawHandlePic(item->window.rect.x+1, item->window.rect.y+1, item->window.rect.w-2, item->window.rect.h-2, item->asset);
}
void Item_ListBox_ValidateScroll( itemDef_t *item, int rows, int count )
{
listBoxDef_t *lp = (listBoxDef_t*)item->typeData;
int scroll = max( 0, count-rows );
if( lp->startPos < 0 )
lp->startPos = 0;
if( lp->startPos > scroll )
lp->startPos = scroll;
}
void Item_TableCell_Rect( itemDef_t * item, int x, int y, qboolean scrollBar, rectDef_t * r )
{
listBoxDef_t * listPtr = (listBoxDef_t*)item->typeData;
float oy = 0.0f; //fmodf( (item->window.rect.h - listPtr->titlebar), listPtr->elementHeight ) * 0.5f;
if ( listPtr->flags & LISTBOX_ROTATE ) {
if ( x == -1 ) {
r->y = item->window.rect.y + listPtr->titlebar;
r->h = item->window.rect.h - listPtr->titlebar;
} else {
columnInfo_t * colInfo = listPtr->columnInfo + x;
r->y = item->window.rect.y + colInfo->pos;
r->h = colInfo->width;
}
if ( y == -1 ) {
r->x = item->window.rect.x;
r->w = item->window.rect.w;
} else {
r->x = item->window.rect.x + (y * listPtr->elementHeight );
r->w = listPtr->elementHeight;
}
return;
}
if ( x == -1 )
{
r->x = item->window.rect.x;
r->w = item->window.rect.w - ((scrollBar)?SCROLLBAR_SIZE+4.0f:0.0f);
} else
{
columnInfo_t * colInfo = listPtr->columnInfo + x;
r->x = item->window.rect.x + colInfo->pos + item->window.borderSize*0.5f;
r->w = colInfo->width - item->window.borderSize;
if ( x == 0 ) // first column
{
r->x += item->window.borderSize;
r->w -= item->window.borderSize;
}
if ( x == listPtr->numColumns-1 ) // last column
{
float edge = item->window.rect.x + item->window.rect.w - (((scrollBar)?SCROLLBAR_SIZE+6.0f:0.0f)+ item->window.borderSize);
if ( r->x + r->w > edge ) {
r->w = edge-r->x;
}
}
}
if ( y == -1 )
{
r->y = oy+item->window.rect.y + listPtr->titlebar;// + item->window.borderSize;
r->h = item->window.rect.h - listPtr->titlebar;// - item->window.borderSize*2.0f;
} else
{
r->y = oy+item->window.rect.y + listPtr->titlebar + (y * listPtr->elementHeight );
r->h = listPtr->elementHeight;
}
}
void Item_TableCellText_PaintColor( itemDef_t * item, rectDef_t * cell, int column, const char * text, vec4_t color, float scale )
{
listBoxDef_t * listPtr = (listBoxDef_t*)item->typeData;
columnInfo_t * colInfo = listPtr->columnInfo + column;
if ( column < listPtr->numColumns )
{
trap_R_SetColor ( item->window.backColor );
trap_R_RenderText ( cell,
((colInfo->textscale>0.0f)?colInfo->textscale:item->textscale) * scale,
color,
text,
-1,
colInfo->horizAlign | colInfo->nowrap,
colInfo->vertAlign,
(colInfo->textStyle)?colInfo->textStyle:item->textStyle,
(colInfo->font)?colInfo->font:item->font,
-1,
0
);
}
}
void Item_TableCellText_Paint( itemDef_t * item, rectDef_t * cell, int column, const char * text )
{
Item_TableCellText_PaintColor( item, cell, column, text, item->window.foreColor, 1.0f );
}
static void Item_ListBox_Paint_Empty( itemDef_t * item, listBoxDef_t * listPtr )
{
const char * text = item->text;
if ( item->ownerText ) {
text = DC->getOwnerText( item->ownerText );
}
if ( text && text[ 0 ] )
{
trap_R_RenderText( &item->window.rect,
item->textscale,
item->window.foreColor,
text,
-1,
TEXT_ALIGN_CENTER,
TEXT_ALIGN_CENTER,
item->textStyle,
item->font,
-1,
0 );
}
if ( listPtr->selectButton )
listPtr->selectButton->window.flags &= ~WINDOW_VISIBLE;
}
static void Item_ListBox_Paint_ScrollBar( itemDef_t * item, listBoxDef_t * listPtr, int rows, int count )
{
rectDef_t b, t, ti;
Item_ListBox_GetScrollBar( item, listPtr, &b, &t, rows, count );
/*
If we need to switch between small/med/tall images
this is the spot to check the aspect ratio on t.
*/
ti.x = t.x;
ti.w = t.w;
ti.h = t.w * 2.0F;
if( ti.h > t.h )
ti.h = t.h;
ti.y = (t.y + t.y + t.h - ti.h) * 0.5F;
DC->fillRect( b.x, b.y, b.w, b.h, item->window.backColor, DC->Assets.menu ); // draw bar
DC->fillRect( t.x, t.y, t.w, t.h, item->window.outlineColor, DC->Assets.menu ); // draw thumb
DC->drawHandlePic( ti.x, ti.y, ti.w, ti.h, DC->Assets.sbThumb ); // draw arrows
}
static void Item_ListBox_Paint_Grid( itemDef_t * item, listBoxDef_t * listPtr, int rows, int scrollBar )
{
int i;
rectDef_t r,a,b;
DC->setColor( item->window.color );
// columns
if ( !(listPtr->flags & LISTBOX_NOVERTLINES) ) {
for ( i=1; i<listPtr->numColumns; i++ )
{
Item_TableCell_Rect( item, i-1, -1, scrollBar, &a );
Item_TableCell_Rect( item, i, -1, scrollBar, &b );
r.x = (a.x + a.w + b.x - 1.5f) * 0.5f;
r.w = 1.5f;
r.y = a.y + 6.0f - listPtr->titlebar;
r.h = a.h - 12.0f + listPtr->titlebar;
AdjustFrom640( &r.x, &r.y, &r.w, &r.h );
DC->drawStretchPic( r.x, r.y, r.w, r.h, 0.0f, 0.0f, 1.0f, 1.0f, DC->whiteShader );
}
}
if ( listPtr->titlebar > 0 )
{
Item_TableCell_Rect( item, -1, 0, scrollBar, &r );
r.x += 2.0f;
r.w -= 4.0f;
r.y = item->window.rect.y + listPtr->titlebar - (1.5f)*0.5f;
r.h = 1.5f;
AdjustFrom640( &r.x, &r.y, &r.w, &r.h );
DC->drawStretchPic( r.x, r.y, r.w, r.h, 0.0f, 0.0f, 1.0f, 1.0f, DC->whiteShader );
}
if ( !(listPtr->flags & LISTBOX_NOHORIZLINES) ) {
for ( i=1; i<rows; i++ )
{
Item_TableCell_Rect( item, -1, i-1, scrollBar, &a );
Item_TableCell_Rect( item, -1, i, scrollBar, &b );
r.x = a.x+2.0f;
r.y = (a.y + a.h + b.y - 1.5f) * 0.5f;
r.w = a.w-4.0f;
r.h = 1.5f;
AdjustFrom640( &r.x, &r.y, &r.w, &r.h );
DC->drawStretchPic( r.x, r.y, r.w, r.h, 0.0f, 0.0f, 1.0f, 1.0f, DC->whiteShader );
}
}
}
float Item_TableSelection_GetFade( itemDef_t * item, int i, int cursel )
{
listBoxDef_t * listPtr = (listBoxDef_t*)item->typeData;
if ( listPtr->flags & LISTBOX_NOTSELECTABLE )
return 0.0f;
else if ( i == cursel )
return listPtr->fade;
else if ( listPtr->fade < 1.0f && i == listPtr->lastSel )
return 1.0f - listPtr->fade;
else
return 0.0f;
}
static void SCR_FillBar( float x, float y, float w, float h, float by, float bh, qhandle_t shader ) {
float rx,ry;
float cw,ch;
float y1,y2;
float tx,ty;
x -= cl_cornersize.value*0.25f;
y -= cl_cornersize.value*0.25f;
w += cl_cornersize.value*0.5f;
h += cl_cornersize.value*0.5f;
x = x*DC->glconfig.xscale + DC->glconfig.xbias;
y *= DC->glconfig.yscale;
w *= DC->glconfig.xscale;
h *= DC->glconfig.yscale;
by *= DC->glconfig.yscale;
bh *= DC->glconfig.yscale;
rx = cl_cornersize.value * DC->glconfig.xscale;
ry = cl_cornersize.value * DC->glconfig.yscale;
// find corner size
if ( rx * 2.0f >= w )
rx = w*0.5f;
if ( ry * 2.0f >= h )
ry = h*0.5f;
if ( rx < ry )
ry = rx;
if ( ry < rx )
rx = ry;
cw = w-rx*2.0f;
ch = h-ry*2.0f;
tx = (rx+cw)/rx;
ty = (ry+ch)/ry;
y1 = ((by-y)/(ry+ch));
y2 = (((by+bh)-y)/(ry+ch));
// corners
if ( y1 < 1.0f && y2 < 1.0f ) {
DC->drawStretchPic( x, by, rx+cw, bh, 0.0f, y1*ty, tx, y2*ty, shader );
DC->drawStretchPic( x+w-rx, by, rx, bh, 1.0f, y1*ty, 0.0f, y2*ty, shader );
} else {
if ( y1 < 1.0f ) {
DC->drawStretchPic( x, by, rx+cw, y+ry+ch-by, 0.0f, y1*ty, tx, ty, shader );
DC->drawStretchPic( x+w-rx, by, rx, y+ry+ch-by, 1.0f, y1*ty, 0.0f,ty, shader );
bh = (by+bh)-(y+ry+ch);
by = y+ry+ch;
}
y1 = 1.0f - (((by-y)-(ry+ch))/ry);
y2 = 1.0f - ((((by+bh)-y)-(ry+ch))/ry);
DC->drawStretchPic( x, by, rx+cw, bh, 0.0f, y1, tx, y2, shader );
DC->drawStretchPic( x+w-rx, by, rx, bh, 1.0f, y1, 0.0f, y2, shader );
}
//DC->drawStretchPic( x, y, rx+cw, ry+ch, 0.0f, 0.0f, (rx+cw)/rx, (ry+ch)/ry, shader );
//DC->drawStretchPic( x, y+h-ry, rx+cw, ry, 0.0f, 1.0f, (rx+cw)/rx, 0.0f, shader );
//DC->drawStretchPic( x+w-rx, y, rx, ry+ch, 1.0f, 0.0f, 0.0f, (ry+ch)/ry, shader );
//DC->drawStretchPic( x+w-rx, y+h-ry, rx, ry, 1.0f, 1.0f, 0.0f, 0.0f, shader );
}
static void Item_ListBox_Paint_Selection( itemDef_t * item, listBoxDef_t * listPtr, int rows, int scrollBar, int sel, float fade )
{
vec4_t color;
int i;
rectDef_t r;
color[0] = item->window.color[0];
color[1] = item->window.color[1];
color[2] = item->window.color[2];
color[3] = item->window.color[3]*0.75f;
// Fade in new selection
i = sel - listPtr->startPos;
if ( i >= 0 && i < rows )
{
color[ 3 ] *= fade;
Item_TableCell_Rect ( item, -1, i, scrollBar, &r );
//r.x += 2.0f;
//r.w -= 4.0f;
DC->setColor( color );
//DC->drawHandlePic( r.x, r.y, r.w, r.h, (listPtr->selectshader)?listPtr->selectshader:DC->whiteShader );
SCR_FillBar( r.x, item->window.rect.y, r.w, item->window.rect.h, r.y, r.h, (listPtr->selectshader)?listPtr->selectshader:DC->Assets.menubar );
if ( listPtr->selectButton )
{
if ( listPtr->flags & LISTBOX_ROTATE ) {
listPtr->selectButton->window.rect.y = r.y + r.h - (listPtr->selectButton->window.rect.h + 4.0f);
listPtr->selectButton->window.rect.x = r.x + ((r.w - listPtr->selectButton->window.rect.w) * 0.5f);
} else {
listPtr->selectButton->window.rect.x = r.x + r.w - (listPtr->selectButton->window.rect.w + 4.0f);
listPtr->selectButton->window.rect.y = r.y + ((r.h - listPtr->selectButton->window.rect.h) * 0.5f);
}
listPtr->selectButton->window.flags |= WINDOW_VISIBLE;
}
} else if ( listPtr->selectButton )
{
listPtr->selectButton->window.flags &= ~WINDOW_VISIBLE;
}
// Fade out old selection
if ( fade < 1.0f )
{
i = listPtr->lastSel - listPtr->startPos;
if ( i >=0 && i < rows )
{
color[ 3 ] *= (1.0f - fade);
Item_TableCell_Rect ( item, -1, i, scrollBar, &r );
//r.x += 2.0f;
//r.w -= 4.0f;
DC->setColor( color );
//DC->drawHandlePic( r.x, r.y, r.w, r.h, (listPtr->selectshader)?listPtr->selectshader:DC->whiteShader );
SCR_FillBar( r.x, item->window.rect.y, r.w, item->window.rect.h, r.y, r.h, (listPtr->selectshader)?listPtr->selectshader:DC->Assets.menubar );
}
} else
listPtr->lastSel = sel;
}
static void Item_ListBox_Paint_TitleBar( itemDef_t * item, listBoxDef_t * listPtr, int scrollBar, int rows )
{
int i;
for ( i=0; i<listPtr->numColumns; i++ )
{
const columnInfo_t * colInfo = listPtr->columnInfo + i;
const char * text = colInfo->text;
if ( colInfo->ownerText ) {
text = DC->getOwnerText( colInfo->ownerText );
}
if ( text && listPtr->titlebar > 0 )
{
rectDef_t r;
Item_TableCell_Rect ( item, i,0, scrollBar, &r );
r.y = item->window.rect.y;
r.h = listPtr->titlebar;
trap_R_RenderText ( &r,
item->textscale,
item->window.foreColor,
text,
-1,
colInfo->horizAlign,
ITEM_ALIGN_CENTER,
item->textStyle | ITEM_TEXTSTYLE_ITALIC,
item->font,
-1,0 );
}
if ( colInfo->footer )
{
rectDef_t r;
const char * text = colInfo->footer;
if ( sql( "SELECT index FROM strings SEARCH name $;", "t", text ) ) {
int index = sqlint(0);
sqldone();
text = DC->getOwnerText( index );
}
Item_TableCell_Rect ( item, i,rows, scrollBar, &r );
trap_R_RenderText ( &r,
item->textscale,
item->window.foreColor,
text,
-1,
colInfo->horizAlign,
ITEM_ALIGN_CENTER,
item->textStyle,
item->font,
-1,0 );
}
}
}
static int Item_ListBox_UpdateSelection( itemDef_t * item, listBoxDef_t * listPtr ) {
int sel;
cell_t * cells = (cell_t*)listPtr->buffer;
if ( listPtr->flags & LISTBOX_NOTSELECTABLE ) {
return -1;
}
trap_SQL_Prepare ( "SELECT sel FROM feeders SEARCH name $;" );
trap_SQL_BindText ( 1, item->window.name );
if ( trap_SQL_Step() ) {
sel = trap_SQL_ColumnAsInt(0);
trap_SQL_Done();
} else
return -1;
if ( sel >= listPtr->numRows ) {
sel = listPtr->numRows-1;
}
trap_SQL_Prepare ( "UPDATE feeders SET sel=?1,id=?2,enabled=?3 SEARCH name $4 WHERE (sel!=?1) OR (id!=?2);" );
trap_SQL_BindInt ( 1, sel );
if ( sel == -1 ) {
trap_SQL_BindInt ( 2, -1 );
trap_SQL_BindInt ( 3, -1 );
} else {
trap_SQL_BindInt ( 2, cells[ sel * (listPtr->numColumns+2) ].integer );
trap_SQL_BindInt ( 3, cells[ sel * (listPtr->numColumns+2)+1 ].integer );
}
trap_SQL_BindText ( 4, item->window.name );
trap_SQL_Step ();
if ( trap_SQL_Done() && listPtr->select && listPtr->flags&LISTBOX_MONITORTABLE ) {
Item_RunScript( item, listPtr->select );
}
return sel;
}
static int Item_Chat_NumRows( const char *chatText, itemDef_t * item )
{
fontInfo_t font;
rectDef_t r;
listBoxDef_t * listPtr = (listBoxDef_t*)item->typeData;
float lineHeight;
trap_R_GetFont ( item->font, item->textscale, &font );
lineHeight = (font.glyphs[ 'A' ].height * font.glyphScale * item->textscale) * 1.4f;
listPtr->elementHeight = lineHeight;
Item_TableCell_Rect( item, 0, -1, qtrue, &r );
return trap_R_FlowText( &font, item->textscale * font.glyphScale, item->window.foreColor, item->window.foreColor, chatText, -1, item->textalignx, r.w, NULL, -1 );
}
static void Item_Chat_Draw( const char *chatText, itemDef_t * item, int start, int count )
{
rectDef_t r;
Item_TableCell_Rect( item, 0, -1, qtrue, &r );
trap_R_RenderText( &r, item->textscale, item->window.foreColor, chatText, -1, start, count, item->textStyle | ITEM_TEXTSTYLE_MULTILINE, item->font, -1, 0 );
}
static void drawclient( rectDef_t * r, char * model, int spin )
{
playerInfo_t info;
vec3_t viewangles;
vec3_t moveangles;
memset( &info, 0, sizeof(playerInfo_t) );
viewangles[YAW] = (spin)?((DC->realTime%3600) * 0.1f):(180.0f - 10.0f);
viewangles[PITCH] = 0.0f;
viewangles[ROLL] = 0.0f;
VectorClear( moveangles );
UI_PlayerInfo_SetModel( &info, model, "", 0 );
UI_PlayerInfo_SetInfo( &info, LEGS_IDLE, LEGS_IDLE, viewangles, vec3_origin, WP_PISTOL, qfalse );
UI_DrawPlayer( r, &info, DC->realTime, 1.0f );
}
static char chatbuf[1024 * 4];
void Item_ListBox_Paint( itemDef_t *item )
{
int rows, count, i,j;
qboolean scrollBar = qfalse;
int view;
int sel = -1;
int last_rowcount;
menuDef_t * menu;
listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData;
cell_t * cells;
last_rowcount = listPtr->numRows;
if( listPtr->flags & LISTBOX_CHATLOG )
{
trap_Con_GetText( chatbuf, sizeof( chatbuf ), 1 );
}
if ( listPtr->flags & LISTBOX_CHATLOG ) {
listPtr->numRows = Item_Chat_NumRows( chatbuf, item );
} else {
if ( !listPtr->source )
return;
if ( !listPtr->buffer ) {
if ( !listPtr->buffer_size ) {
listPtr->buffer_size = 1024;
}
listPtr->buffer = UI_Alloc( listPtr->buffer_size );
}
if ( listPtr->flags&LISTBOX_SERVER ) {
listPtr->numRows = trap_SQL_SelectFromServer( listPtr->source, listPtr->buffer, listPtr->buffer_size );
} else {
listPtr->numRows = trap_SQL_Select( listPtr->source, listPtr->buffer, listPtr->buffer_size );
}
}
cells = (cell_t*)listPtr->buffer;
count = listPtr->numRows;
sel = Item_ListBox_UpdateSelection( item, listPtr );
if ( count <= 0 )
{
Item_ListBox_Paint_Empty( item, listPtr );
return;
}
rows = Item_ListBox_NumRows( item, listPtr ); // number of rows that can be seen
Item_ListBox_ValidateScroll( item, rows, count );
if ( listPtr->flags & LISTBOX_AUTOSCROLL ) {
if ( listPtr->startPos + rows == (last_rowcount-1) ) {
listPtr->startPos = (listPtr->numRows-1) - rows;
}
}
// Scroll Bar
if( count > rows )
{
item->window.flags |= WINDOW_HASSCROLLBAR;
if ( !(listPtr->flags & LISTBOX_NOSCROLLBAR) ) {
Item_ListBox_Paint_ScrollBar( item, listPtr, rows, count );
}
scrollBar = qtrue;
} else {
item->window.flags &= ~WINDOW_HASSCROLLBAR;
}
// grid
if ( !(listPtr->flags & LISTBOX_NOGRIDLINES) )
Item_ListBox_Paint_Grid( item, listPtr, min( rows, count+1 ), scrollBar );
// selection
if ( !(listPtr->flags & LISTBOX_NOTSELECTABLE) && !(listPtr->flags & LISTBOX_DONTDRAWSELECT) )
{
listPtr->fade = min( 1.0f, (DC->realTime - listPtr->timeSel)* ( 1.0f / (float)(ITEM_FADE_TIME) ) );
Item_ListBox_Paint_Selection( item, listPtr, rows, scrollBar, sel, listPtr->fade );
} else
{
listPtr->fade = 0.0f;
}
// title bar
Item_ListBox_Paint_TitleBar( item, listPtr, scrollBar, rows );
view = min( rows, count-listPtr->startPos );
if ( listPtr->flags & LISTBOX_CHATLOG ) {
Item_Chat_Draw( chatbuf, item, listPtr->startPos, view );
return;
}
menu = ((menuDef_t*)item->parent);
for ( i=0; i<view; i++ )
{
cell_t * row = &cells[ (listPtr->startPos+i)*(listPtr->numColumns+2) ];
float offset;
float scale;
float * color;
if ( listPtr->flags & LISTBOX_NOSCALESELECT ) {
offset = 0.0f;
scale = 1.0f;
} else if ( listPtr->flags & LISTBOX_SCALEENABLED && row[ 1 ].integer ) {
offset = -0.15f;
scale = 1.15f;
} else {
offset = Item_TableSelection_GetFade( item, listPtr->startPos + i, sel ) * 0.15f;
scale = 1.0f + offset;
}
if ( listPtr->flags & LISTBOX_SELECTENABLED ) {
if ( row[ 1 ].integer ) {
vec4_t color;
rectDef_t r;
color[0] = item->window.color[0];
color[1] = item->window.color[1];
color[2] = item->window.color[2];
color[3] = item->window.color[3]*0.75f;
Item_TableCell_Rect( item, -1,i, scrollBar, &r );
r.x += 2.0f;
r.w -= 4.0f;
DC->setColor( color );
DC->drawHandlePic( r.x, r.y, r.w, r.h, DC->whiteShader );
}
color = item->window.foreColor;
} else {
color = ( row[ 1 ].integer == 0 )?menu->disableColor:item->window.foreColor;
}
for ( j=0; j<listPtr->numColumns; j++ )
{
rectDef_t r;
columnInfo_t * c = listPtr->columnInfo + j;
cell_t * cell = &row[ j + 2 ];
Item_TableCell_Rect( item, j,i, scrollBar, &r );
r.x += c->border;
r.w -= c->border*2.0f;
r.y += c->border;
r.h -= c->border*2.0f;
if( c->flags & (COL_ISSHADER|COL_ISEFFECT|COL_ISSPRITE) )
{
float w = r.w;
float h = r.h;
if ( c->flags & COL_SQUARE ) {
if ( r.w > r.h ) {
r.w = r.h;
} else if ( r.h > r.w ) {
r.h = r.w;
}
}
switch ( c->horizAlign ) {
case ITEM_ALIGN_CENTER: r.x += (w-r.w)*0.5f; break;
case ITEM_ALIGN_RIGHT: r.x += (w-r.w); break;
}
switch ( c->vertAlign ) {
case ITEM_ALIGN_CENTER: r.y += (h-r.h)*0.5f; break;
case ITEM_ALIGN_RIGHT: r.y += (h-r.h); break;
}
if ( c->flags & COL_ISEFFECT ) {
UI_Effect_SetRect( cell->integer, &r, item );
} else {
qhandle_t s = -1;
if ( c->flags&COL_ISSPRITE && cell->integer >= 0 && cell->integer < lengthof(c->sprites) ) {
s = c->sprites[ cell->integer ];
} else
s = cell->integer;
if ( s >= 0 ) {
DC->setColor( color );
DC->drawHandlePic( r.x-r.h*offset*0.5f, r.y-r.h*offset*0.5f, r.h*scale, r.h*scale, s );
}
}
} else if ( c->flags & COL_ISCOUNTER ) {
int v = cell->integer;
float t = (float)(DC->realTime - menu->time)/2000.0f;
if ( t < 1.0f ) {
v = (int)( (float)v * t );
}
Item_TableCellText_PaintColor( item, &r, j, fn( v, c->format ), color, scale );
} else if ( c->flags & COL_ISCLIENT ) {
drawclient( &r, cell->string, (sel - listPtr->startPos) == i );
} else {
Item_TableCellText_PaintColor( item, &r, j, cell->string, color, scale );
}
}
}
}
void Item_OwnerDraw_Paint( itemDef_t *item )
{
menuDef_t *parent;
if( !item )
return;
parent = (menuDef_t*)item->parent;
if( DC->ownerDrawItem )
{
vec4_t color;
menuDef_t *parent = (menuDef_t*)item->parent;
Fade( &item->window.flags, &item->window.foreColor[3], parent->fadeClamp, &item->window.nextTime, parent->fadeCycle, qtrue, parent->fadeAmount );
memcpy( &color, &item->window.foreColor, sizeof( color ) );
if( Item_HasFocus( item ) )
{
Item_Color_Pulse( color, parent->focusColor );
}
else if( item->textStyle & ITEM_TEXTSTYLE_BLINK && !((DC->realTime / BLINK_DIVISOR) & 1) )
{
Item_Color_Pulse( color, item->window.foreColor );
}
if( item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar( item, CVAR_ENABLE ) )
{
memcpy( color, parent->disableColor, sizeof( vec4_t ) ); // bk001207 - FIXME: Com_Memcpy
}
if( item->text )
{
Item_Text_Paint( item );
}
DC->ownerDrawItem( item, color );
}
}
#ifdef DEVELOPER
void DebugMode_PaintWindowSize( const char * name, rectDef_t * r, rectDef_t * parent, int s )
{
const float b = 1.0f;
DC->drawRect( r->x,r->y,r->w,r->h, b, ( s==SCROLL_DEBUGMODE_MOVE )?colorGreen:((parent)?colorRed:colorYellow) );
DC->setColor( colorGreen );
if ( s & SCROLL_DEBUGMODE_LEFT )
DC->drawHandlePic( r->x, r->y, b, r->h, DC->whiteShader );
if ( s & SCROLL_DEBUGMODE_RIGHT )
DC->drawHandlePic( r->x + r->w - b, r->y, 1.0f, r->h, DC->whiteShader );
if ( s & SCROLL_DEBUGMODE_TOP )
DC->drawHandlePic( r->x, r->y, r->w, b, DC->whiteShader );
if ( s & SCROLL_DEBUGMODE_BOTTOM )
DC->drawHandlePic( r->x, r->y+r->h-1.0f, r->w, b, DC->whiteShader );
DC->setColor( 0 );
if ( s )
{
rectDef_t b = *r;
int px = r->x - ((parent)?parent->x:0);
int py = r->y - ((parent)?parent->y:0);
b.x += 4.0f;
b.y += 4.0f;
b.w -= 8.0f;
b.h -= 8.0f;
trap_R_RenderText( &b, 0.18f, colorRed, va( "%d, %d", px, py ),
-1, ITEM_ALIGN_LEFT, ITEM_ALIGN_LEFT, 3, DC->Assets.font, -1, 0 );
trap_R_RenderText( &b, 0.18f, colorRed, va( "%dx%d", (int)r->w, (int)r->h ),
-1, ITEM_ALIGN_RIGHT, ITEM_ALIGN_RIGHT, 3, DC->Assets.font, -1, 0 );
if ( name ) {
trap_R_RenderText( r, 0.18f, colorYellow, name, -1, ITEM_ALIGN_CENTER, ITEM_ALIGN_CENTER, 3, DC->Assets.font, -1, 0 );
}
}
}
void Item_PaintDebug( itemDef_t * item, int i )
{
int s = 0;
if ( itemCapture == item )
s = scrollInfo.scrollKey;
else if ( !itemCapture )
s = DebugMode_GetScroll( &item->window.rect, DC->cursorx, DC->cursory );
DebugMode_PaintWindowSize( item->window.name, &item->window.rect, &((menuDef_t*)item->parent)->window.rect, s );
if ( s )
{
//trap_R_RenderText( &item->window.rect, 0.21f, colorRed, va( "%d", i ),
// -1, ITEM_ALIGN_RIGHT, ITEM_ALIGN_LEFT, 3, DC->Assets.font, -1, 0 );
}
}
#endif
void Item_Paint( itemDef_t *item )
{
menuDef_t *parent = (menuDef_t*)item->parent;
if( item == NULL )
return;
if( item->window.flags & WINDOW_ORBITING )
{
if( DC->realTime > item->window.nextTime )
{
float rx, ry, a, c, s, w, h;
item->window.nextTime = DC->realTime + item->window.offsetTime;
// translate
w = item->window.rectClient.w / 2;
h = item->window.rectClient.h / 2;
rx = item->window.rectClient.x + w - item->window.rectEffects.x;
ry = item->window.rectClient.y + h - item->window.rectEffects.y;
a = 3.0f * M_PI / 180.0f;
c = cosf(a);
s = sinf(a);
item->window.rectClient.x = (rx * c - ry * s) + item->window.rectEffects.x - w;
item->window.rectClient.y = (rx * s + ry * c) + item->window.rectEffects.y - h;
Item_UpdatePosition(item);
}
}
if( item->cvarFlags & (CVAR_SHOW | CVAR_HIDE) )
{
if( !Item_EnableShowViaCvar( item, CVAR_SHOW ) )
{
return;
}
}
if( !(item->window.flags & WINDOW_VISIBLE) )
{
return;
}
Window_Paint( &item->window, parent->focusItem == item, item->focusTime );
//
// HiLight Edit Field
//
if ( item == parent->focusItem )
{
switch( item->type )
{
case ITEM_TYPE_BIND:
case ITEM_TYPE_MULTI:
case ITEM_TYPE_YESNO:
case ITEM_TYPE_EDITFIELD:
case ITEM_TYPE_NUMERICFIELD:
{
vec4_t newColor;
if ( item->window.outlineColor[3] < 1.0f )
{
memcpy(newColor, &parent->focusColor, sizeof(vec4_t));
} else
{
Item_Color_Pulse( newColor, parent->focusColor );
}
newColor[ 3 ] *= item->window.outlineColor[3];
DC->fillRect( item->textRect.x-4.0f, item->window.rect.y, item->textRect.w+8.0f, item->window.rect.h, newColor, DC->Assets.menu );
}
}
}
if ( item->type == ITEM_TYPE_SLIDER || item->type == ITEM_TYPE_NUMERICFIELD ) {
if ( item->values ) {
if ( trap_SQL_Prepare( item->values ) ) {
editFieldDef_t* editDef = item->typeData;
trap_SQL_Step();
editDef->minVal = (float)trap_SQL_ColumnAsInt( 0 );
editDef->maxVal = (float)trap_SQL_ColumnAsInt( 1 );
editDef->defVal = (float)trap_SQL_ColumnAsInt( 2 );
trap_SQL_Done();
}
}
}
switch( item->type )
{
case ITEM_TYPE_OWNERDRAW:
Item_OwnerDraw_Paint( item );
break;
case ITEM_TYPE_TEXT:
case ITEM_TYPE_BUTTON:
Item_Text_Paint( item );
break;
case ITEM_TYPE_EDITFIELD:
case ITEM_TYPE_NUMERICFIELD:
Item_TextField_Paint( item );
break;
case ITEM_TYPE_LISTBOX:
Item_ListBox_Paint( item );
break;
/*
case ITEM_TYPE_IMAGE:
Item_Image_Paint( item );
break;
*/
case ITEM_TYPE_YESNO:
Item_YesNo_Paint( item );
break;
case ITEM_TYPE_MULTI:
Item_Multi_Paint( item );
break;
case ITEM_TYPE_BIND:
Item_Bind_Paint( item );
break;
case ITEM_TYPE_SLIDER:
Item_Slider_Paint( item );
break;
case ITEM_TYPE_PROGRESS:
Item_Progress_Paint( item );
break;
default:
break;
}
}
void Menu_Init(menuDef_t *menu)
{
memset(menu, 0, sizeof(menuDef_t));
menu->fadeAmount = DC->Assets.fadeAmount;
menu->fadeClamp = DC->Assets.fadeClamp;
menu->fadeCycle = DC->Assets.fadeCycle;
Window_Init(&menu->window);
}
itemDef_t *Menu_GetFocusedItem(menuDef_t *menu)
{
return menu->focusItem;
}
menuDef_t *Menu_GetFocused()
{
return (menuStack->count)?menuStack->m[ menuStack->count - 1 ]:0;
}
qboolean Menus_AnyFullScreenVisible() {
int i;
for ( i=0; i<menuStack->count; i++ )
{
if ( menuStack->m[ i ]->window.flags & WINDOW_VISIBLE && menuStack->m[ i ]->window.flags & WINDOW_FULLSCREEN )
return qtrue;
}
return qfalse;
}
void Menus_ActivateByName(const char *p)
{
Menus_Activate( Menus_FindByName( p ) );
}
void Item_Init(itemDef_t *item) {
memset(item, 0, sizeof(itemDef_t));
item->textscale = 0.55f;
Window_Init(&item->window);
}
void Menu_HandleMouseMove( menuDef_t *menu, float x, float y )
{
int i;
qboolean focusItem = qfalse;
if( !menu || !(menu->window.flags & (WINDOW_VISIBLE)) )
return;
if( itemCapture )
return;
if( debugMode )
return;
if( g_waitingForKey || g_editItem )
return;
// scan the items from top to bottom
for ( i=menu->itemCount-1; i>=0; i-- )
{
itemDef_t* item = menu->items[ i ];
if( !(item->window.flags & (WINDOW_VISIBLE) ) )
continue;
if ( item->window.flags & (WINDOW_DECORATION | WINDOW_FADINGOUT) )
continue;
// items can be enabled and disabled based on cvars
if( item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE)
&& !Item_EnableShowViaCvar( item, CVAR_ENABLE ) )
continue;
if( item->cvarFlags & (CVAR_SHOW | CVAR_HIDE)
&& !Item_EnableShowViaCvar( item, CVAR_SHOW ) )
continue;
// first item hit, this one get the focus, really there shouldn't be overlaping items in well designed gui's
if ( Rect_ContainsPoint( &item->window.rect, x, y ) )
{
if ( !(item->window.flags & WINDOW_MOUSEOVER) )
Item_MouseEnter( item, x, y );
if ( !focusItem )
focusItem = Menu_SetFocusItem( menu, item );
if ( item->type == ITEM_TYPE_OWNERDRAW )
Item_OwnerDraw_HandleMouseMove( item,x,y );
if ( item->window.flags & WINDOW_POPUP )
break; // don't let any controls under this one get messages
} else
{
if ( item->window.flags & WINDOW_MOUSEOVER )
Item_MouseLeave( item );
}
}
// the mouse is not over an item, lose the focus on the old item
if ( !focusItem && menu->focusItem )
{
Menu_ClearFocusItem( menu, 0 );
}
}
void Menu_Paint(menuDef_t *menu, qboolean forcePaint) {
int i;
if (menu == NULL) {
return;
}
if (!(menu->window.flags & WINDOW_VISIBLE) && !forcePaint) {
return;
}
if ( menu->showif && *menu->showif ) {
int r = 0;
trap_SQL_Prepare( menu->showif );
if ( trap_SQL_Step() ) {
r = trap_SQL_ColumnAsInt(0);
trap_SQL_Done();
}
if ( r == 0 )
return;
}
// paint the background and or border
Window_Paint( &menu->window, 0, 0 );
for (i = 0; i < menu->itemCount; i++) {
Item_Paint(menu->items[i]);
#ifdef DEVELOPER
if ( debugMode && (!debug.menu || debug.menu==menu) )
Item_PaintDebug( menu->items[ i ], i );
#endif
}
UI_Effects_Draw( menu );
#ifdef DEVELOPER
if (debugMode && (!debug.menu || debug.menu == menu) ) {
int s = 0;
if ( itemCapture == (itemDef_t*)menu )
s = scrollInfo.scrollKey;
else if ( !itemCapture )
s = DebugMode_GetScroll( &menu->window.rect, DC->cursorx, DC->cursory );
DebugMode_PaintWindowSize( NULL, &menu->window.rect, 0, s );
trap_R_RenderText( &menu->window.rect, 0.18f, colorYellow, va( "[%s] - %s", menu->window.name, menu->filename ),
-1, ITEM_ALIGN_LEFT, ITEM_ALIGN_LEFT, 3, DC->Assets.font, -1, 0 );
}
#endif
}
//
// ---------------------------------------------------------------------------
// MENU PARSING
// ---------------------------------------------------------------------------
//
/*
===============
Item_ValidateTypeData
===============
*/
void Item_ValidateTypeData(itemDef_t *item) {
if (item->typeData) {
return;
}
switch( item->type )
{
case ITEM_TYPE_LISTBOX:
{
item->typeData = UI_Alloc(sizeof(listBoxDef_t));
memset(item->typeData, 0, sizeof(listBoxDef_t));
} break;
case ITEM_TYPE_EDITFIELD:
case ITEM_TYPE_NUMERICFIELD:
case ITEM_TYPE_YESNO:
case ITEM_TYPE_BIND:
case ITEM_TYPE_SLIDER:
case ITEM_TYPE_TEXT:
{
item->typeData = UI_Alloc(sizeof(editFieldDef_t));
memset(item->typeData, 0, sizeof(editFieldDef_t));
if (item->type == ITEM_TYPE_EDITFIELD) {
if (!((editFieldDef_t *) item->typeData)->maxPaintChars) {
((editFieldDef_t *) item->typeData)->maxPaintChars = MAX_EDITFIELD;
}
}
((editFieldDef_t *) item->typeData)->minVal = -1e5f;
((editFieldDef_t *) item->typeData)->maxVal = +1e5f;
} break;
case ITEM_TYPE_MULTI:
{
item->typeData = UI_Alloc(sizeof(multiDef_t));
} break;
case ITEM_TYPE_MODEL:
{
item->typeData = UI_Alloc(sizeof(modelDef_t));
} break;
case ITEM_TYPE_PROGRESS:
{
item->typeData = UI_Alloc(sizeof(progressDef_t));
} break;
}
}
static field_t * Find_FieldRange( field_t * fields, int count, int type, int * n )
{
int i;
int s;
for ( i=0; i<count; i++ )
{
if ( fields[ i ].type == type )
{
i++;
break;
}
}
if ( i == count )
return 0;
s = i;
for ( ; i<count; i++ )
{
if ( fields[ i ].type >= FIELD_WINDOW )
break;
}
*n = i - s;
return fields + s;
}
static field_t * Find_Field( const char * name, field_t * fields, int count )
{
int i;
for ( i=0; i<count; i++ )
{
if ( !Q_stricmp( fields[ i ].name, name ) )
return fields + i;
}
return 0;
}
static qboolean Parse_Field( int handle, byte* data, field_t * field )
{
switch ( field->type )
{
case FIELD_INTEGER:
{
if (!PC_Int_Parse(handle, (int*)( data + field->offset ))) {
return qfalse;
}
} break;
case FIELD_FLOAT:
{
if (!PC_Float_Parse(handle, (float*)( data + field->offset ))) {
return qfalse;
}
} break;
case FIELD_STRING:
{
if (!PC_String_Parse(handle, (const char**)( data + field->offset ))) {
return qfalse;
}
} break;
case FIELD_COLOR:
{
int j;
for ( j=0; j<4; j++ )
{
if (!PC_Float_Parse(handle, ((float*)( data + field->offset ))+j )) {
return qfalse;
}
}
} break;
case FIELD_RECT:
{
if (!PC_Rect_Parse(handle, (rectDef_t*)( data + field->offset ))) {
return qfalse;
}
} break;
case FIELD_STATE:
{
if (!PC_String_Parse(handle, (const char**)( data + field->offset ))) {
return qfalse;
}
} break;
case FIELD_BIT:
{
*(int*)( data + field->offset ) |= field->bit;
} break;
case FIELD_FONT:
{
const char * font;
if (!PC_String_Parse(handle, &font)) {
return qfalse;
}
*(qhandle_t*)( data + field->offset ) = DC->registerFont( font );
return qtrue;
} break;
case FIELD_SHADER:
{
const char *temp;
if (!PC_String_Parse(handle, &temp)) {
return qfalse;
}
*(qhandle_t*)( data + field->offset ) = DC->registerShaderNoMip(temp);
} break;
case FIELD_SCRIPT:
{
if (!PC_Script_Parse(handle, (const char**)( data + field->offset ))) {
return qfalse;
}
} break;
case FIELD_KEY:
{
int i;
keyAction_t *keys = (keyAction_t*)( data + field->offset );
// find a spot to stick it
for( i=0; i<MAX_KEY_ACTIONS; i++ )
if( keys[ i ].key == 0 )
{
if( !PC_Int_Parse( handle, &keys[ i ].key ) )
return qfalse;
if ( !PC_Script_Parse( handle, &keys[ i ].action ) )
return qfalse;
break;
}
if ( i==MAX_KEY_ACTIONS )
return qfalse;
} break;
case FIELD_FUNC:
{
field->func( (itemDef_t*)data, handle );
} break;
default:
return qfalse;
}
return qtrue;
}
static int Field_Parse( int handle, const char * name, byte * data, field_t * fields, int count )
{
field_t * field = Find_Field( name, fields, count );
if ( field )
{
if ( !Parse_Field( handle, data, field ) )
{
PC_SourceError(handle, "couldn't parse menu item keyword %s", name);
return 2; // found, but error
}
return 1; // found
}
return 0; // not found
}
/*
===============
Column_Parse
===============
*/
qboolean Column_Parse(int handle, columnInfo_t * col) {
pc_token_t token;
field_t * columnFields;
int columnFieldsCount;
col->border = 1.0f;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (*token.string != '{') {
return qfalse;
}
columnFields = Find_FieldRange( fields, fieldCount, FIELD_COLUMNS, &columnFieldsCount );
for( ; ; )
{
if (!trap_PC_ReadToken(handle, &token)) {
PC_SourceError(handle, "end of file inside column\n");
return qfalse;
}
if (*token.string == '}') {
return qtrue;
}
if ( Field_Parse( handle, token.string, (byte*)col, columnFields, columnFieldsCount ) )
continue;
PC_SourceError(handle, "unknown column keyword %s", token.string);
break;
}
return qfalse; // bk001205 - LCC missing return value
}
/*
===============
Item Keyword Parse functions
===============
*/
// asset_model <string>
qboolean ItemParse_asset_model( itemDef_t *item, int handle ) {
const char *temp;
modelDef_t *modelPtr;
Item_ValidateTypeData(item);
modelPtr = (modelDef_t*)item->typeData;
if (!PC_String_Parse(handle, &temp)) {
return qfalse;
}
item->asset = DC->registerModel(temp);
modelPtr->angle = 90;
return qtrue;
}
// asset_shader <string>
qboolean ItemParse_asset_shader( itemDef_t *item, int handle ) {
const char *temp;
if (!PC_String_Parse(handle, &temp)) {
return qfalse;
}
item->asset = DC->registerShaderNoMip(temp);
return qtrue;
}
// model_origin <number> <number> <number>
qboolean ItemParse_model_origin( itemDef_t *item, int handle ) {
modelDef_t *modelPtr;
Item_ValidateTypeData(item);
modelPtr = (modelDef_t*)item->typeData;
if (PC_Float_Parse(handle, &modelPtr->origin[0])) {
if (PC_Float_Parse(handle, &modelPtr->origin[1])) {
if (PC_Float_Parse(handle, &modelPtr->origin[2])) {
return qtrue;
}
}
}
return qfalse;
}
// model_fovx <number>
qboolean ItemParse_model_fovx( itemDef_t *item, int handle ) {
modelDef_t *modelPtr;
Item_ValidateTypeData(item);
modelPtr = (modelDef_t*)item->typeData;
if (!PC_Float_Parse(handle, &modelPtr->fov_x)) {
return qfalse;
}
return qtrue;
}
// model_fovy <number>
qboolean ItemParse_model_fovy( itemDef_t *item, int handle ) {
modelDef_t *modelPtr;
Item_ValidateTypeData(item);
modelPtr = (modelDef_t*)item->typeData;
if (!PC_Float_Parse(handle, &modelPtr->fov_y)) {
return qfalse;
}
return qtrue;
}
// model_rotation <integer>
qboolean ItemParse_model_rotation( itemDef_t *item, int handle ) {
modelDef_t *modelPtr;
Item_ValidateTypeData(item);
modelPtr = (modelDef_t*)item->typeData;
if (!PC_Int_Parse(handle, &modelPtr->rotationSpeed)) {
return qfalse;
}
return qtrue;
}
// model_angle <integer>
qboolean ItemParse_model_angle( itemDef_t *item, int handle ) {
modelDef_t *modelPtr;
Item_ValidateTypeData(item);
modelPtr = (modelDef_t*)item->typeData;
if (!PC_Int_Parse(handle, &modelPtr->angle)) {
return qfalse;
}
return qtrue;
}
// columns sets a number of columns and an x pos and width per..
qboolean ItemParse_columnDef( itemDef_t *item, int handle )
{
listBoxDef_t * listPtr;
Item_ValidateTypeData( item );
if( !item->typeData )
return qfalse;
listPtr = (listBoxDef_t*)item->typeData;
if ( listPtr->numColumns >= MAX_LB_COLUMNS )
return qfalse;
if ( Column_Parse( handle, listPtr->columnInfo + listPtr->numColumns ) ) {
listPtr->numColumns++;
} else {
PC_SourceError(handle, "couldn't parse column in list '%s' from menu '%s'", item->window.name, ((menuDef_t*)item->parent)->window.name );
}
return qtrue;
}
qboolean ItemParse_cvarFloat( itemDef_t *item, int handle ) {
editFieldDef_t *editPtr;
Item_ValidateTypeData(item);
if (!item->typeData)
return qfalse;
editPtr = (editFieldDef_t*)item->typeData;
if (PC_String_Parse(handle, &item->cvar) &&
PC_Float_Parse(handle, &editPtr->defVal) &&
PC_Float_Parse(handle, &editPtr->minVal) &&
PC_Float_Parse(handle, &editPtr->maxVal)) {
return qtrue;
}
return qfalse;
}
qboolean ItemParse_cvarStrList( itemDef_t *item, int handle ) {
pc_token_t token;
multiDef_t *multiPtr;
int pass;
Item_ValidateTypeData(item);
if (!item->typeData)
return qfalse;
multiPtr = (multiDef_t*)item->typeData;
multiPtr->count = 0;
multiPtr->strDef = qtrue;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (*token.string != '{') {
return qfalse;
}
pass = 0;
for( ; ; )
{
if (!trap_PC_ReadToken(handle, &token)) {
PC_SourceError(handle, "end of file inside menu item\n");
return qfalse;
}
if (*token.string == '}') {
return qtrue;
}
if (*token.string == ',' || *token.string == ';') {
continue;
}
if (pass == 0) {
if ( sql( "SELECT index FROM strings SEARCH name $;", "t", token.string ) ) {
int index = sqlint(0);
sqldone();
trap_SQL_Run( token.string, sizeof(token.string), index, 0,0,0 );
}
multiPtr->cvarList[multiPtr->count] = String_Alloc(token.string);
pass = 1;
} else {
multiPtr->cvarStr[multiPtr->count] = String_Alloc(token.string);
pass = 0;
multiPtr->count++;
if (multiPtr->count >= MAX_MULTI_CVARS) {
return qfalse;
}
}
}
}
qboolean ItemParse_cvarFloatList( itemDef_t *item, int handle ) {
pc_token_t token;
multiDef_t *multiPtr;
Item_ValidateTypeData(item);
if (!item->typeData)
return qfalse;
multiPtr = (multiDef_t*)item->typeData;
multiPtr->count = 0;
multiPtr->strDef = qfalse;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (*token.string != '{') {
return qfalse;
}
for( ; ; )
{
if (!trap_PC_ReadToken(handle, &token)) {
PC_SourceError(handle, "end of file inside menu item\n");
return qfalse;
}
if (*token.string == '}') {
return qtrue;
}
if (*token.string == ',' || *token.string == ';') {
continue;
}
if ( sql( "SELECT index FROM strings SEARCH name $;", "t", token.string ) ) {
int index = sqlint(0);
sqldone();
trap_SQL_Run( token.string, sizeof(token.string), index, 0,0,0 );
}
multiPtr->cvarList[multiPtr->count] = String_Alloc(token.string);
if (!PC_Float_Parse(handle, &multiPtr->cvarValue[multiPtr->count])) {
return qfalse;
}
multiPtr->count++;
if (multiPtr->count >= MAX_MULTI_CVARS) {
return qfalse;
}
}
}
qboolean ItemParse_ownerdrawFlag( itemDef_t *item, int handle ) {
int i;
if (!PC_Int_Parse(handle, &i)) {
return qfalse;
}
item->window.ownerDrawFlags |= i;
return qtrue;
}
qboolean ItemParse_enableCvar( itemDef_t *item, int handle ) {
if (PC_Script_Parse(handle, &item->enableCvar)) {
item->cvarFlags = CVAR_ENABLE;
return qtrue;
}
return qfalse;
}
qboolean ItemParse_disableCvar( itemDef_t *item, int handle ) {
if (PC_Script_Parse(handle, &item->enableCvar)) {
item->cvarFlags = CVAR_DISABLE;
return qtrue;
}
return qfalse;
}
qboolean ItemParse_showCvar( itemDef_t *item, int handle ) {
if (PC_Script_Parse(handle, &item->enableCvar)) {
item->cvarFlags = CVAR_SHOW;
return qtrue;
}
return qfalse;
}
qboolean ItemParse_hideCvar( itemDef_t *item, int handle ) {
if (PC_Script_Parse(handle, &item->enableCvar)) {
item->cvarFlags = CVAR_HIDE;
return qtrue;
}
return qfalse;
}
qboolean ItemParse_showif( itemDef_t *item, int handle ) {
if ( PC_String_Parse(handle, &item->showif)) {
item->cvarFlags = CVAR_SHOW;
return qtrue;
}
return qfalse;
}
qboolean ItemParse_passwordCvar( itemDef_t *item, int handle ) {
item->cvarFlags = CVAR_PASSWORD;
return qtrue;
}
qboolean ItemParse_AttachToList( itemDef_t *item, int handle )
{
const char* list;
itemDef_t* listItem;
listBoxDef_t* listPtr;
if ( !PC_String_Parse( handle, &list ) )
return qfalse;
listItem = Menu_FindItemByName( item->parent, list );
if ( !listItem )
return qfalse;
listPtr = (listBoxDef_t*)listItem->typeData;
listPtr->selectButton = item;
return qtrue;
}
qboolean ItemParse_snapleft( itemDef_t *item, int handle )
{
const char* other;
if ( !PC_String_Parse( handle, &other ) )
return qfalse;
item->snapleft = Menu_FindItemByName( item->parent, other );
return qtrue;
}
qboolean ItemParse_snapright( itemDef_t *item, int handle )
{
const char* other;
if ( !PC_String_Parse( handle, &other ) )
return qfalse;
item->snapright = Menu_FindItemByName( item->parent, other );
return qtrue;
}
/*
===============
Item_Parse
===============
*/
qboolean Item_Parse(int handle, itemDef_t *item) {
pc_token_t token;
char last[ 64 ];
int i = 0;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (*token.string != '{') {
return qfalse;
}
for( ; ; )
{
byte * data = 0;
int j;
Q_strncpyz( last, (i>0)?token.string:"itemDef", sizeof(last) );
if (!trap_PC_ReadToken(handle, &token)) {
PC_SourceError(handle, "end of file inside menu item\n");
return qfalse;
}
i++;
if (*token.string == '}') {
Item_ValidateTypeData( item );
return qtrue;
}
for ( j=0; j<fieldCount; j++ )
{
Item_UpdateData( item, fields + j, &data );
if ( !data )
continue;
if ( !Q_stricmp( fields[ j ].name, token.string ) )
{
if ( !Parse_Field( handle, data, fields + j ) )
{
PC_SourceError(handle, "couldn't parse menu item keyword %s", token.string);
return qfalse;
}
break;
}
}
if ( j==fieldCount )
PC_SourceError(handle, "unknown menu item keyword %s after %s", token.string, last );
}
}
/*
===============
Menu Keyword Parse functions
===============
*/
qboolean MenuParse_ownerdrawFlag( itemDef_t *item, int handle ) {
int i;
menuDef_t *menu = (menuDef_t*)item;
if (!PC_Int_Parse(handle, &i)) {
return qfalse;
}
menu->window.ownerDrawFlags |= i;
return qtrue;
}
qboolean MenuParse_itemDef( menuDef_t * menu, int handle )
{
if ( menu->itemCount < MAX_MENUITEMS )
{
itemDef_t * item = UI_Alloc(sizeof(itemDef_t));
menu->items[ menu->itemCount ] = item;
Item_Init( item );
menu->items[menu->itemCount]->parent = menu;
if (!Item_Parse(handle, item ))
return qfalse;
menu->itemCount++;
}
return qtrue;
}
qboolean Effect_Parse(int handle, effectDef_t * effect ) {
pc_token_t token;
int i;
int s;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (*token.string != '{') {
return qfalse;
}
for ( s=0; s<fieldCount; s++ )
{
if ( fields[ s ].type == FIELD_EFFECT )
{
s++;
break;
}
}
if ( s == fieldCount )
return qfalse;
i=0;
for( ; ; )
{
int j;
if (!trap_PC_ReadToken(handle, &token)) {
PC_SourceError(handle, "end of file inside menu effect\n");
return qfalse;
}
i++;
if (*token.string == '}') {
return qtrue;
}
for ( j=s; j<fieldCount; j++ )
{
if ( fields[ j ].type >= FIELD_WINDOW )
break;
if ( !Q_stricmp( fields[ j ].name, token.string ) )
{
if ( !Parse_Field( handle, (byte*)effect, fields + j ) )
{
PC_SourceError(handle, "couldn't parse menu effect keyword %s", token.string);
return qfalse;
}
break;
}
}
if ( j==fieldCount )
PC_SourceError(handle, "unknown menu effect keyword %s", token.string );
}
}
/*
===============
Menu_Parse
===============
*/
qboolean Menu_Parse(int handle, menuDef_t *menu) {
pc_token_t token;
if (!trap_PC_ReadToken(handle, &token))
return qfalse;
if (*token.string != '{') {
return qfalse;
}
for( ; ; )
{
byte * data = 0;
int j;
memset(&token, 0, sizeof(pc_token_t));
if (!trap_PC_ReadToken(handle, &token)) {
PC_SourceError(handle, "end of file inside menu\n");
return qfalse;
}
if (*token.string == '}') {
return qtrue;
}
if ( !Q_stricmp( token.string, "itemDef" ) )
{
MenuParse_itemDef( menu, handle );
continue;
}
for ( j=0; j<fieldCount; j++ )
{
Menu_UpdateData( menu, fields + j, &data );
if ( !data )
continue;
if ( !Q_stricmp( fields[ j ].name, token.string ) )
{
if ( !Parse_Field( handle, data, fields + j ) )
{
PC_SourceError(handle, "couldn't parse menu (%s) keyword %s", menu->window.name, token.string);
return qfalse;
}
break;
}
}
if ( j == fieldCount )
PC_SourceError(handle, "(%s) unknown menu keyword %s", menu->window.name, token.string);
}
}
/*
===============
Menu_New
===============
*/
void Menu_New(int handle, const char * filename ) {
menuDef_t *menu = &Menus[menuCount];
if (menuCount < MAX_MENUS) {
Menu_Init(menu);
if (Menu_Parse(handle, menu)) {
Menu_PostParse(menu);
menu->filename = String_Alloc( filename );
menuCount++;
}
}
}
int Menu_Count() {
return menuCount;
}
void Menu_PaintAll() {
float fade;
int i;
if (captureFunc) {
captureFunc(captureData);
}
Item_RunDelayedScripts();
Menus_SetVisible();
if ( menuStack->count && !Q_stricmp(menuStack->m[ menuStack->count-1]->window.name, "editor" ) )
debugMode = 0;
//
// draw the menus that are stack from the bottom up
//
// start with the last full screen menu
for( i = menuStack->count - 1; i >= 0; i-- )
if( menuStack->m[ i ]->window.flags&(WINDOW_FULLSCREEN|WINDOW_LASTMENU) )
{
i--;
break;
}
for( i++; i < menuStack->count; i++ )
Menu_Paint( menuStack->m[i], qtrue );
fade = (DC->realTime - menuStack->openTime ) / 550.0f;
if ( menuStack->openQueueCount ) // menu in queue waiting for current menu stack to fade out
{
if ( fade > 1.0f )
{
Menus_CloseAll();
menuStack->openTime = DC->realTime;
for ( i=0; i<menuStack->openQueueCount; i++ )
Menus_Activate( menuStack->openQueue[i] );
menuStack->openQueueCount = 0;
fade = 1.0f;
}
} else
fade = 1.0f - fade;
if ( fade >= 0.0f && fade <= 1.0f )
{
float x = DC->glconfig.xbias / DC->glconfig.xscale;
vec4_t color = { 0.0f, 0.0f, 0.0f, 0.0f };
color[ 3 ] = fade;
DC->setColor( color );
DC->drawHandlePic( -x, 0.0f, 640.0f + x*2.0f, 480.0f, DC->whiteShader );
}
// draw cursor
if ( menuStack->count ) {
if ( trap_Cvar_VariableInt( "journal" ) == 2 ) {
if ( !uiInfo.uiDC.Assets.cursor ) {
uiInfo.uiDC.Assets.cursor = trap_R_RegisterShaderNoMip( "ui/assets/cursornotarget" );
}
DC->setColor( colorWhite );
DC->drawHandlePic( uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory, 32, 32, uiInfo.uiDC.Assets.cursor);
}
}
}
void Menu_Reset() {
menuCount = 0;
}
displayContextDef_t *Display_GetContext() {
return DC;
}
int Display_MouseMove(int x, int y)
{
int i;
int cursor = 0;
for ( i=menuStack->count-1; i>=0; i-- )
{
menuDef_t *menu = menuStack->m[ i ];
if ( Rect_ContainsPoint( &menu->window.rect, x,y ) ) {
Menu_HandleMouseMove( menu, x,y );
} else {
Menu_ClearFocusItem( menu, 0 );
}
if ( menu->focusItem ) {
itemDef_t * item = menu->focusItem;
if ( item->type == ITEM_TYPE_LISTBOX ) {
int i;
listBoxDef_t * listPtr = (listBoxDef_t*)item->typeData;
int count = listPtr->numRows;
for ( i=0; i<count; i++ ) {
rectDef_t r;
Item_TableCell_Rect( item, -1, i, qfalse, &r );
if ( Rect_ContainsPoint( &r, x, y ) ) {
cursor = 1;
break;
}
}
} else
cursor = 1;
}
if ( menu->window.flags & (WINDOW_FULLSCREEN|WINDOW_LASTMENU) )
return cursor;
if ( menu->window.flags & WINDOW_POPUP )
break;
}
// anything under a pop window must lose focus
for ( i=i-1; i>=0; i-- )
{
menuDef_t *menu = menuStack->m[ i ];
Menu_ClearFocusItem( menu, 0 );
}
return cursor;
}
#ifdef DEVELOPER
extern void Save_Menu( menuDef_t * menu, const char * filename );
void Save_ActiveMenus()
{
int i;
for ( i=0; i<menuStack->count; i++ )
{
if ( !(menuStack->m[ i ]->window.flags&WINDOW_DONTSAVE) ) {
Save_Menu( menuStack->m[ i ], menuStack->m[ i ]->filename );
}
}
}
#endif
void Display_HandleKey(int key, qboolean down, int x, int y)
{
int i;
if ( !down )
{
if ( (key == K_MOUSE1 || key == K_MOUSE2) ) {
if ( itemCapture ) {
Item_StopCapture( itemCapture );
itemCapture = 0;
captureFunc = 0;
captureData = 0;
} else {
// search from the top of the menu stack down...
for( i = menuStack->count - 1; i >= 0; i-- )
{
menuDef_t * menu = menuStack->m[i];
if ( Rect_ContainsPoint( &menu->window.rect, x, y ) ) {
itemDef_t * item = Menu_GetItemUnderCursor( menu, x, y );
if ( item && (item->type == ITEM_TYPE_BUTTON || item->type == ITEM_TYPE_TEXT) ) {
Item_RunScript(item, item->action);
}
}
if ( menu->window.flags & WINDOW_POPUP )
break;
}
}
}
return;
}
// search from the top of the menu stack down...
for( i = menuStack->count - 1; i >= 0; i-- )
{
menuDef_t * menu = menuStack->m[i];
if( Menu_HandleKeyDown( menu, key ) || (menu->window.flags & WINDOW_POPUP && !debugMode) ) {
//either menu consumed key or was popup which blocks all down-stack menus
break;
}
}
// global hot keys for all menus
switch ( key )
{
#ifdef DEVELOPER
case K_F11:
{
if ( debugMode )
Save_ActiveMenus();
debugMode ^= 1;
debug.menu = 0;
} break;
case K_F9:
{
debug_cgmenu = !debug_cgmenu;
} break;
case K_MWHEELDOWN:
{
if ( !debug.menu )
debug.menu = menuStack->m[ menuStack->count-1 ];
else
{
int i;
for ( i=menuStack->count-1; i>=0; i-- )
{
if( menuStack->m[ i ]->window.flags&WINDOW_FULLSCREEN )
break;
if ( debug.menu == menuStack->m[ i ] )
break;
}
i--;
debug.menu = (i<0)?0:menuStack->m[ i ];
}
} break;
case K_MWHEELUP:
{
if ( !debug.menu )
debug.menu = menuStack->m[ 0 ];
else
{
int i;
for ( i=0; i<menuStack->count; i++ )
{
if ( debug.menu == menuStack->m[ i ] )
break;
}
i++;
debug.menu = (i>=menuStack->count)?0:menuStack->m[ i ];
}
} break;
#endif
case K_F8:
{
trap_Cmd_ExecuteText( EXEC_INSERT, "screenshot" );
}break;
case K_F7:
{
if ( trap_Cvar_VariableInt( "sv_cheats" ) == 1 ) {
Menus_CloseAll();
Menus_ActivateByName( "mission_editor" );
}
} break;
case K_F3:
case K_F10:
if ( !debugMode && trap_Cvar_VariableInt( "developer" ) ) {
Menus_OpenByName( "reportbug" );
}
break;
default:
{
if( DC->getCVarValue( "cl_spacetrader" ) == 1.0F && !g_editItem )
{
int i;
char binding[32];
DC->getBindingBuf( key, binding, 32 );
for( i=0; i < lengthof(g_bindings); i++ )
{
if( Q_stricmp( g_bindings[i].command, binding ) == 0 && (g_bindings[i].flags & BIND_ALLOW_IN_UI) )
trap_Cmd_ExecuteText( EXEC_INSERT, binding );
}
}
} break;
}
}
static void Window_CacheContents(windowDef_t *window) {
if (window) {
if (window->cinematicName) {
int cin = DC->playCinematic(window->cinematicName, 0, 0, 0, 0);
DC->stopCinematic(cin);
}
}
}
static void Item_CacheContents(itemDef_t *item) {
if (item) {
Window_CacheContents(&item->window);
}
}
static void Menu_CacheContents(menuDef_t *menu) {
if (menu) {
int i;
Window_CacheContents(&menu->window);
for (i = 0; i < menu->itemCount; i++) {
Item_CacheContents(menu->items[i]);
}
if (menu->soundName && *menu->soundName) {
DC->registerSound(menu->soundName, qfalse);
}
}
}
void Display_CacheAll() {
int i;
for (i = 0; i < menuCount; i++) {
Menu_CacheContents(&Menus[i]);
}
}
#ifdef DEVELOPER
static itemDef_t * UI_Editor_CreateControl( menuDef_t * menu, field_t * field, rectDef_t * r, const char * name )
{
itemDef_t * item = UI_Alloc(sizeof(itemDef_t));
Item_Init( item );
item->window.rectClient = *r;
item->action = String_Alloc( "uiScript editorupdate" );
item->text = String_Alloc( field->name );
item->cvar = String_Alloc( ed_cvar( name, field->name ) );
item->window.group = String_Alloc( "grpControls" );
item->window.name = String_Alloc( name );
item->textscale = 0.19f;
item->textdivx = (int)(r->w * 0.45f);
item->textaligny = ITEM_ALIGN_RIGHT;
item->textaligny = ITEM_ALIGN_CENTER;
item->window.outlineColor[ 0 ] =
item->window.outlineColor[ 1 ] =
item->window.outlineColor[ 2 ] = 0.3f;
item->window.outlineColor[ 3 ] = 1.0f;
menu->items[ menu->itemCount ] = item;
menu->items[menu->itemCount]->parent = menu;
menu->itemCount++;
return item;
}
static void UI_Editor_MakeSlider( itemDef_t * item, float minVal, float maxVal )
{
editFieldDef_t * editPtr;
item->type = ITEM_TYPE_SLIDER;
Item_ValidateTypeData(item);
editPtr = (editFieldDef_t*)item->typeData;
editPtr->defVal = minVal;
editPtr->minVal = minVal;
editPtr->maxVal = maxVal;
}
static void UI_Editor_AddField( menuDef_t * menu, const char * name, field_t * field, rectDef_t * r )
{
// create a new control to edit this field
itemDef_t * item = UI_Editor_CreateControl( menu, field, r, name );
switch( field->type )
{
case FIELD_INTEGER:
{
if ( field->fieldEnum[ 0 ].name )
{
multiDef_t *multiPtr;
int i;
item->type = ITEM_TYPE_MULTI;
Item_ValidateTypeData(item);
multiPtr = (multiDef_t*)item->typeData;
multiPtr->strDef = qfalse;
for ( i=0; i<lengthof( field->fieldEnum ) && field->fieldEnum[ i ].name; i++ )
{
multiPtr->cvarList[ i ] = String_Alloc( field->fieldEnum[ i ].shortname );
multiPtr->cvarValue[ i ] = field->fieldEnum[ i ].value;
}
multiPtr->count = i;
} else
item->type = ITEM_TYPE_NUMERICFIELD;
} break;
case FIELD_BIT:
{
item->type = ITEM_TYPE_YESNO;
} break;
case FIELD_FLOAT:
{
if ( field->minVal != field->maxVal )
{
UI_Editor_MakeSlider( item, field->minVal, field->maxVal );
} else
item->type = ITEM_TYPE_NUMERICFIELD;
} break;
case FIELD_FONT:
{
char info[ MAX_INFO_STRING ];
multiDef_t *multiPtr;
char * s;
trap_R_GetFonts( info, sizeof(info ) );
s = info;
item->type = ITEM_TYPE_MULTI;
Item_ValidateTypeData(item);
multiPtr = (multiDef_t*)item->typeData;
multiPtr->strDef = qfalse;
multiPtr->count = 0;
for ( ; ; )
{
char * t = COM_ParseExt( &s, qfalse );
if ( !t || t[ 0 ] == '\0' )
break;
multiPtr->cvarValue[ multiPtr->count ] = atoi( t );
multiPtr->cvarList[ multiPtr->count ] = String_Alloc( COM_ParseExt( &s, qfalse ) );
multiPtr->count++;
}
} break;
//case FIELD_SCRIPT:
case FIELD_STRING:
{
item->type = ITEM_TYPE_EDITFIELD;
Item_ValidateTypeData(item);
((editFieldDef_t*)item->typeData)->maxPaintChars = 32;
} break;
case FIELD_COLOR:
{
#if 0
rectDef_t a;
item->window.rectClient.w = item->textdivx + (r->w-item->textdivx)* 0.25f;
UI_Editor_MakeSlider( item, 0.0f, 1.0f ); // r
item->cvar = String_Alloc( va( "ed_%s_r", field->name ) );
a = item->window.rectClient;
a.h *= 0.5f;
a.x += a.w;
item = UI_Editor_CreateControl( menu, field, &a, name ); // g
UI_Editor_MakeSlider( item, 0.0f, 1.0f );
item->cvar = String_Alloc( va( "ed_%s_g", field->name ) );
item->text = 0;
item->textdivx = 4;
a.x += a.w;
item = UI_Editor_CreateControl( menu, field, &a, name ); // b
UI_Editor_MakeSlider( item, 0.0f, 1.0f );
item->cvar = String_Alloc( va( "ed_%s_b", field->name ) );
item->text = 0;
item->textdivx = 4;
a.x += a.w;
item = UI_Editor_CreateControl( menu, field, &a, name ); // a
UI_Editor_MakeSlider( item, 0.0f, 1.0f );
item->cvar = String_Alloc( va( "ed_%s_a", field->name ) );
item->text = 0;
item->textdivx = 4;
#endif
} break;
default:
menu->itemCount--;
return;
}
Item_ValidateTypeData( item );
}
static void UI_Editor_GetControlRect( menuDef_t * menu, rectDef_t * r, int i )
{
int n = (menu->window.rect.h-64.0f) / 18.0f;
r->w = menu->window.rect.w / 3.0f;
r->h = 16.0f;
r->x = (i/n) * r->w;
r->y = 16.0f + (i%n) * 18.0f;
}
void UI_Editor_Init()
{
menuDef_t * menu = Menus_FindByName( "editor" );
int i,t;
int h = 0;
for ( i=0,t=0; i<fieldCount; i++ )
{
rectDef_t r;
if ( fields[ i ].flags & FIELD_SECTION ) {
h = i;
continue;
}
if ( !fields[ h ].name || !fields[ i ].name || fields[ i ].flags & FIELD_DONTEDIT )
continue;
UI_Editor_GetControlRect( menu, &r, i-h );
UI_Editor_AddField( menu, fields[ h ].name, fields + i, &r );
}
Menu_PostParse( menu );
}
#endif
#define WINDOWF(x) (int)&((windowDef_t*)0)->x,0,0
#define WINDOWB(x,b) (int)&((windowDef_t*)0)->x,b,0
#define ENUMF(n,v) { n,#v,v }
#define ITEMF(x) (int)&((itemDef_t*)0)->x,0,0
#define ITEMB(x,b) (int)&((itemDef_t*)0)->x,b,0
#define FUNC(x) 0,0,x,FIELD_FUNC
#define MENUF(x) (int)&((menuDef_t*)0)->x,0,0
#define MENUB(x,b) (int)&((menuDef_t*)0)->x,b,0
#define EDITF(x) (int)&((editFieldDef_t*)0)->x,0,0
#define EDITB(x,b) (int)&((editFieldDef_t*)0)->x,b,0
#define LISTF(x) (int)&((listBoxDef_t*)0)->x,0,0
#define LISTB(x,b) (int)&((listBoxDef_t*)0)->x,b,0
#define COLF(x) (int)&((columnInfo_t*)0)->x,0,0
#define COLB(x,b) (int)&((columnInfo_t*)0)->x,b,0
#define EFFECTF(x) (int)&((effectDef_t*)0)->x,0,0
#define EFFECTB(x,b) (int)&((effectDef_t*)0)->x,b,0
#define PROGRESSF(x) (int)&((progressDef_t*)0)->x,0,0
static field_t _fields[] = {
// ----------------------------------------------------------------------------
{ "window", 0,0,0, FIELD_WINDOW, FIELD_SECTION },
{ "name", WINDOWF(name), FIELD_STRING },
{ "rect", WINDOWF(rectClient), FIELD_RECT },
{ "group", WINDOWF(group), FIELD_STRING },
{ "bordersize", WINDOWF(borderSize), FIELD_FLOAT },
{ "background", WINDOWF(background), FIELD_SHADER },
{ "forecolor", WINDOWF(foreColor), FIELD_COLOR },
{ "backcolor", WINDOWF(backColor), FIELD_COLOR },
{ "bordercolor", WINDOWF(borderColor), FIELD_COLOR },
{ "outlinecolor", WINDOWF(outlineColor), FIELD_COLOR },
{ "cinematic", WINDOWF(cinematicName), FIELD_STRING },
{ "style", WINDOWF(style), FIELD_INTEGER, 0,
{
ENUMF( "empty", WINDOW_STYLE_EMPTY ),
ENUMF( "filled", WINDOW_STYLE_FILLED ),
ENUMF( "shader", WINDOW_STYLE_SHADER ),
{ 0,0,0 },
}
},
{ "visible", WINDOWB(flags, WINDOW_VISIBLE ), FIELD_BIT },
{ "decoration", WINDOWB(flags, WINDOW_DECORATION ), FIELD_BIT },
{ "outOfBoundsClick", WINDOWB(flags, WINDOW_OOB_CLICK ), FIELD_BIT },
{ "popup", WINDOWB(flags, WINDOW_POPUP ), FIELD_BIT },
{ "wrapped", WINDOWB(flags, WINDOW_WRAPPED ), FIELD_BIT },
{ "horizontalscroll", WINDOWB(flags, WINDOW_HORIZONTAL ), FIELD_BIT },
{ "nofocus", WINDOWB(flags, WINDOW_NOFOCUS ), FIELD_BIT },
{ "focusflash", WINDOWB(flags, WINDOW_FOCUSFLASH ), FIELD_BIT },
{ "fullscreen", WINDOWB(flags, WINDOW_FULLSCREEN ), FIELD_BIT },
{ "lastmenu", WINDOWB(flags, WINDOW_LASTMENU ), FIELD_BIT },
{ "dimbackground", WINDOWB(flags, WINDOW_DIMBACKGROUND ), FIELD_BIT },
{ "border", WINDOWB(flags, WINDOW_HASBORDER ), FIELD_BIT },
{ "pause", WINDOWB(flags, WINDOW_PAUSEGAME ), FIELD_BIT },
{ "alignleft", WINDOWB(flags, WINDOW_ALIGNLEFT ), FIELD_BIT },
{ "alignright", WINDOWB(flags, WINDOW_ALIGNRIGHT ), FIELD_BIT },
{ "alignwidth", WINDOWB(flags, WINDOW_ALIGNWIDTH ), FIELD_BIT },
{ "textscroll", WINDOWB(flags, WINDOW_TEXTSCROLL ), FIELD_BIT },
{ "dontsave", WINDOWB(flags, WINDOW_DONTSAVE ), FIELD_BIT },
{ "ownerdraw", WINDOWF(ownerDraw), FIELD_INTEGER, FIELD_DONTSAVE|FIELD_DONTEDIT },
// ----------------------------------------------------------------------------
{ "item", 0,0,0, FIELD_ITEM, FIELD_SECTION },
{ "text", ITEMF(text), FIELD_STRING },
{ "textalignx", ITEMF(textalignx), FIELD_INTEGER, 0,
{
ENUMF( "left", ITEM_ALIGN_LEFT ),
ENUMF( "center", ITEM_ALIGN_CENTER ),
ENUMF( "right", ITEM_ALIGN_RIGHT ),
ENUMF( "justify", ITEM_ALIGN_JUSTIFY ),
{ 0,0,0 },
}
},
{ "textaligny", ITEMF(textaligny), FIELD_INTEGER, 0,
{
ENUMF( "left", ITEM_ALIGN_LEFT ),
ENUMF( "center", ITEM_ALIGN_CENTER ),
ENUMF( "right", ITEM_ALIGN_RIGHT ),
ENUMF( "justify", ITEM_ALIGN_JUSTIFY ),
{ 0,0,0 },
}
},
{ "textscale", ITEMF(textscale), FIELD_FLOAT, 0, { 0 }, 0.1f, 1.0f },
{ "cvarTest", ITEMF(cvarTest), FIELD_STRING },
{ "textdivx", ITEMF(textdivx), FIELD_INTEGER },
{ "textdivy", ITEMF(textdivy), FIELD_INTEGER },
{ "font", ITEMF(font), FIELD_FONT },
{ "tooltip", ITEMF(tooltip), FIELD_STRING, FIELD_DONTSAVE|FIELD_DONTEDIT },
{ "mouseEnter", ITEMF(mouseEnter), FIELD_SCRIPT },
{ "mouseExit", ITEMF(mouseExit), FIELD_SCRIPT },
{ "action", ITEMF(action), FIELD_SCRIPT },
{ "onFocus", ITEMF(onFocus), FIELD_SCRIPT },
{ "leaveFocus", ITEMF(leaveFocus), FIELD_SCRIPT },
{ "cvar", ITEMF(cvar), FIELD_STRING },
{ "textshadow", ITEMB(textStyle, ITEM_TEXTSTYLE_SHADOWED ), FIELD_BIT },
{ "textoutline", ITEMB(textStyle, ITEM_TEXTSTYLE_OUTLINED ), FIELD_BIT },
{ "textblink", ITEMB(textStyle, ITEM_TEXTSTYLE_BLINK ), FIELD_BIT },
{ "textitalic", ITEMB(textStyle, ITEM_TEXTSTYLE_ITALIC ), FIELD_BIT },
{ "smallcaps", ITEMB(textStyle, ITEM_TEXTSTYLE_SMALLCAPS ),FIELD_BIT },
{ "type", ITEMF(type), FIELD_INTEGER, FIELD_DONTSAVE|FIELD_DONTEDIT },
{ "ownertext", ITEMF(ownerText), FIELD_INTEGER, FIELD_DONTSAVE|FIELD_DONTEDIT },
{ "owneralpha", ITEMF(ownerAlpha), FIELD_INTEGER, FIELD_DONTSAVE|FIELD_DONTEDIT },
{ "focuskey", ITEMF(onFocusKey), FIELD_KEY },
{ "hotKey", ITEMF(onHotKey), FIELD_KEY },
{ "values", ITEMF(values), FIELD_STRING },
{ "asset_model", FUNC( ItemParse_asset_model ) },
{ "asset_shader", FUNC( ItemParse_asset_shader ) },
{ "model_origin", FUNC( ItemParse_model_origin ) },
{ "model_fovx", FUNC( ItemParse_model_fovx ) },
{ "model_fovy", FUNC( ItemParse_model_fovy ) },
{ "model_rotation", FUNC( ItemParse_model_rotation ) },
{ "model_angle", FUNC( ItemParse_model_angle ) },
{ "columnDef", FUNC( ItemParse_columnDef ) },
{ "cvarFloat", FUNC( ItemParse_cvarFloat ) },
{ "cvarStrList", FUNC( ItemParse_cvarStrList ) },
{ "cvarFloatList", FUNC( ItemParse_cvarFloatList ) },
{ "ownerdrawFlag", FUNC( ItemParse_ownerdrawFlag ) },
{ "enableCvar", FUNC( ItemParse_enableCvar ) },
{ "disableCvar", FUNC( ItemParse_disableCvar ) },
{ "showCvar", FUNC( ItemParse_showCvar ) },
{ "hideCvar", FUNC( ItemParse_hideCvar ) },
{ "showif", FUNC( ItemParse_showif ) },
{ "passwordCvar", FUNC( ItemParse_passwordCvar ) },
{ "attachtolist", FUNC( ItemParse_AttachToList ) },
{ "snapleft", FUNC( ItemParse_snapleft ) },
{ "snapright", FUNC( ItemParse_snapright ) },
// ----------------------------------------------------------------------------
{ "menu", 0,0,0, FIELD_MENU, FIELD_SECTION },
{"font", MENUF(font), FIELD_FONT },
{"onOpen", MENUF(onOpen), FIELD_SCRIPT },
{"onFocus", MENUF(onFocus), FIELD_SCRIPT },
{"onClose", MENUF(onClose), FIELD_SCRIPT },
{"focuscolor", MENUF(focusColor), FIELD_COLOR },
{"disablecolor", MENUF(disableColor), FIELD_COLOR },
{"soundLoop", MENUF(soundName), FIELD_STRING },
{"fadeClamp", MENUF(fadeClamp), FIELD_FLOAT },
{"fadeCycle", MENUF(fadeCycle), FIELD_FLOAT },
{"fadeAmount", MENUF(fadeAmount), FIELD_FLOAT },
{"menuKey", MENUF(onMenuKey), FIELD_KEY },
{"fadeClamp", MENUF(fadeClamp), FIELD_FLOAT },
{"fadeCycle", MENUF(fadeCycle), FIELD_FLOAT },
{"fadeAmount", MENUF(fadeAmount), FIELD_FLOAT },
{"showif", MENUF(showif), FIELD_STRING },
{"ownerdrawFlag", FUNC( MenuParse_ownerdrawFlag ) },
// ----------------------------------------------------------------------------
{ "editfield", 0,0,0, FIELD_EDITFIELD, FIELD_SECTION },
{"maxChars", EDITF(maxChars), FIELD_INTEGER },
{"maxPaintChars", EDITF(maxPaintChars), FIELD_INTEGER },
{"money", EDITB(flags, EDITFIELD_MONEY), FIELD_BIT },
{"percent", EDITB(flags, EDITFIELD_PERCENT), FIELD_BIT },
{"integer", EDITB(flags, EDITFIELD_INTEGER), FIELD_BIT },
{"cdkey", EDITB(flags, EDITFIELD_CDKEY), FIELD_BIT },
// ----------------------------------------------------------------------------
{ "listbox", 0,0,0, FIELD_LISTBOX, FIELD_SECTION },
// listBox
{"notselectable", LISTB(flags,LISTBOX_NOTSELECTABLE), FIELD_BIT },
{"dontdrawselect", LISTB(flags,LISTBOX_DONTDRAWSELECT), FIELD_BIT },
{"selectenabled", LISTB(flags,LISTBOX_SELECTENABLED), FIELD_BIT },
{"alwaysselect", LISTB(flags,LISTBOX_ALWAYSSELECT), FIELD_BIT },
{"nogridlines", LISTB(flags,LISTBOX_NOGRIDLINES), FIELD_BIT },
{"novertlines", LISTB(flags,LISTBOX_NOVERTLINES), FIELD_BIT },
{"nohorizlines", LISTB(flags,LISTBOX_NOHORIZLINES), FIELD_BIT },
{"monitortable", LISTB(flags,LISTBOX_MONITORTABLE), FIELD_BIT },
{"chatlog", LISTB(flags,LISTBOX_CHATLOG), FIELD_BIT },
{"autoscroll", LISTB(flags,LISTBOX_AUTOSCROLL), FIELD_BIT },
{"scaleenabled", LISTB(flags,LISTBOX_SCALEENABLED), FIELD_BIT },
{"noscrollbar", LISTB(flags,LISTBOX_NOSCROLLBAR), FIELD_BIT },
{"server", LISTB(flags,LISTBOX_SERVER), FIELD_BIT },
{"elementheight", LISTF(elementHeight), FIELD_FLOAT },
{"titlebar", LISTF(titlebar), FIELD_INTEGER },
{"selectshader", LISTF(selectshader), FIELD_SHADER },
{"source", LISTF(source), FIELD_STRING, FIELD_DONTSAVE|FIELD_DONTEDIT },
{"select", LISTF(select), FIELD_SCRIPT },
{"noscaleselect", LISTB(flags,LISTBOX_NOSCALESELECT), FIELD_BIT },
{"buffer_size", LISTF(buffer_size), FIELD_INTEGER, FIELD_DONTSAVE|FIELD_DONTEDIT },
{"selectcolor", LISTF(selectcolor), FIELD_COLOR },
{"rotate", LISTB(flags,LISTBOX_ROTATE), FIELD_BIT },
// ----------------------------------------------------------------------------
{ "progress", 0,0,0, FIELD_PROGRESS, FIELD_SECTION },
// progress
{"empty", PROGRESSF(empty), FIELD_SHADER, FIELD_DONTSAVE|FIELD_DONTEDIT },
{"full", PROGRESSF(full), FIELD_SHADER, FIELD_DONTSAVE|FIELD_DONTEDIT },
// ----------------------------------------------------------------------------
{ "column", 0,0,0, FIELD_COLUMNS, FIELD_SECTION },
{"pos", COLF(pos), FIELD_INTEGER },
{"width", COLF(widthPerCent), FIELD_INTEGER },
{"textalignx", COLF(horizAlign), FIELD_INTEGER },
{"textaligny", COLF(vertAlign), FIELD_INTEGER },
{"nowrap", COLB(nowrap, TEXT_ALIGN_NOCLIP), FIELD_BIT },
{"text", COLF(text), FIELD_STRING },
{"footer", COLF(footer), FIELD_STRING },
{"font", COLF(font), FIELD_FONT },
{"format", COLF(format), FIELD_INTEGER },
{"textscale", COLF(textscale), FIELD_FLOAT },
{"border", COLF(border), FIELD_FLOAT },
{"isshader", COLB(flags, COL_ISSHADER), FIELD_BIT },
{"iseffect", COLB(flags, COL_ISEFFECT), FIELD_BIT },
{"square", COLB(flags, COL_SQUARE), FIELD_BIT },
{"issprite", COLB(flags, COL_ISSPRITE), FIELD_BIT },
{"iscounter", COLB(flags, COL_ISCOUNTER), FIELD_BIT },
{"isclient", COLB(flags, COL_ISCLIENT), FIELD_BIT },
{"textshadow", COLB(textStyle, ITEM_TEXTSTYLE_SHADOWED), FIELD_BIT },
{"textoutline", COLB(textStyle, ITEM_TEXTSTYLE_OUTLINED), FIELD_BIT },
{"textblink", COLB(textStyle, ITEM_TEXTSTYLE_BLINK), FIELD_BIT },
{"textitalic", COLB(textStyle, ITEM_TEXTSTYLE_ITALIC), FIELD_BIT },
{"sprite0", COLF(sprites[0]), FIELD_SHADER },
{"sprite1", COLF(sprites[1]), FIELD_SHADER },
{"sprite2", COLF(sprites[2]), FIELD_SHADER },
{"sprite3", COLF(sprites[3]), FIELD_SHADER },
{"sprite4", COLF(sprites[4]), FIELD_SHADER },
{"sprite5", COLF(sprites[5]), FIELD_SHADER },
{"sprite6", COLF(sprites[6]), FIELD_SHADER },
{"sprite7", COLF(sprites[7]), FIELD_SHADER },
// ----------------------------------------------------------------------------
{ "effect", 0,0,0, FIELD_EFFECT, FIELD_SECTION },
{"name", EFFECTF(name), FIELD_STRING },
{"shader", EFFECTF(shader), FIELD_SHADER },
{"forecolor", EFFECTF(forecolor), FIELD_COLOR },
{"backcolor", EFFECTF(backcolor), FIELD_COLOR },
{"action", EFFECTF(action), FIELD_SCRIPT },
{"font", EFFECTF(font), FIELD_FONT },
{"textscale", EFFECTF(textscale), FIELD_FLOAT },
{"diewithmenu", EFFECTB(flags, ED_DIEWITHMENU), FIELD_BIT },
{"alwaysontop", EFFECTB(flags, ED_ALWAYSONTOP), FIELD_BIT },
{"nodraw_inui", EFFECTB(flags, ED_NODRAW_INUI), FIELD_BIT },
{"textalignx", EFFECTF(textalignx), FIELD_INTEGER, 0,
{
ENUMF( "left", ITEM_ALIGN_LEFT ),
ENUMF( "center", ITEM_ALIGN_CENTER ),
ENUMF( "right", ITEM_ALIGN_RIGHT ),
ENUMF( "justify", ITEM_ALIGN_JUSTIFY ),
{ 0,0,0 },
}
},
{"textaligny", EFFECTF(textaligny), FIELD_INTEGER, 0,
{
ENUMF( "left", ITEM_ALIGN_LEFT ),
ENUMF( "center", ITEM_ALIGN_CENTER ),
ENUMF( "right", ITEM_ALIGN_RIGHT ),
ENUMF( "justify", ITEM_ALIGN_JUSTIFY ),
{ 0,0,0 },
}
},
{ "textitalic", EFFECTB( textStyle, ITEM_TEXTSTYLE_ITALIC ), FIELD_BIT },
{ "rect", EFFECTF( rect ), FIELD_RECT },
{ "maxchars", EFFECTF( maxChars ), FIELD_INTEGER },
};
field_t * fields = _fields;
int fieldCount = lengthof(_fields );