diff options
Diffstat (limited to 'src/ui_menu.c')
-rw-r--r-- | src/ui_menu.c | 1911 |
1 files changed, 1911 insertions, 0 deletions
diff --git a/src/ui_menu.c b/src/ui_menu.c new file mode 100644 index 0000000..c99b3a2 --- /dev/null +++ b/src/ui_menu.c @@ -0,0 +1,1911 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program 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. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "ui_local.h" + +/* +=================================================================== + +ACTION CONTROL + +=================================================================== +*/ + +static void Action_Free( menuAction_t *a ) { + Z_Free( a->generic.name ); + Z_Free( a->cmd ); + Z_Free( a ); +} + +/* +================= +Action_Init +================= +*/ +static void Action_Init( menuAction_t *a ) { + if( !a->generic.name ) { + Com_Error( ERR_FATAL, "Action_Init: NULL a->generic.name" ); + } + + if( ( a->generic.uiFlags & UI_CENTER ) != UI_CENTER ) { + a->generic.x += RCOLUMN_OFFSET; + } + + a->generic.rect.x = a->generic.x; + a->generic.rect.y = a->generic.y; + UI_StringDimensions( &a->generic.rect, a->generic.uiFlags, a->generic.name ); +} + + +/* +================= +Action_Draw +================= +*/ +static void Action_Draw( menuAction_t *a ) { + int flags; + + flags = a->generic.uiFlags; + if( a->generic.flags & QMF_HASFOCUS ) { + if( ( a->generic.uiFlags & UI_CENTER ) != UI_CENTER ) { + if( ( uis.realtime >> 8 ) & 1 ) { + UI_DrawChar( a->generic.x - RCOLUMN_OFFSET / 2, a->generic.y, a->generic.uiFlags | UI_RIGHT, 13 ); + } + } else { + flags |= UI_ALTCOLOR; + } + } + + UI_DrawString( a->generic.x, a->generic.y, NULL, + flags, a->generic.name ); +} + +/* +=================================================================== + +STATIC CONTROL + +=================================================================== +*/ + +/* +================= +Static_Init +================= +*/ +static void Static_Init( menuStatic_t *s ) { + if( !s->generic.name ) { + Com_Error( ERR_FATAL, "Static_Init: NULL s->generic.name" ); + } + + if( !s->maxChars ) { + s->maxChars = MAX_STRING_CHARS; + } + + s->generic.rect.x = s->generic.x; + s->generic.rect.y = s->generic.y; + + UI_StringDimensions( &s->generic.rect, + s->generic.uiFlags, s->generic.name ); +} + +/* +================= +Static_Draw +================= +*/ +static void Static_Draw( menuStatic_t *s ) { + UI_DrawString( s->generic.x, s->generic.y, + ( s->generic.flags & QMF_CUSTOM_COLOR ) ? s->generic.color : NULL, + s->generic.uiFlags, s->generic.name ); +} + +/* +=================================================================== + +KEYBIND CONTROL + +=================================================================== +*/ + +static void Keybind_Free( menuKeybind_t *k ) { + Z_Free( k->generic.name ); + Z_Free( k->cmd ); + Z_Free( k ); +} + +/* +================= +Keybind_Init +================= +*/ +static void Keybind_Init( menuKeybind_t *k ) { + if( !k->generic.name ) { + Com_Error( ERR_FATAL, "Keybind_Init: NULL k->generic.name" ); + } + + k->generic.uiFlags &= ~( UI_LEFT | UI_RIGHT ); + + k->generic.rect.x = k->generic.x + LCOLUMN_OFFSET; + k->generic.rect.y = k->generic.y; + + UI_StringDimensions( &k->generic.rect, + k->generic.uiFlags | UI_RIGHT, k->generic.name ); + + k->generic.rect.width += ( RCOLUMN_OFFSET - LCOLUMN_OFFSET ) + + strlen( k->binding ) * CHAR_WIDTH; +} + +/* +================= +Keybind_Draw +================= +*/ +static void Keybind_Draw( menuKeybind_t *k ) { + char string[MAX_STRING_CHARS]; + byte *color; + int flags; + + color = NULL; + flags = UI_ALTCOLOR; + if( k->generic.flags & QMF_HASFOCUS ) { + /*if( k->generic.parent->keywait ) { + UI_DrawChar( k->generic.x + RCOLUMN_OFFSET / 2, k->generic.y, k->generic.uiFlags | UI_RIGHT, '=' ); + } else*/ if( ( uis.realtime >> 8 ) & 1 ) { + UI_DrawChar( k->generic.x + RCOLUMN_OFFSET / 2, k->generic.y, k->generic.uiFlags | UI_RIGHT, 13 ); + } + } else { + if( k->generic.parent->keywait ) { + color = uis.color.disabled; + flags = 0; + } + } + + UI_DrawString( k->generic.x + LCOLUMN_OFFSET, k->generic.y, color, + k->generic.uiFlags | UI_RIGHT | flags, k->generic.name ); + + if( k->altbinding[0] ) { + Q_concat( string, sizeof( string ), k->binding, " or ", k->altbinding, NULL ); + } else if( k->binding[0] ) { + strcpy( string, k->binding ); + } else { + strcpy( string, "???" ); + } + + UI_DrawString( k->generic.x + RCOLUMN_OFFSET, k->generic.y, color, + k->generic.uiFlags | UI_LEFT, string ); +} + +static menuSound_t Keybind_DoEnter( menuKeybind_t *k ) { + menuFrameWork_t *menu = k->generic.parent; + + menu->keywait = qtrue; + menu->status = "Press the desired key, Escape to cancel"; + return QMS_IN; +} + +static void Keybind_Push( menuKeybind_t *k ) { + int key = Key_EnumBindings( 0, k->cmd ); + k->altbinding[0] = 0; + if( key == -1 ) { + strcpy( k->binding, "???" ); + } else { + strcpy( k->binding, Key_KeynumToString( key ) ); + key = Key_EnumBindings( key + 1, k->cmd ); + if( key != -1 ) { + strcpy( k->altbinding, Key_KeynumToString( key ) ); + } + } +} + +static void Keybind_Update( menuFrameWork_t *menu ) { + menuKeybind_t *k; + int i; + + for( i = 0; i < menu->nitems; i++ ) { + k = menu->items[i]; + if( k->generic.type == MTYPE_KEYBIND ) { + Keybind_Push( k ); + } + } +} + +static void Keybind_Remove( const char *cmd ) { + int key; + + for( key = 0; ; key++ ) { + key = Key_EnumBindings( key, cmd ); + if( key == -1 ) { + break; + } + Key_SetBinding( key, NULL ); + } +} + +static menuSound_t Keybind_Key( menuKeybind_t *k, int key ) { + menuFrameWork_t *menu = k->generic.parent; + + if( menu->keywait ) { + if( key != K_ESCAPE ) { + if( k->altbinding[0] ) { + Keybind_Remove( k->cmd ); + } + Key_SetBinding( key, k->cmd ); + } + Keybind_Update( menu ); + menu->keywait = qfalse; + menu->status = "Press Enter to change, Backspace to clear"; + return QMS_OUT; + } + + if( key == K_BACKSPACE || key == K_DEL ) { + Keybind_Remove( k->cmd ); + Keybind_Update( menu ); + return QMS_IN; + } + + return QMS_NOTHANDLED; +} + + +/* +=================================================================== + +FIELD CONTROL + +=================================================================== +*/ + +static void Field_Push( menuField_t *f ) { + IF_Init( &f->field, f->width, MAX_FIELD_TEXT - 1 ); + IF_Replace( &f->field, f->cvar->string ); +} + +static void Field_Pop( menuField_t *f ) { + Cvar_SetByVar( f->cvar, f->field.text, FROM_CONSOLE ); +} + +static void Field_Free( menuField_t *f ) { + Z_Free( f->generic.name ); + Z_Free( f->generic.status ); + Z_Free( f ); +} + +/* +================= +Field_Init +================= +*/ +static void Field_Init( menuField_t *f ) { + int w = f->width * CHAR_WIDTH; + + f->generic.uiFlags &= ~( UI_LEFT | UI_RIGHT ); + + if( f->generic.name ) { + f->generic.rect.x = f->generic.x + LCOLUMN_OFFSET; + f->generic.rect.y = f->generic.y; + UI_StringDimensions( &f->generic.rect, + f->generic.uiFlags | UI_RIGHT, f->generic.name ); + f->generic.rect.width += ( RCOLUMN_OFFSET - LCOLUMN_OFFSET ) + w; + } else { + f->generic.rect.x = f->generic.x - w / 2; + f->generic.rect.y = f->generic.y; + f->generic.rect.width = w; + f->generic.rect.height = CHAR_HEIGHT; + } +} + + +/* +================= +Field_Draw +================= +*/ +static void Field_Draw( menuField_t *f ) { + int flags = f->generic.uiFlags; + byte *color = uis.color.normal; + + if( f->generic.flags & QMF_HASFOCUS ) { + flags |= UI_DRAWCURSOR; + color = uis.color.active; + } + + if( f->generic.name ) { + UI_DrawString( f->generic.x + LCOLUMN_OFFSET, f->generic.y, NULL, + f->generic.uiFlags | UI_RIGHT | UI_ALTCOLOR, f->generic.name ); + + R_DrawFillEx( f->generic.x + RCOLUMN_OFFSET, f->generic.y - 1, + f->field.visibleChars * CHAR_WIDTH, CHAR_HEIGHT + 2, color ); + + IF_Draw( &f->field, f->generic.x + RCOLUMN_OFFSET, f->generic.y, + flags, uis.fontHandle ); + } else { + R_DrawFillEx( f->generic.rect.x, f->generic.rect.y - 1, + f->generic.rect.width, CHAR_HEIGHT + 2, color ); + + IF_Draw( &f->field, f->generic.rect.x, f->generic.rect.y, + flags, uis.fontHandle ); + } +} + +/* +================= +Field_Key +================= +*/ +static int Field_Key( menuField_t *f, int key ) { + qboolean ret; + + ret = IF_KeyEvent( &f->field, key ); + if( ret ) { + return QMS_SILENT; + } + if( f->generic.flags & QMF_NUMBERSONLY ) { + if( Q_isdigit( key ) ) { + return QMS_SILENT; + } + } else { + if( key >= 32 && key < 127 ) { + return QMS_SILENT; + } + } + return QMS_NOTHANDLED; +} + +/* +================= +Field_Char +================= +*/ +static int Field_Char( menuField_t *f, int key ) { + int ret; + + if( f->generic.flags & QMF_NUMBERSONLY ) { + if( key < '0' || key > '9' ) { + return QMS_BEEP; + } + } + + ret = IF_CharEvent( &f->field, key ); + if( f->generic.change ) { + f->generic.change( &f->generic ); + } + + return ret ? QMS_SILENT : QMS_NOTHANDLED; +} + +/* +=================================================================== + +SPIN CONTROL + +=================================================================== +*/ + +static void SpinControl_Push( menuSpinControl_t *s ) { + int val = s->cvar->integer; + clamp( val, 0, s->numItems - 1 ); + s->curvalue = val; +} + +static void SpinControl_Pop( menuSpinControl_t *s ) { + Cvar_SetInteger( s->cvar, s->curvalue, FROM_CONSOLE ); +} + +static void SpinControl_Free( menuSpinControl_t *s ) { + int i; + + Z_Free( s->generic.name ); + for( i = 0; i < s->numItems; i++ ) { + Z_Free( s->itemnames[i] ); + } + Z_Free( s->itemnames ); + Z_Free( s ); +} + + +/* +================= +SpinControl_Init +================= +*/ +void SpinControl_Init( menuSpinControl_t *s ) { + char **n; + int maxLength, length; + + s->generic.uiFlags &= ~( UI_LEFT | UI_RIGHT ); + + s->generic.rect.x = s->generic.x + LCOLUMN_OFFSET; + s->generic.rect.y = s->generic.y; + + UI_StringDimensions( &s->generic.rect, + s->generic.uiFlags | UI_RIGHT, s->generic.name ); + + maxLength = 0; + s->numItems = 0; + n = s->itemnames; + while( *n ) { + length = strlen( *n ); + + if( maxLength < length ) { + maxLength = length; + } + s->numItems++; + n++; + } + + s->generic.rect.width += ( RCOLUMN_OFFSET - LCOLUMN_OFFSET ) + + maxLength * CHAR_WIDTH; +} + +/* +================= +SpinControl_DoEnter +================= +*/ +static int SpinControl_DoEnter( menuSpinControl_t *s ) { + s->curvalue++; + + if( s->curvalue >= s->numItems ) + s->curvalue = 0; + + if( s->generic.change ) { + s->generic.change( &s->generic ); + } + + return QMS_MOVE; +} + +/* +================= +SpinControl_DoSlide +================= +*/ +static int SpinControl_DoSlide( menuSpinControl_t *s, int dir ) { + s->curvalue += dir; + + if( s->curvalue < 0 ) { + s->curvalue = s->numItems - 1; + } else if( s->curvalue >= s->numItems ) { + s->curvalue = 0; + } + + if( s->generic.change ) { + s->generic.change( &s->generic ); + } + + return QMS_MOVE; +} + +/* +================= +SpinControl_Draw +================= +*/ +static void SpinControl_Draw( menuSpinControl_t *s ) { + UI_DrawString( s->generic.x + LCOLUMN_OFFSET, s->generic.y, NULL, + s->generic.uiFlags | UI_RIGHT | UI_ALTCOLOR, s->generic.name ); + + if( s->generic.flags & QMF_HASFOCUS ) { + if( ( uis.realtime >> 8 ) & 1 ) { + UI_DrawChar( s->generic.x + RCOLUMN_OFFSET / 2, s->generic.y, + s->generic.uiFlags | UI_RIGHT, 13 ); + } + } + + UI_DrawString( s->generic.x + RCOLUMN_OFFSET, s->generic.y, NULL, + s->generic.uiFlags, s->itemnames[s->curvalue] ); +} + +/* +=================================================================== + +BITFIELD CONTROL + +=================================================================== +*/ + +static void BitField_Push( menuSpinControl_t *s ) { + if( s->cvar->integer & s->mask ) { + s->curvalue = 1 ^ s->negate; + } else { + s->curvalue = 0 ^ s->negate; + } +} + +static void BitField_Pop( menuSpinControl_t *s ) { + int val = s->cvar->integer; + + if( s->curvalue ^ s->negate ) { + val |= s->mask; + } else { + val &= ~s->mask; + } + Cvar_SetInteger( s->cvar, val, FROM_CONSOLE ); +} + +static void BitField_Free( menuSpinControl_t *s ) { + Z_Free( s->generic.name ); + Z_Free( s ); +} + +/* +=================================================================== + +PAIRS CONTROL + +=================================================================== +*/ + +static void Pairs_Push( menuSpinControl_t *s ) { + int i; + + for( i = 0; i < s->numItems; i++ ) { + if( !Q_stricmp( s->itemvalues[i], s->cvar->string ) ) { + s->curvalue = i; + break; + } + } +} + +static void Pairs_Pop( menuSpinControl_t *s ) { + Cvar_SetByVar( s->cvar, s->itemvalues[s->curvalue], FROM_CONSOLE ); +} + +static void Pairs_Free( menuSpinControl_t *s ) { + int i; + + Z_Free( s->generic.name ); + for( i = 0; i < s->numItems; i++ ) { + Z_Free( s->itemnames[i] ); + Z_Free( s->itemvalues[i] ); + } + Z_Free( s->itemnames ); + Z_Free( s->itemvalues ); + Z_Free( s ); +} + +/* +=================================================================== + +STRINGS CONTROL + +=================================================================== +*/ + +static void Strings_Push( menuSpinControl_t *s ) { + int i; + + for( i = 0; i < s->numItems; i++ ) { + if( !Q_stricmp( s->itemnames[i], s->cvar->string ) ) { + s->curvalue = i; + break; + } + } +} + +static void Strings_Pop( menuSpinControl_t *s ) { + Cvar_SetByVar( s->cvar, s->itemnames[s->curvalue], FROM_CONSOLE ); +} + +/* +=================================================================== + +TOGGLE CONTROL + +=================================================================== +*/ + +static void Toggle_Push( menuSpinControl_t *s ) { + s->curvalue = ( s->cvar->integer ? 1 : 0 ) ^ s->negate; +} + +/* +=================================================================== + +LIST CONTROL + +=================================================================== +*/ + +/* +================= +MenuList_ValidatePrestep +================= +*/ +static void MenuList_ValidatePrestep( menuList_t *l ) { + if( l->prestep > l->numItems - l->maxItems ) { + l->prestep = l->numItems - l->maxItems; + } + if( l->prestep < 0 ) { + l->prestep = 0; + } +} + +static void MenuList_AdjustPrestep( menuList_t *l ) { + if( l->numItems > l->maxItems ) { + if( l->prestep > l->curvalue ) { + l->prestep = l->curvalue; + } else if( l->prestep < l->curvalue - l->maxItems + 1 ) { + l->prestep = l->curvalue - l->maxItems + 1; + } + } else { + l->prestep = 0; + } +} + +/* +================= +MenuList_Init +================= +*/ +void MenuList_Init( menuList_t *l ) { + int height; + int i; + + height = l->generic.height; + if( !( l->mlFlags & MLF_HIDE_HEADER ) ) { + height -= MLIST_SPACING; + } + + l->maxItems = height / MLIST_SPACING; + + clamp( l->curvalue, 0, l->numItems - 1 ); + + MenuList_ValidatePrestep( l ); + + l->generic.rect.x = l->generic.x; + l->generic.rect.y = l->generic.y; + + l->generic.rect.width = 0; + for( i = 0; i < l->numcolumns; i++ ) { + l->generic.rect.width += l->columns[i].width; + } + +// if( !( l->mlFlags & MLF_HIDE_SCROLLBAR ) ) { +// rc->width += MLIST_SCROLLBAR_WIDTH; +// } + + l->generic.rect.height = l->generic.height; + + if( l->sortdir && l->sort ) { + l->sort( l, l->sortcol ); + } +} + +/* +================= +MenuList_SetValue +================= +*/ +void MenuList_SetValue( menuList_t *l, int value ) { + clamp( value, 0, l->numItems - 1 ); + + if( value != l->curvalue ) { + l->curvalue = value; + if( l->generic.change ) { + l->generic.change( &l->generic ); + } + } + + MenuList_AdjustPrestep( l ); +} + +static int MenuList_SetColumn( menuList_t *l, int value ) { + if( l->sortcol == value ) { + l->sortdir = -l->sortdir; + } else { + l->sortcol = value; + l->sortdir = 1; + } + if( l->sort ) { + l->sort( l, l->sortcol ); + } + return QMS_SILENT; +} + + +/* +================= +MenuList_Click +================= +*/ +static int MenuList_Click( menuList_t *l ) { + int i, j; + vrect_t rect; + + if( !l->items ) { + return QMS_SILENT; + } + + rect.x = l->generic.rect.x; + rect.y = l->generic.rect.y; + rect.width = l->generic.rect.width; + rect.height = MLIST_SPACING; + + // click on header + if( !( l->mlFlags & MLF_HIDE_HEADER ) ) { + if( l->sortdir && UI_CursorInRect( &rect ) ) { + for( j = 0; j < l->numcolumns; j++ ) { + rect.width = l->columns[j].width; + if( UI_CursorInRect( &rect ) ) { + return MenuList_SetColumn( l, j ); + } + rect.x += rect.width; + } + return QMS_SILENT; + } + rect.y += MLIST_SPACING; + } + + // click on item + j = min( l->numItems, l->prestep + l->maxItems ); + for( i = l->prestep; i < j; i++ ) { + if( UI_CursorInRect( &rect ) ) { + if( l->curvalue == i && uis.realtime - + l->clickTime < DOUBLE_CLICK_DELAY ) + { + if( l->generic.activate ) { + return l->generic.activate( &l->generic ); + } + return QMS_SILENT; + } + l->clickTime = uis.realtime; + l->curvalue = i; + if( l->generic.change ) { + return l->generic.change( &l->generic ); + } + return QMS_SILENT; + } + rect.y += MLIST_SPACING; + } + + return QMS_SILENT; +} + +/* +================= +MenuList_Key +================= +*/ +static int MenuList_Key( menuList_t *l, int key ) { + //int i; + + if( !l->items ) { + return QMS_NOTHANDLED; + } + + if( Key_IsDown( K_ALT ) && Q_isdigit( key ) ) { + int col = key == '0' ? 9 : key - '0' - 1; + if( l->sortdir && col < l->numcolumns ) { + return MenuList_SetColumn( l, col ); + } + return QMS_NOTHANDLED; + } + +#if 0 + if( key > 32 && key < 127 ) { + if( uis.realtime > l->scratchTime + 1300 ) { + l->scratchCount = 0; + l->scratchTime = uis.realtime; + } + + if( l->scratchCount >= sizeof( l->scratch ) - 1 ) { + return QMS_NOTHANDLED; + } + + l->scratch[l->scratchCount++] = key; + l->scratch[l->scratchCount] = 0; + + //l->scratchTime = uis.realtime; + + if( !Q_stricmpn( UI_GetColumn( ( char * )l->items[l->curvalue] + l->extrasize, l->sortcol ), + l->scratch, l->scratchCount ) ) + { + return QMS_NOTHANDLED; + } + + for( i = 0; i < l->numItems; i++ ) { + if( !Q_stricmpn( UI_GetColumn( ( char * )l->items[i] + l->extrasize, l->sortcol ), l->scratch, l->scratchCount ) ) { + MenuList_SetValue( l, i ); + return QMS_SILENT; + } + i++; + } + + return QMS_NOTHANDLED; + } +#endif + + l->scratchCount = 0; + + switch( key ) { + case K_LEFTARROW: + case 'h': + if( l->sortdir ) { + if( l->sortcol > 0 ) { + return MenuList_SetColumn( l, l->sortcol - 1 ); + } + return MenuList_SetColumn( l, l->numcolumns - 1 ); + } + break; + case K_RIGHTARROW: + case 'l': + if( l->sortdir ) { + if( l->sortcol < l->numcolumns - 1 ) { + return MenuList_SetColumn( l, l->sortcol + 1 ); + } + return MenuList_SetColumn( l, 0 ); + } + break; + case K_UPARROW: + case K_KP_UPARROW: + case 'k': + if( l->curvalue > 0 ) { + l->curvalue--; + if( l->generic.change ) { + l->generic.change( &l->generic ); + } + MenuList_AdjustPrestep( l ); + return QMS_MOVE; + } + return QMS_BEEP; + + case K_DOWNARROW: + case K_KP_DOWNARROW: + case 'j': + if( l->curvalue < l->numItems - 1 ) { + l->curvalue++; + if( l->generic.change ) { + l->generic.change( &l->generic ); + } + MenuList_AdjustPrestep( l ); + return QMS_MOVE; + } + return QMS_BEEP; + + case K_HOME: + case K_KP_HOME: + l->prestep = 0; + l->curvalue = 0; + if( l->generic.change ) { + l->generic.change( &l->generic ); + } + return QMS_MOVE; + + case K_END: + case K_KP_END: + if( l->numItems > l->maxItems ) { + l->prestep = l->numItems - l->maxItems; + } + l->curvalue = l->numItems - 1; + if( l->generic.change ) { + l->generic.change( &l->generic ); + } + return QMS_MOVE; + + case K_MWHEELUP: + if( Key_IsDown( K_CTRL ) ) { + l->prestep -= 4; + } else { + l->prestep -= 2; + } + MenuList_ValidatePrestep( l ); + return QMS_SILENT; + + case K_MWHEELDOWN: + if( Key_IsDown( K_CTRL ) ) { + l->prestep += 4; + } else { + l->prestep += 2; + } + MenuList_ValidatePrestep( l ); + return QMS_SILENT; + + case K_PGUP: + case K_KP_PGUP: + l->prestep -= l->maxItems; + MenuList_ValidatePrestep( l ); + return QMS_SILENT; + + case K_PGDN: + case K_KP_PGDN: + l->prestep += l->maxItems; + MenuList_ValidatePrestep( l ); + return QMS_SILENT; + + case K_MOUSE1: + case K_MOUSE2: + //case K_MOUSE3: + return MenuList_Click( l ); + } + + return QMS_NOTHANDLED; +} + +/* +================= +MenuList_DrawString +================= +*/ +static void MenuList_DrawString( int x, int y, int flags, + menuListColumn_t *column, + const char *string ) +{ + clipRect_t rc; + + rc.left = x; + rc.right = x + column->width - 1; + rc.top = 0; + rc.bottom = 0; + + if( ( column->uiFlags & UI_CENTER ) == UI_CENTER ) { + x += column->width / 2; + } else if( column->uiFlags & UI_RIGHT ) { + x += column->width - MLIST_PRESTEP; + } else { + x += MLIST_PRESTEP; + } + + R_SetClipRect( DRAW_CLIP_RIGHT|DRAW_CLIP_LEFT, &rc ); + UI_DrawString( x, y + 1, NULL, column->uiFlags | flags, string ); +#if USE_REF == REF_SOFT + R_SetClipRect( DRAW_CLIP_MASK, &uis.clipRect ); +#else + R_SetClipRect( DRAW_CLIP_DISABLED, NULL ); +#endif +} + +/* +================= +MenuList_Draw +================= +*/ +static void MenuList_Draw( menuList_t *l ) { + char *s; + int x, y, xx, yy; + int i, j, k; + int width, height; + float pageFrac, prestepFrac; + int barHeight; + + x = l->generic.rect.x; + y = l->generic.rect.y; + width = l->generic.rect.width; + height = l->generic.rect.height; + + // draw header + if( !( l->mlFlags & MLF_HIDE_HEADER ) ) { + xx = x; + for( j = 0; j < l->numcolumns; j++ ) { + int flags = UI_ALTCOLOR; + byte *color = uis.color.normal; + + if( l->sortcol == j && l->sortdir ) { + flags = 0; + if( l->generic.flags & QMF_HASFOCUS ) { + color = uis.color.active; + } + } + R_DrawFillEx( xx, y, l->columns[j].width - 1, + MLIST_SPACING - 1, color ); + + if( l->columns[j].name ) { + MenuList_DrawString( xx, y, flags, + &l->columns[j], l->columns[j].name ); + } + xx += l->columns[j].width; + } + y += MLIST_SPACING; + height -= MLIST_SPACING; + } + + if( !( l->mlFlags & MLF_HIDE_SCROLLBAR ) && + ( !( l->mlFlags & MLF_HIDE_SCROLLBAR_EMPTY ) || l->numItems > l->maxItems ) ) + { + barHeight = height - MLIST_SPACING * 2; + yy = y + MLIST_SPACING; + + // draw scrollbar background + if( !( l->mlFlags & MLF_HIDE_BACKGROUND ) ) { + R_DrawFillEx( x + width, yy, MLIST_SCROLLBAR_WIDTH - 1, + barHeight, uis.color.normal ); + } + + if( l->numItems > l->maxItems ) { + pageFrac = ( float )l->maxItems / l->numItems; + prestepFrac = ( float )l->prestep / l->numItems; + } else { + pageFrac = 1; + prestepFrac = 0; + } + + // draw scrollbar thumb + R_DrawFillEx( x + width, + yy + Q_rint( barHeight * prestepFrac ), + MLIST_SCROLLBAR_WIDTH - 1, + Q_rint( barHeight * pageFrac ), + uis.color.selection ); + } + + xx = x; + for( j = 0; j < l->numcolumns; j++ ) { + byte *color = uis.color.normal; + + if( l->sortcol == j && l->sortdir ) { + if( l->generic.flags & QMF_HASFOCUS ) { + color = uis.color.active; + } + } + R_DrawFillEx( xx, y, l->columns[j].width - 1, + height, color ); + + xx += l->columns[j].width; + } + + yy = y; + k = min( l->numItems, l->prestep + l->maxItems ); + for( i = l->prestep; i < k; i++ ) { + // draw selection + if( !( l->generic.flags & QMF_DISABLED ) && i == l->curvalue ) { + xx = x; + for( j = 0; j < l->numcolumns; j++ ) { + R_DrawFillEx( xx, yy, l->columns[j].width - 1, + MLIST_SPACING, uis.color.selection ); + xx += l->columns[j].width; + } + } + + // draw contents + s = ( char * )l->items[i] + l->extrasize; + xx = x; + for( j = 0; j < l->numcolumns; j++ ) { + if( !*s ) { + break; + } + + MenuList_DrawString( xx, yy, 0, &l->columns[j], s ); + + xx += l->columns[j].width; + s += strlen( s ) + 1; + } + + yy += MLIST_SPACING; + } +} + +void MenuList_Sort( menuList_t *l, int offset, int (*cmpfunc)( const void *, const void * ) ) { + void *n; + int i; + + if( !l->items ) { + return; + } + + n = l->items[l->curvalue]; + + qsort( l->items + offset, l->numItems - offset, sizeof( char * ), cmpfunc ); + + for( i = 0; i < l->numItems; i++ ) { + if( l->items[i] == n ) { + l->curvalue = i; + break; + } + } + + MenuList_AdjustPrestep( l ); +} + +/* +=================================================================== + +SLIDER CONTROL + +=================================================================== +*/ + +static void Slider_Push( menuSlider_t *s ) { + s->curvalue = s->cvar->value; + cclamp( s->curvalue, s->minvalue, s->maxvalue ); +} + +void Slider_Pop( menuSlider_t *s ) { + Cvar_SetValue( s->cvar, s->curvalue, FROM_CONSOLE ); +} + +static void Slider_Free( menuSlider_t *s ) { + Z_Free( s->generic.name ); + Z_Free( s ); +} + +static void Slider_Init( menuSlider_t *s ) { + int len = strlen( s->generic.name ) * CHAR_WIDTH; + + s->generic.rect.x = s->generic.x + LCOLUMN_OFFSET - len; + s->generic.rect.y = s->generic.y; + + s->generic.rect.width = ( RCOLUMN_OFFSET - LCOLUMN_OFFSET ) + + len + ( SLIDER_RANGE + 2 ) * CHAR_WIDTH; + s->generic.rect.height = CHAR_HEIGHT; +} + +static int Slider_Key( menuSlider_t *s, int key ) { + switch( key ) { + case K_END: + s->curvalue = s->maxvalue; + return QMS_MOVE; + case K_HOME: + s->curvalue = s->minvalue; + return QMS_MOVE; + } + + return QMS_NOTHANDLED; +} + + +/* +================= +Slider_DoSlide +================= +*/ +static int Slider_DoSlide( menuSlider_t *s, int dir ) { + s->curvalue += dir * s->step; + + cclamp( s->curvalue, s->minvalue, s->maxvalue ); + + if( s->generic.change ) { + menuSound_t sound = s->generic.change( &s->generic ); + if( sound != QMS_NOTHANDLED ) { + return sound; + } + } + + return QMS_SILENT; +} + +/* +================= +Slider_Draw +================= +*/ +static void Slider_Draw( menuSlider_t *s ) { + int i, flags; + float pos; + + flags = s->generic.uiFlags & ~( UI_LEFT | UI_RIGHT ); + + if( s->generic.flags & QMF_HASFOCUS ) { + if( ( uis.realtime >> 8 ) & 1 ) { + UI_DrawChar( s->generic.x + RCOLUMN_OFFSET / 2, s->generic.y, s->generic.uiFlags | UI_RIGHT, 13 ); + } + } + + UI_DrawString( s->generic.x + LCOLUMN_OFFSET, s->generic.y, NULL, + flags | UI_RIGHT | UI_ALTCOLOR, s->generic.name ); + + UI_DrawChar( s->generic.x + RCOLUMN_OFFSET, s->generic.y, flags | UI_LEFT, 128 ); + + for( i = 0 ; i < SLIDER_RANGE ; i++ ) + UI_DrawChar( RCOLUMN_OFFSET + s->generic.x + i * CHAR_WIDTH + CHAR_WIDTH, s->generic.y, flags | UI_LEFT, 129 ); + + UI_DrawChar( RCOLUMN_OFFSET + s->generic.x + i * CHAR_WIDTH + CHAR_WIDTH, s->generic.y, flags | UI_LEFT, 130 ); + + pos = ( s->curvalue - s->minvalue ) / ( s->maxvalue - s->minvalue ); + clamp( pos, 0, 1 ); + + UI_DrawChar( CHAR_WIDTH + RCOLUMN_OFFSET + s->generic.x + ( SLIDER_RANGE - 1 ) * CHAR_WIDTH * pos, s->generic.y, flags | UI_LEFT, 131 ); +} + +/* +=================================================================== + +SEPARATOR CONTROL + +=================================================================== +*/ + +/* +================= +Separator_Init +================= +*/ +static void Separator_Init( menuSeparator_t *s ) { + s->generic.rect.x = s->generic.rect.y = 999999; + s->generic.rect.width = s->generic.rect.height = -999999; +} + +/* +================= +Separator_Draw +================= +*/ +static void Separator_Draw( menuSeparator_t *s ) { + if( s->generic.name ) + UI_DrawString( s->generic.x, s->generic.y, NULL, UI_RIGHT, s->generic.name ); +} + +/* +=================================================================== + +MISC + +=================================================================== +*/ + +/* +================= +Common_DoEnter +================= +*/ +static int Common_DoEnter( menuCommon_t *item ) { + if( item->activate ) { + menuSound_t sound = item->activate( item ); + if( sound != QMS_NOTHANDLED ) { + return sound; + } + } + + return QMS_IN; +} + + +/* +================= +Menu_AddItem +================= +*/ +void Menu_AddItem( menuFrameWork_t *menu, void *item ) { + if( menu->nitems >= MAXMENUITEMS ) { + Com_WPrintf( "Menu_AddItem: %s: too many items\n", menu->name ); + return; + } + + menu->items[menu->nitems++] = item; + ((menuCommon_t *)item)->parent = menu; +} + +void Menu_Init( menuFrameWork_t *menu ) { + void *item; + int i; + int focus = 0; + + menu->y1 = 0; + menu->y2 = uis.height; + + if( !menu->size ) { + menu->size = Menu_Size; + } + menu->size( menu ); + + for( i = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + + focus |= ((menuCommon_t *)item)->flags & QMF_HASFOCUS; + switch( ((menuCommon_t *)item)->type ) { + case MTYPE_FIELD: + Field_Init( item ); + break; + case MTYPE_SLIDER: + Slider_Init( item ); + break; + case MTYPE_LIST: + MenuList_Init( item ); + break; + case MTYPE_SPINCONTROL: + case MTYPE_BITFIELD: + case MTYPE_PAIRS: + case MTYPE_VALUES: + case MTYPE_STRINGS: + case MTYPE_TOGGLE: + SpinControl_Init( item ); + break; + case MTYPE_ACTION: + Action_Init( item ); + break; + case MTYPE_SEPARATOR: + Separator_Init( item ); + break; + case MTYPE_STATIC: + Static_Init( item ); + break; + case MTYPE_KEYBIND: + Keybind_Init( item ); + break; + default: + Com_Error( ERR_FATAL, "Menu_Init: unknown item type" ); + break; + } + } + + // set focus to the first item by default + if( !focus && menu->nitems ) { + item = menu->items[0]; + ((menuCommon_t *)item)->flags |= QMF_HASFOCUS; + } +} + +void Menu_Size( menuFrameWork_t *menu ) { + menuCommon_t *item; + int x, y; + int i, count; + + // count visible items + for( i = 0, count = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + if( item->flags & QMF_HIDDEN ) { + continue; + } + count++; + } + + // set menu top/bottom + if( menu->transparent ) { + menu->y1 = ( uis.height - MENU_SPACING * count ) / 2 - MENU_SPACING; + menu->y2 = ( uis.height + MENU_SPACING * count ) / 2 + MENU_SPACING; + } else { + menu->y1 = 0; + menu->y2 = uis.height; + } + + x = uis.width / 2; + y = ( uis.height - MENU_SPACING * count ) / 2; + + // align items + for( i = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + if( item->flags & QMF_HIDDEN ) { + continue; + } + item->x = x; + item->y = y; + y += MENU_SPACING; + } + +} + + +menuCommon_t *Menu_ItemAtCursor( menuFrameWork_t *m ) { + menuCommon_t *item; + int i; + + for( i = 0; i < m->nitems; i++ ) { + item = m->items[i]; + if( item->flags & QMF_HASFOCUS ) { + return item; + } + } + + return NULL; +} + +void Menu_SetFocus( menuCommon_t *focus ) { + menuFrameWork_t *menu; + menuCommon_t *item; + int i; + + if( focus->flags & QMF_HASFOCUS ) { + return; + } + + menu = focus->parent; + + for( i = 0; i < menu->nitems ; i++ ) { + item = (menuCommon_t *)menu->items[i]; + + if( item == focus ) { + item->flags |= QMF_HASFOCUS; + if( item->focus ) { + item->focus( item, qtrue ); + } else if( item->status ) { + menu->status = item->status; + } + } else if( item->flags & QMF_HASFOCUS ) { + item->flags &= ~QMF_HASFOCUS; + if( item->focus ) { + item->focus( item, qfalse ); + } else if( menu->status == item->status ) { + menu->status = NULL; + } + } + } + +} + +/* +================= +Menu_AdjustCursor + +This function takes the given menu, the direction, and attempts +to adjust the menu's cursor so that it's at the next available +slot. +================= +*/ +menuSound_t Menu_AdjustCursor( menuFrameWork_t *m, int dir ) { + menuCommon_t *item; + int cursor, pos; + int i; + + pos = 0; + for( i=0 ; i<m->nitems ; i++ ) { + item = (menuCommon_t *)m->items[i]; + + if( item->flags & QMF_HASFOCUS ) { + pos = i; + break; + } + } + + /* + ** crawl in the direction indicated until we find a valid spot + */ + cursor = pos; + if( dir == 1 ) { + do { + cursor++; + if( cursor >= m->nitems ) + cursor = 0; + + item = (menuCommon_t *)m->items[cursor]; + if( UI_IsItemSelectable( item ) ) + break; + } while( cursor != pos ); + } else { + do { + cursor--; + if( cursor < 0 ) + cursor = m->nitems - 1; + + item = (menuCommon_t *)m->items[cursor]; + if( UI_IsItemSelectable( item ) ) + break; + } while( cursor != pos ); + } + + Menu_SetFocus( item ); + + return QMS_MOVE; +} + +/* +================= +Menu_Draw +================= +*/ +void Menu_Draw( menuFrameWork_t *menu ) { + void *item; + int i; + +// +// draw background +// + if( menu->image ) { + R_DrawStretchPic( 0, menu->y1, uis.width, + menu->y2 - menu->y1, menu->image ); + } else { + R_DrawFillEx( 0, menu->y1, uis.width, + menu->y2 - menu->y1, menu->color ); + } + +// +// draw title bar +// + if( menu->title ) { + UI_DrawString( uis.width / 2, menu->y1, NULL, + UI_CENTER|UI_ALTCOLOR, menu->title ); + } + +// +// draw contents +// + for( i = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + if( (( menuCommon_t * )item)->flags & QMF_HIDDEN ) { + continue; + } + + switch( (( menuCommon_t * )item)->type ) { + case MTYPE_FIELD: + Field_Draw( item ); + break; + case MTYPE_SLIDER: + Slider_Draw( item ); + break; + case MTYPE_LIST: + MenuList_Draw( item ); + break; + case MTYPE_SPINCONTROL: + case MTYPE_BITFIELD: + case MTYPE_PAIRS: + case MTYPE_VALUES: + case MTYPE_STRINGS: + case MTYPE_TOGGLE: + SpinControl_Draw( item ); + break; + case MTYPE_ACTION: + Action_Draw( item ); + break; + case MTYPE_SEPARATOR: + Separator_Draw( item ); + break; + case MTYPE_STATIC: + Static_Draw( item ); + break; + case MTYPE_KEYBIND: + Keybind_Draw( item ); + break; + default: + Com_Error( ERR_FATAL, "Menu_Draw: unknown item type" ); + break; + } + + if( ui_debug->integer ) { + UI_DrawRect( &(( menuCommon_t * )item)->rect, 1, 223 ); + } + } + +// +// draw status bar +// + if( menu->status ) { + R_DrawFill( 0, menu->y2 - 8, uis.width, 8, 4 ); + UI_DrawString( uis.width / 2, menu->y2 - 8, NULL, + UI_CENTER, menu->status ); + } +} + +menuSound_t Menu_SelectItem( menuFrameWork_t *s ) { + menuCommon_t *item; + + if( !( item = Menu_ItemAtCursor( s ) ) ) { + return QMS_NOTHANDLED; + } + + switch( item->type ) { + //case MTYPE_SLIDER: + // return Slider_DoSlide( (menuSlider_t *)item, 1 ); + case MTYPE_SPINCONTROL: + case MTYPE_BITFIELD: + case MTYPE_PAIRS: + case MTYPE_VALUES: + case MTYPE_STRINGS: + case MTYPE_TOGGLE: + return SpinControl_DoEnter( (menuSpinControl_t *)item ); + case MTYPE_KEYBIND: + return Keybind_DoEnter( ( menuKeybind_t * )item ); + case MTYPE_FIELD: + case MTYPE_ACTION: + case MTYPE_LIST: + return Common_DoEnter( item ); + default: + return QMS_NOTHANDLED; + } +} + +menuSound_t Menu_SlideItem( menuFrameWork_t *s, int dir ) { + menuCommon_t *item; + + if( !( item = Menu_ItemAtCursor( s ) ) ) { + return QMS_NOTHANDLED; + } + + switch( item->type ) { + case MTYPE_SLIDER: + return Slider_DoSlide( (menuSlider_t *)item, dir ); + case MTYPE_SPINCONTROL: + case MTYPE_BITFIELD: + case MTYPE_PAIRS: + case MTYPE_VALUES: + case MTYPE_STRINGS: + case MTYPE_TOGGLE: + return SpinControl_DoSlide( (menuSpinControl_t *)item, dir ); + default: + return QMS_NOTHANDLED; + } +} + +menuSound_t Menu_KeyEvent( menuCommon_t *item, int key ) { + if( item->keydown ) { + menuSound_t sound = item->keydown( item, key ); + if( sound != QMS_NOTHANDLED ) { + return sound; + } + } + + switch( item->type ) { + case MTYPE_FIELD: + return Field_Key( ( menuField_t * )item, key ); + case MTYPE_LIST: + return MenuList_Key( ( menuList_t * )item, key ); + case MTYPE_SLIDER: + return Slider_Key( ( menuSlider_t * )item, key ); + case MTYPE_KEYBIND: + return Keybind_Key( ( menuKeybind_t * )item, key ); + default: + return QMS_NOTHANDLED; + } +} + +menuSound_t Menu_CharEvent( menuCommon_t *item, int key ) { + switch( item->type ) { + case MTYPE_FIELD: + return Field_Char( (menuField_t *)item, key ); + default: + return QMS_NOTHANDLED; + } +} + +menuSound_t Menu_MouseMove( menuCommon_t *item ) { + return QMS_NOTHANDLED; +} + +menuSound_t Menu_DefaultKey( menuFrameWork_t *m, int key ) { + menuCommon_t *item; + + switch( key ) { + case K_ESCAPE: + UI_PopMenu(); + return QMS_OUT; + + case K_KP_UPARROW: + case K_UPARROW: + case 'k': + return Menu_AdjustCursor( m, -1 ); + + case K_KP_DOWNARROW: + case K_DOWNARROW: + case K_TAB: + case 'j': + return Menu_AdjustCursor( m, 1 ); + + case K_KP_LEFTARROW: + case K_LEFTARROW: + case K_MWHEELDOWN: + case 'h': + return Menu_SlideItem( m, -1 ); + + case K_KP_RIGHTARROW: + case K_RIGHTARROW: + case K_MWHEELUP: + case 'l': + return Menu_SlideItem( m, 1 ); + + case K_MOUSE1: + case K_MOUSE2: + case K_MOUSE3: + item = Menu_HitTest( m ); + if( !item ) { + return QMS_NOTHANDLED; + } + + if( !( item->flags & QMF_HASFOCUS ) ) { + return QMS_NOTHANDLED; + } + + // fall through + + case K_JOY1: + case K_JOY2: + case K_JOY3: + case K_JOY4: + case K_AUX1: + case K_AUX2: + case K_AUX3: + case K_AUX4: + case K_AUX5: + case K_AUX6: + case K_AUX7: + case K_AUX8: + case K_AUX9: + case K_AUX10: + case K_AUX11: + case K_AUX12: + case K_AUX13: + case K_AUX14: + case K_AUX15: + case K_AUX16: + case K_AUX17: + case K_AUX18: + case K_AUX19: + case K_AUX20: + case K_AUX21: + case K_AUX22: + case K_AUX23: + case K_AUX24: + case K_AUX25: + case K_AUX26: + case K_AUX27: + case K_AUX28: + case K_AUX29: + case K_AUX30: + case K_AUX31: + case K_AUX32: + case K_KP_ENTER: + case K_ENTER: + return Menu_SelectItem( m ); + } + + return QMS_NOTHANDLED; +} + +menuSound_t Menu_Keydown( menuFrameWork_t *menu, int key ) { + menuCommon_t *item; + menuSound_t sound; + + if( menu->keywait ) { + } + + if( menu->keydown ) { + sound = menu->keydown( menu, key ); + if( sound != QMS_NOTHANDLED ) { + return sound; + } + } + + item = Menu_ItemAtCursor( menu ); + if( item ) { + sound = Menu_KeyEvent( item, key ); + if( sound != QMS_NOTHANDLED ) { + return sound; + } + } + + sound = Menu_DefaultKey( menu, key ); + return sound; +} + + +menuCommon_t *Menu_HitTest( menuFrameWork_t *menu ) { + int i; + menuCommon_t *item; + + if( menu->keywait ) { + return NULL; + } + + for( i = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + if( item->flags & QMF_HIDDEN ) { + continue; + } + + if( UI_CursorInRect( &item->rect ) ) { + return item; + } + } + + return NULL; +} + +qboolean Menu_Push( menuFrameWork_t *menu ) { + void *item; + int i; + + for( i = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + + switch( ((menuCommon_t *)item)->type ) { + case MTYPE_SLIDER: + Slider_Push( item ); + break; + case MTYPE_BITFIELD: + BitField_Push( item ); + break; + case MTYPE_PAIRS: + Pairs_Push( item ); + break; + case MTYPE_STRINGS: + Strings_Push( item ); + break; + case MTYPE_SPINCONTROL: + SpinControl_Push( item ); + break; + case MTYPE_TOGGLE: + Toggle_Push( item ); + break; + case MTYPE_KEYBIND: + Keybind_Push( item ); + break; + case MTYPE_FIELD: + Field_Push( item ); + break; + default: + break; + } + } + return qtrue; +} + +void Menu_Pop( menuFrameWork_t *menu ) { + void *item; + int i; + + for( i = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + + switch( ((menuCommon_t *)item)->type ) { + case MTYPE_SLIDER: + Slider_Pop( item ); + break; + case MTYPE_BITFIELD: + BitField_Pop( item ); + break; + case MTYPE_PAIRS: + Pairs_Pop( item ); + break; + case MTYPE_STRINGS: + Strings_Pop( item ); + break; + case MTYPE_SPINCONTROL: + case MTYPE_TOGGLE: + SpinControl_Pop( item ); + break; + case MTYPE_FIELD: + Field_Pop( item ); + break; + default: + break; + } + } +} + +void Menu_Free( menuFrameWork_t *menu ) { + void *item; + int i; + + for( i = 0; i < menu->nitems; i++ ) { + item = menu->items[i]; + + switch( ((menuCommon_t *)item)->type ) { + case MTYPE_ACTION: + Action_Free( item ); + break; + case MTYPE_SLIDER: + Slider_Free( item ); + break; + case MTYPE_BITFIELD: + case MTYPE_TOGGLE: + BitField_Free( item ); + break; + case MTYPE_PAIRS: + Pairs_Free( item ); + break; + case MTYPE_SPINCONTROL: + case MTYPE_STRINGS: + SpinControl_Free( item ); + break; + case MTYPE_KEYBIND: + Keybind_Free( item ); + break; + case MTYPE_FIELD: + Field_Free( item ); + break; + case MTYPE_SEPARATOR: + Z_Free( item ); + break; + default: + break; + } + } + + Z_Free( menu->title ); + Z_Free( menu->name ); + Z_Free( menu ); +} + |