/* 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 #include #include "ui_local.h" static void Field_Init( menucommon_t *f ); static void Action_DoEnter( menucommon_t *a ); static void Action_Draw( menucommon_t *a ); void Menu_DrawStatusBar( char *string ); void Menu_AdjustRectangle ( int *mins, int *maxs ); static void Separator_Draw( menucommon_t *s ); static void Slider_DoSlide( menucommon_t *s, int dir ); static void Slider_Draw( menucommon_t *s ); //static void SpinControl_DoEnter( menulist_s *s ); static void SpinControl_Draw( menucommon_t *s ); static void SpinControl_DoSlide( menucommon_t *s, int dir ); static void Scrollbar_DoSlide( menucommon_t *s, int dir ); static void Scrollbar_Draw( menucommon_t *s ); #define RCOLUMN_OFFSET 16 #define LCOLUMN_OFFSET -16 vec4_t colorWarsowOrange = { 0.95f, 0.33f, 0.1f, 1.0f }; vec4_t colorWarsowOrangeBright = { 1.0f, 0.6f, 0.1f, 1.0f }; vec4_t colorWarsowPurpleBright = { 0.66f, 0.66f, 1.0f, 1.0f }; vec4_t colorWarsowPurple = { 0.3843f, 0.2902f, 0.5843f, 1.0f }; /* =============================================================================== STRINGS DRAWING =============================================================================== */ //============= //UI_FillRect //============= void UI_FillRect( int x, int y, int w, int h, vec4_t color ) { trap_R_DrawStretchPic( x, y, w, h, 0, 0, 1, 1, color, uis.whiteShader ); } //============= //UI_StringWidth //============= int UI_StringWidth( char *s, struct mufont_s *font ) { if( !font ) font = uis.fontSystemSmall; return trap_SCR_strWidth( s, font, 0 ); } //============= //UI_StringHeight //============= int UI_StringHeight( struct mufont_s *font ) { if( !font ) font = uis.fontSystemSmall; return trap_SCR_strHeight( font ); } //============= //UISCR_HorizontalAlignOffset //============= int UISCR_HorizontalAlignOffset( int align, int width ) { int nx = 0; if( align % 3 == 0 ) // left nx = 0; if( align % 3 == 1 ) // center nx = -(width / 2); if( align % 3 == 2 ) // right nx = -width; return nx; } //============= //UISCR_VerticalAlignOffset //============= int UISCR_VerticalAlignOffset( int align, int height ) { int ny = 0; if( align / 3 == 0 ) // top ny = 0; else if( align / 3 == 1 ) // middle ny = -(height / 2); else if( align / 3 == 2 ) // bottom ny = -height; return ny; } //============= //UI_DrawStringHigh //This string is highlighted by the mouse //============= void UI_DrawStringHigh( int x, int y, int align, char *str, int maxlen, struct mufont_s *font, vec4_t color ) { int shadowoffset = 1 + (trap_SCR_strHeight(font) >= trap_SCR_strHeight(uis.fontSystemBig)); if( !font ) font = uis.fontSystemSmall; if( maxlen > 0 ) { trap_SCR_DrawStringLen( x+shadowoffset, y+shadowoffset, align, COM_RemoveColorTokens(str), maxlen, font, colorBlack ); trap_SCR_DrawStringLen( x, y, align, COM_RemoveColorTokens(str), maxlen, font, UI_COLOR_HIGHLIGHT ); } else { trap_SCR_DrawString( x+shadowoffset, y+shadowoffset, align, str, font, colorBlack ); trap_SCR_DrawString( x, y, align, str, font, UI_COLOR_HIGHLIGHT ); } } //============= //UI_DrawString //============= void UI_DrawString( int x, int y, int align, char *str, int maxlen, struct mufont_s *font, vec4_t color ) { if( !font ) font = uis.fontSystemSmall; if( maxlen > 0 ) trap_SCR_DrawStringLen( x, y, align, str, maxlen, font, color ); else trap_SCR_DrawString( x, y, align, str, font, color ); } //================================================================= // // MENUITEMS // //================================================================= //================================================================= // ACTION //================================================================= static void Action_UpdateBox( menucommon_t *a ) { a->mins[0] = a->x + a->parent->x + UISCR_HorizontalAlignOffset( a->align, UI_StringWidth( a->title, a->font ) ); if( a->width > 0 ) { a->maxs[0] = a->mins[0] + a->width; } else { a->maxs[0] = a->mins[0] + UI_StringWidth( a->title, a->font ); } a->mins[1] = a->y + a->parent->y + UISCR_VerticalAlignOffset( a->align, UI_StringHeight( a->font ) ); if( a->height > 0 ) { a->maxs[1] = a->mins[1] + a->height; } else { a->maxs[1] = a->mins[1] + UI_StringHeight( a->font ); } } static void Action_Init( menucommon_t *a ) { Action_UpdateBox( a ); } void Action_DoEnter( menucommon_t *menuitem ) { if( menuitem->callback ) menuitem->callback( menuitem ); } void Action_Draw( menucommon_t *menuitem ) { int x, y; int maxlen = 0; x = menuitem->x + menuitem->parent->x; y = menuitem->y + menuitem->parent->y; // update box for string size Action_UpdateBox( menuitem ); if( menuitem->ownerdraw ) menuitem->ownerdraw( menuitem ); if( menuitem->width > 0 ) { maxlen = trap_SCR_StrlenForWidth( COM_RemoveColorTokens(menuitem->title), menuitem->font, menuitem->width ); } if( Menu_ItemAtCursor( menuitem->parent ) == menuitem ) { UI_DrawStringHigh( x, y, menuitem->align, menuitem->title, maxlen, menuitem->font, colorWhite ); } else { UI_DrawString( x, y, menuitem->align, menuitem->title, maxlen, menuitem->font, UI_COLOR_LIVETEXT ); } } //================================================================= // FIELD //================================================================= static void Field_ResetCursor( menucommon_t *f ) { menufield_t *itemlocal; itemlocal = (menufield_t*)f->itemlocal; itemlocal->cursor = strlen(itemlocal->buffer); if( itemlocal->cursor > itemlocal->length ) itemlocal->cursor = itemlocal->length; } static void Field_SetupBox( menucommon_t *f ) { menufield_t *itemlocal; itemlocal = (menufield_t*)f->itemlocal; f->mins[0] = f->x + f->parent->x + 16; f->maxs[0] = f->mins[0] + itemlocal->width; f->mins[1] = f->y + f->parent->y; f->maxs[1] = f->mins[1] + UI_StringHeight( f->font ); } static void Field_Init( menucommon_t *f ) { Field_SetupBox( f ); } qboolean Field_DoEnter( menucommon_t *f ) { if( f->callback ) { f->callback( f ); return qtrue; } return qfalse; } void Field_Draw( menucommon_t *f ) { int x, y; char tempbuffer[128] = ""; menufield_t *itemlocal; itemlocal = (menufield_t*)f->itemlocal; if( !itemlocal ) return; Field_SetupBox( f ); // update box x = f->x + f->parent->x + LCOLUMN_OFFSET; y = f->y + f->parent->y; if( f->title[0] ) { UI_DrawString( x, y, f->align, f->title, 0, f->font, UI_COLOR_DEADTEXT ); } x = f->x + f->parent->x + 16; y = f->y + f->parent->y; { float color[4] = { 0.5, 0.5, 0.5, 0.5 }; UI_FillRect( x, y, itemlocal->width, trap_SCR_strHeight(f->font), color ); } if( Menu_ItemAtCursor( f->parent ) == f ) { int offset, xcursor; char *str; Q_strncpyz( tempbuffer, itemlocal->buffer, sizeof(tempbuffer) ); str = tempbuffer; while( *str && ((int)trap_SCR_strWidth(str, f->font, 0) > itemlocal->width - 16) ) { str++; } offset = str - tempbuffer; if( itemlocal->cursor < offset ) Field_ResetCursor( f ); // force the cursor to be always at the end of the string UI_DrawString( x, y, ALIGN_LEFT_TOP, str, 0, f->font, UI_COLOR_LIVETEXT ); //draw cursor if( ( int ) ( uis.time / 250 ) & 1 ) { xcursor = trap_SCR_strWidth(str,f->font,itemlocal->cursor); UI_DrawString( x+xcursor, y, ALIGN_LEFT_TOP, "_", 0, f->font, UI_COLOR_LIVETEXT ); } } else { x = f->x + f->parent->x + 16; y = f->y + f->parent->y; Q_strncpyz( tempbuffer, itemlocal->buffer, sizeof( tempbuffer ) ); UI_DrawString( x, y, ALIGN_LEFT_TOP, tempbuffer, 0, f->font, colorLtGrey ); } } qboolean Field_Key( menucommon_t *f, int key ) { menufield_t *itemlocal; itemlocal = (menufield_t*)f->itemlocal; if( !itemlocal ) return qfalse; /* ** support pasting from the clipboard */ if( ( toupper( key ) == 'V' && trap_Key_IsDown (K_CTRL) ) || ( ( ( key == K_INS ) || ( key == KP_INS ) ) && trap_Key_IsDown (K_SHIFT) ) ) { char cbd[64]; trap_CL_GetClipboardData ( cbd, sizeof(cbd) ); if ( cbd[0] ) { strtok( cbd, "\n\r\b" ); Q_strncpyz( itemlocal->buffer, cbd, sizeof(itemlocal->buffer) ); Field_ResetCursor( f ); //Field_DoEnter( f ); // exec callback } return qtrue; } switch ( key ) { case K_LEFTARROW: case K_BACKSPACE: if ( itemlocal->cursor > 0 ) { memmove( &itemlocal->buffer[itemlocal->cursor-1], &itemlocal->buffer[itemlocal->cursor], strlen( &itemlocal->buffer[itemlocal->cursor] ) + 1 ); Field_ResetCursor( f ); //Field_DoEnter( f ); // exec callback } return qtrue; case KP_DEL: case K_DEL: { memmove( &itemlocal->buffer[itemlocal->cursor], &itemlocal->buffer[itemlocal->cursor+1], strlen( &itemlocal->buffer[itemlocal->cursor+1] ) + 1 ); Field_ResetCursor( f ); //Field_DoEnter( f ); // exec callback } return qtrue; case KP_HOME: case KP_UPARROW: case KP_PGUP: case KP_LEFTARROW: case KP_5: case KP_RIGHTARROW: case KP_END: case KP_DOWNARROW: case KP_PGDN: return qtrue; // ignore keypad stuff, so it can be used for writing numbers (little ugly) } return qfalse; } qboolean Field_CharEvent( menucommon_t *f, int key ) { menufield_t *itemlocal; itemlocal = (menufield_t*)f->itemlocal; if( !itemlocal ) return qfalse; if (key < 32 || key > 126) return qfalse; // non printable // jalfixme: reimplement numbers only option? //if ( !isdigit( key ) && ( f->flags & QMF_NUMBERSONLY ) ) // return qfalse; if( itemlocal->cursor < itemlocal->length ) { itemlocal->buffer[itemlocal->cursor++] = key; itemlocal->buffer[itemlocal->cursor] = 0; Field_SetupBox( f ); //Field_DoEnter( f ); // exec callback } return qtrue; } //================================================================= // SEPARATOR //================================================================= static void Separator_Init( menucommon_t *s ) { } void Separator_Draw( menucommon_t *s ) { int x, y; int maxlen = 0; if( !s->title[0] ) { return; } x = s->x + s->parent->x; y = s->y + s->parent->y; if( s->width > 0 ) { maxlen = trap_SCR_StrlenForWidth( s->title, s->font, s->width ); } UI_DrawString( x, y, s->align, s->title, maxlen, s->font, UI_COLOR_DEADTEXT ); } //================================================================= // SLIDER //================================================================= #define SCROLLBAR_PIC_SIZE 16 static void Slider_Init( menucommon_t *s ) { s->align = ALIGN_LEFT_TOP; s->mins[0] = s->x + s->parent->x; s->maxs[0] = s->mins[0] + s->width * SCROLLBAR_PIC_SIZE + SCROLLBAR_PIC_SIZE; s->mins[1] = s->y + s->parent->y; s->maxs[1] = s->mins[1] + SCROLLBAR_PIC_SIZE; } void Slider_Draw( menucommon_t *s ) { int i; int x, y; vec4_t buttoncolor; int cursorxpos; Slider_Init( s ); //update box size x = s->x + s->parent->x; y = s->y + s->parent->y; //draw slider tittle, if any if( s->title[0] ) { UI_DrawString( x + LCOLUMN_OFFSET, y, ALIGN_RIGHT_TOP, s->title, 0, s->font, UI_COLOR_DEADTEXT ); } if( s->maxvalue > s->minvalue ) { s->range = (float)( s->curvalue - s->minvalue ); // reserve space for the buttons s->range /= (float)( s->maxvalue - s->minvalue ); clamp( s->range, 0, 1 ); } else s->range = 0; trap_R_DrawStretchPic( x, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/slidebar_1" ) ); for( i = 1; i < s->width-1 ; i++ ) trap_R_DrawStretchPic( x + i*SCROLLBAR_PIC_SIZE, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/slidebar_2" ) ); trap_R_DrawStretchPic( x + s->width*SCROLLBAR_PIC_SIZE - SCROLLBAR_PIC_SIZE, y , SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/slidebar_3" ) ); if( Menu_ItemAtCursor( s->parent ) == s ) { Vector4Copy( UI_COLOR_HIGHLIGHT, buttoncolor ); } else { Vector4Copy( colorWhite, buttoncolor ); } cursorxpos = x + SCROLLBAR_PIC_SIZE // start with offset for the button + ((s->width-3) * SCROLLBAR_PIC_SIZE * s->range); trap_R_DrawStretchPic( cursorxpos, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, buttoncolor, trap_R_RegisterPic( "gfx/ui/slidebar_4" ) ); } void Slider_DoSlide( menucommon_t *s, int dir ) { int min, max; float newrange; float value; //empty or erroneous if( s->width <= 0 ) return; min = s->x + s->parent->x + SCROLLBAR_PIC_SIZE; // extra pics reserved for the buttons max = min + ((s->width-1) * SCROLLBAR_PIC_SIZE) - SCROLLBAR_PIC_SIZE; //empty or erroneous if( max < min ) UI_Error( "Invalid slidebar range: 'min < max'" ); // see if the mouse is touching the step buttons if( uis.cursorX < min ) { s->curvalue--; } else if( uis.cursorX > max ) { s->curvalue++; } else { newrange = uis.cursorX - min; newrange /= (float)(max - min); clamp( newrange, 0.0, 1.0 ); value = newrange * (float)(s->maxvalue - s->minvalue) + s->minvalue; if( value - (int)value > 0.5 ) value = (int)value + 1; else value = (int)value; s->curvalue = (int)value; } if( s->curvalue > s->maxvalue ) s->curvalue = s->maxvalue; else if( s->curvalue < s->minvalue ) s->curvalue = s->minvalue; if( s->callback ) s->callback( s ); } //================================================================= // SCROLLBAR //================================================================= static void Scrollbar_Init( menucommon_t *s ) { s->align = ALIGN_LEFT_TOP; s->mins[0] = s->x + s->parent->x; s->maxs[0] = s->mins[0] + SCROLLBAR_PIC_SIZE; s->mins[1] = s->y + s->parent->y; s->maxs[1] = s->mins[1] + s->height * SCROLLBAR_PIC_SIZE + SCROLLBAR_PIC_SIZE; } void Scrollbar_DoSlide( menucommon_t *s, int dir ) { int min, max; float newrange; float value; //empty or erroneous if( s->height <= 0 ) return; min = s->y + s->parent->y + SCROLLBAR_PIC_SIZE; // extra pics reserved for the buttons max = min + ((s->height-1) * SCROLLBAR_PIC_SIZE) - SCROLLBAR_PIC_SIZE; //empty or erroneous if( max < min ) UI_Error( "Invalid scrollbar range: 'min < max'" ); // see if the mouse is touching the step buttons if( uis.cursorY < min ) { s->curvalue--; } else if( uis.cursorY > max ) { s->curvalue++; } else { newrange = uis.cursorY - min; newrange /= (float)(max - min); clamp( newrange, 0.0, 1.0 ); value = newrange * (float)(s->maxvalue - s->minvalue) + s->minvalue; if( value - (int)value > 0.5 ) value = (int)value + 1; else value = (int)value; s->curvalue = (int)value; } if( s->curvalue > s->maxvalue ) s->curvalue = s->maxvalue; else if( s->curvalue < s->minvalue ) s->curvalue = s->minvalue; if( s->callback ) s->callback( s ); } void Scrollbar_Draw( menucommon_t *s ) { int i; int x, y; vec4_t buttoncolor; int cursorypos; Scrollbar_Init( s ); //update box size x = s->x + s->parent->x; y = s->y + s->parent->y; //draw scrollbar tittle, if any if( s->title[0] ) { UI_DrawString( x, y-trap_SCR_strHeight(s->font), s->align, s->title, 0, s->font, UI_COLOR_DEADTEXT ); } if( s->maxvalue > s->minvalue ) { s->range = (float)( s->curvalue - s->minvalue ); // reserve space for the buttons s->range /= (float)( s->maxvalue - s->minvalue ); clamp( s->range, 0, 1 ); } else s->range = 0; trap_R_DrawStretchPic( x, y, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/scrollbar_1" ) ); for( i = 1; i < s->height-1 ; i++ ) trap_R_DrawStretchPic( x, y + i*SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/scrollbar_2" ) ); trap_R_DrawStretchPic( x, y + s->height*SCROLLBAR_PIC_SIZE - SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/scrollbar_3" ) ); if( Menu_ItemAtCursor( s->parent ) == s ) { Vector4Copy( UI_COLOR_HIGHLIGHT, buttoncolor ); } else { Vector4Copy( colorWhite, buttoncolor ); } cursorypos = y + SCROLLBAR_PIC_SIZE // start with offset for the button + ((s->height-3) * SCROLLBAR_PIC_SIZE * s->range); trap_R_DrawStretchPic( x, cursorypos, SCROLLBAR_PIC_SIZE, SCROLLBAR_PIC_SIZE, 0, 0, 1, 1, buttoncolor, trap_R_RegisterPic( "gfx/ui/scrollbar_4" ) ); } //================================================================= // SPINCONTROL //================================================================= static void SpinControl_Init( menucommon_t *s ) { char buffer[100] = { 0 }; int ysize, xsize, spacing, len; char **n; n = s->itemnames; if ( !n ) return; s->mins[0] = s->x + s->parent->x + RCOLUMN_OFFSET; s->mins[1] = s->y + s->parent->y; ysize = UI_StringHeight( s->font ); spacing = UI_StringHeight( s->font ); xsize = 0; while( *n ) { if( !strchr( *n, '\n' ) ) { len = UI_StringWidth( *n, s->font ); xsize = max( xsize, len ); } else { Q_strncpyz( buffer, *n, sizeof(buffer) ); *strchr( buffer, '\n' ) = 0; len = UI_StringWidth ( buffer, s->font ); xsize = max ( xsize, len ); ysize = ysize + spacing; Q_strncpyz( buffer, strchr( *n, '\n' ) + 1, sizeof(buffer) ); len = UI_StringWidth( buffer, s->font ); xsize = max( xsize, len ); } *n++; } if( s->align == ALIGN_CENTER_BOTTOM || s->align == ALIGN_CENTER_TOP || s->align == ALIGN_CENTER_MIDDLE ) { s->mins[0] -= xsize / 2; } s->maxs[0] = s->mins[0] + xsize; s->maxs[1] = s->mins[1] + ysize; } /* void SpinControl_DoEnter( menulist_s *s ) { s->curvalue++; if ( s->itemnames[s->curvalue] == 0 ) s->curvalue = 0; if ( s->generic.callback ) s->generic.callback( s ); } */ void SpinControl_DoSlide( menucommon_t *s, int dir ) { s->curvalue += dir; if( s->curvalue < 0 ) s->curvalue = s->maxvalue; else if( s->itemnames[s->curvalue] == 0 ) s->curvalue=0; if( s->callback ) s->callback( s ); return; } void SpinControl_Draw( menucommon_t *s ) { char buffer[100]; int x, y; qboolean font_highlighted = qfalse; SpinControl_Init( s ); //update box size x = s->x + s->parent->x + LCOLUMN_OFFSET; y = s->y + s->parent->y; if( s->title[0] ) { UI_DrawString( x, y, s->align, s->title, 0, s->font, UI_COLOR_DEADTEXT ); } if( Menu_ItemAtCursor( s->parent ) == s ) { font_highlighted = qtrue; } x = s->x + s->parent->x + RCOLUMN_OFFSET; y = s->y + s->parent->y; if( !strchr( s->itemnames[s->curvalue], '\n' ) ) { if( font_highlighted ) UI_DrawStringHigh( x, y, ALIGN_LEFT_TOP, ( char *)s->itemnames[s->curvalue], 0, s->font, colorWhite ); else UI_DrawString( x, y, ALIGN_LEFT_TOP, ( char *)s->itemnames[s->curvalue], 0, s->font, UI_COLOR_LIVETEXT ); } else { Q_strncpyz( buffer, s->itemnames[s->curvalue], sizeof(buffer) ); *strchr( buffer, '\n' ) = 0; if( font_highlighted ) UI_DrawStringHigh( x, y, s->align, buffer, 0, s->font, colorWhite ); else UI_DrawString( x, y, s->align, buffer, 0, s->font, UI_COLOR_LIVETEXT ); Q_strncpyz( buffer, strchr( s->itemnames[s->curvalue], '\n' ) + 1, sizeof(buffer) ); if( font_highlighted ) UI_DrawStringHigh( x, y + UI_StringHeight(s->font), ALIGN_LEFT_TOP, buffer, 0, s->font, colorWhite ); else UI_DrawString( x, y + UI_StringHeight(s->font), ALIGN_LEFT_TOP, buffer, 0, s->font, UI_COLOR_LIVETEXT ); } } //================================================================= // // MENUITEM ASSOCIATED ELEMENTS DRAWING (Generic to all types) // //================================================================= void MenuItem_DrawPict( menucommon_t *menuitem ) { int x, y; x = menuitem->x + menuitem->parent->x; y = menuitem->y + menuitem->parent->y; x += menuitem->pict.xoffset; y += menuitem->pict.yoffset; if( Menu_ItemAtCursor( menuitem->parent ) == menuitem ) { if( menuitem->pict.shaderHigh ) { trap_R_DrawStretchPic( x, y, menuitem->pict.width, menuitem->pict.height, 0, 0, 1, 1, menuitem->pict.colorHigh, menuitem->pict.shaderHigh ); return; } } if( menuitem->pict.shader ) { trap_R_DrawStretchPic( x, y, menuitem->pict.width, menuitem->pict.height, 0, 0, 1, 1, menuitem->pict.color, menuitem->pict.shader ); } } void Menu_DrawWindowedBackground( menuframework_s *menu ) { // it works, but needs to be improved int i; vec2_t mins, maxs; static vec4_t colorback = { .0f, .0f, .0f, 0.6f }; mins[0] = uis.vidWidth; mins[1] = uis.vidHeight; maxs[0] = maxs[1] = 0; for( i = 0; initems; i++ ) { if( menu->items[i]->mins[0] && menu->items[i]->mins[0] < mins[0] ) mins[0] = menu->items[i]->mins[0]; if( menu->items[i]->mins[1] && menu->items[i]->mins[1] < mins[1] ) mins[1] = menu->items[i]->mins[1]; if( menu->items[i]->maxs[0] && menu->items[i]->maxs[0] > maxs[0] ) maxs[0] = menu->items[i]->maxs[0]; if( menu->items[i]->maxs[1] && menu->items[i]->maxs[1] > maxs[1] ) maxs[1] = menu->items[i]->maxs[1]; } //extend it mins[0] -= 16; mins[1] -= 16; maxs[0] += 16; maxs[1] += 16; // add a background to the menu //trap_R_DrawStretchPic( mins[0], mins[1], maxs[0]-mins[0], maxs[1]-mins[1], // 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( "gfx/ui/novideoback") ); trap_R_DrawStretchPic( 0, mins[1], uis.vidWidth, maxs[1]-mins[1], 0, 0, 1, 1, colorback, uis.whiteShader ); } //================================================================= // // MENU // //================================================================= void Menu_AddItem( menuframework_s *menu, void *item ) { int i; qboolean found = qfalse; if( menu->nitems == 0 ) menu->nslots = 0; for( i = 0; i < menu->nitems; i++ ) { if( menu->items[i] == item ) { found = qtrue; break; } } if( !found && menu->nitems < MAXMENUITEMS ) { menu->items[menu->nitems] = item; menu->items[menu->nitems]->parent = menu; menu->nitems++; } menu->nslots = Menu_TallySlots( menu ); } /* ** 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. */ void Menu_AdjustCursor( menuframework_s *m, int dir ) { menucommon_t *citem; // see if it's in a valid spot if( m->cursor >= 0 && m->cursor < m->nitems ) { if( ( citem = Menu_ItemAtCursor( m ) ) != 0 ) { if( citem->type != MTYPE_SEPARATOR ) return; } } // it's not in a valid spot, so crawl in the direction indicated until we // find a valid spot if( dir == 1 ) { while( 1 ) { citem = Menu_ItemAtCursor( m ); if( citem ) if( citem->type != MTYPE_SEPARATOR ) break; m->cursor += dir; if( m->cursor >= m->nitems ) m->cursor = 0; } } else { while( 1 ) { citem = Menu_ItemAtCursor( m ); if( citem ) if( citem->type != MTYPE_SEPARATOR ) break; m->cursor += dir; if( m->cursor < 0 ) m->cursor = m->nitems - 1; } } } void Menu_Center( menuframework_s *menu ) { int height = 0; int i; int width = 0; // x center width = menu->items[menu->nitems-1]->x; for( i = 0; initems; i++ ) { if( menu->items[i]->x > width ) width = menu->items[i]->x; } width += 10; menu->x = ( uis.vidWidth - width ) / 2; // y center height = menu->items[menu->nitems-1]->y; for( i = 0; initems; i++ ) { if( menu->items[i]->y > height ) height = menu->items[i]->y; } height += 10; menu->y = ( uis.vidHeight - height ) / 2; } void Menu_AdjustRectangle( int *mins, int *maxs ) { mins[0] *= UI_WIDTHSCALE; maxs[0] *= UI_HEIGHTSCALE; mins[1] *= UI_WIDTHSCALE; maxs[1] *= UI_HEIGHTSCALE; } void Menu_Init( menuframework_s *menu ) { int i; // init items for( i = 0; i < menu->nitems; i++ ) { switch( menu->items[i]->type ) { case MTYPE_FIELD: Field_Init( menu->items[i] ); break; case MTYPE_SLIDER: Slider_Init( menu->items[i] ); break; case MTYPE_SPINCONTROL: SpinControl_Init( menu->items[i] ); break; case MTYPE_ACTION: Action_Init( menu->items[i] ); break; case MTYPE_SEPARATOR: Separator_Init( menu->items[i] ); break; case MTYPE_SCROLLBAR: Scrollbar_Init( menu->items[i] ); break; } Menu_AdjustRectangle( menu->items[i]->mins, menu->items[i]->maxs ); } } void Menu_Draw( menuframework_s *menu ) { int i; menucommon_t *item; if( !uis.backGround ) Menu_DrawWindowedBackground( menu ); // draw contents for( i = 0; i < menu->nitems; i++ ) { //draw associated picture MenuItem_DrawPict( menu->items[i] ); switch( menu->items[i]->type ) { case MTYPE_FIELD: Field_Draw( menu->items[i] ); break; case MTYPE_SLIDER: Slider_Draw( menu->items[i] ); break; case MTYPE_SPINCONTROL: SpinControl_Draw( menu->items[i] ); break; case MTYPE_ACTION: Action_Draw( menu->items[i] ); break; case MTYPE_SEPARATOR: Separator_Draw( menu->items[i] ); break; case MTYPE_SCROLLBAR: Scrollbar_Draw( menu->items[i] ); break; } } item = Menu_ItemAtCursor( menu ); if( item && item->cursordraw ) { item->cursordraw( item ); } else if( menu->cursordraw ) { menu->cursordraw( menu ); } if( item ) { if( item->statusbarfunc ) item->statusbarfunc( item ); else if( item->statusbar ) Menu_DrawStatusBar( item->statusbar ); else if( menu->statusbar ) Menu_DrawStatusBar( menu->statusbar ); } else if( menu->statusbar ) { Menu_DrawStatusBar( menu->statusbar ); } } void Menu_DrawStatusBar( char *string ) { int len; struct mufont_s *font = uis.fontSystemSmall; UI_FillRect( 0, uis.vidHeight-trap_SCR_strHeight(font), uis.vidWidth, trap_SCR_strHeight(font), colorDkGrey ); len = trap_SCR_StrlenForWidth( string, font, uis.vidWidth ); trap_SCR_DrawStringLen( uis.vidWidth/2, uis.vidHeight, ALIGN_CENTER_BOTTOM, string, len, font, colorWhite ); } menucommon_t *Menu_ItemAtCursor( menuframework_s *m ) { if( m->cursor < 0 || m->cursor >= m->nitems ) return NULL; return m->items[m->cursor]; } qboolean Menu_SelectItem( menuframework_s *s ) { menucommon_t *item = Menu_ItemAtCursor( s ); if( item ) { switch( item->type ) { case MTYPE_FIELD: return Field_DoEnter( item ) ; case MTYPE_ACTION: Action_DoEnter( item ); return qtrue; case MTYPE_SPINCONTROL: // SpinControl_DoEnter( item ); return qfalse; } } return qfalse; } void Menu_SetStatusBar( menuframework_s *m, char *string ) { m->statusbar = string; } qboolean Menu_SlideItem( menuframework_s *s, int dir, int key ) { menucommon_t *item = Menu_ItemAtCursor( s ); if( item ) { switch( item->type ) { case MTYPE_SLIDER: if( key == K_MOUSE1 || key == K_MOUSE2 ) dir = 0; Slider_DoSlide( item, dir ); return qtrue; case MTYPE_SPINCONTROL: SpinControl_DoSlide( item, dir ); return qtrue; case MTYPE_SCROLLBAR: Scrollbar_DoSlide( item, dir ); return qtrue; } } return qfalse; } int Menu_TallySlots( menuframework_s *menu ) { int i; int total = 0; for( i = 0; i < menu->nitems; i++ ) { total++; } return total; } //======================================================= // scroll lists management //======================================================= //================== //UI_FreeScrollItemList //================== void UI_FreeScrollItemList( m_itemslisthead_t *itemlist ) { m_listitem_t *ptr; while( itemlist->headNode ) { ptr = itemlist->headNode; itemlist->headNode = ptr->pnext; UI_Free(ptr); } itemlist->headNode = NULL; itemlist->numItems = 0; } //================== //UI_FindItemInScrollListWithId //================== m_listitem_t *UI_FindItemInScrollListWithId( m_itemslisthead_t *itemlist, int itemid ) { m_listitem_t *item; if( !itemlist->headNode ) return NULL; item = itemlist->headNode; while( item ) { if( item->id == itemid ) return item; item = item->pnext; } return NULL; } //================== //UI_AddItemToScrollList //================== void UI_AddItemToScrollList( m_itemslisthead_t *itemlist, char *name, void *data ) { m_listitem_t *newitem, *checkitem; //check for the address being already in the list checkitem = itemlist->headNode; while( checkitem ) { if( !Q_stricmp( name, checkitem->name ) ) return; checkitem = checkitem->pnext; } newitem = (m_listitem_t*)UI_Malloc( sizeof(m_listitem_t) ); Q_strncpyz( newitem->name, name, sizeof(newitem->name) ); newitem->pnext = itemlist->headNode; itemlist->headNode = newitem; newitem->id = itemlist->numItems; newitem->data = data; // update item names array itemlist->item_names[itemlist->numItems] = UI_CopyString( newitem->name ); itemlist->item_names[itemlist->numItems+1] = NULL; itemlist->numItems++; } //======================================================= // menu actions registration //======================================================= menucommon_t *ui_menuitems_headnode; menucommon_t *UI_MenuItemByName( char *name ) { menucommon_t *menuitem; if( !name ) return NULL; for( menuitem = ui_menuitems_headnode; menuitem; menuitem = menuitem->next ) { if( !Q_stricmp( menuitem->name, name ) ) return menuitem; } return NULL; } char *UI_GetMenuitemFieldBuffer( menucommon_t *menuitem ) { menufield_t *field; if( !menuitem || menuitem->type != MTYPE_FIELD ) return NULL; if( !menuitem->itemlocal ) return NULL; field = (menufield_t*)menuitem->itemlocal; return field->buffer; } menucommon_t *UI_RegisterMenuItem( const char *name, int type ) { menucommon_t *menuitem; size_t size; if( !name ) return NULL; for( menuitem = ui_menuitems_headnode; menuitem; menuitem = menuitem->next ) { if( !Q_stricmp( menuitem->name, name ) ) return menuitem; } switch( type ) { case MTYPE_SLIDER: size = 0; break; case MTYPE_ACTION: size = 0; break; case MTYPE_SPINCONTROL: size = 0; break; case MTYPE_SEPARATOR: size = 0; break; case MTYPE_FIELD: size = sizeof( menufield_t ); break; case MTYPE_SCROLLBAR: size = 0; break; default: size = 0; break; } // allocate one huge array to hold our data menuitem = UI_Malloc( sizeof( menucommon_t ) ); if( size ) menuitem->itemlocal = UI_Malloc( size ); menuitem->name = UI_CopyString( name ); menuitem->next = ui_menuitems_headnode; ui_menuitems_headnode = menuitem; return menuitem; } menucommon_t *UI_InitMenuItem( const char *name, char *tittle, int x, int y, int type, int align, struct mufont_s *font, void *callback ) { menucommon_t *menuitem; if( !name ) return NULL; menuitem = UI_RegisterMenuItem( name, type ); if( !menuitem ) return NULL; menuitem->type = type; menuitem->align = align; menuitem->x = x; menuitem->y = y; menuitem->font = font; menuitem->callback = callback; if( tittle ) Q_strncpyz( menuitem->title, tittle, MAX_STRING_CHARS ); else menuitem->title[0] = 0; // clear up associated picture trash menuitem->pict.shader = NULL; menuitem->pict.shaderHigh = NULL; Vector4Copy( colorWhite, menuitem->pict.color ); Vector4Copy( colorWhite, menuitem->pict.colorHigh ); return menuitem; } menucommon_t *UI_SetupSlider( menucommon_t *menuitem, int width, int curvalue, int minvalue, int maxvalue ) { if( !menuitem ) return NULL; menuitem->curvalue = curvalue; menuitem->minvalue = minvalue; menuitem->maxvalue = maxvalue; clamp( menuitem->curvalue, minvalue, maxvalue ); menuitem->width = width; if( menuitem->width < 3 ) menuitem->width = 3; return menuitem; } menucommon_t *UI_SetupScrollbar( menucommon_t *menuitem, int height, int curvalue, int minvalue, int maxvalue ) { if( !menuitem ) return NULL; menuitem->minvalue = minvalue; menuitem->maxvalue = maxvalue; menuitem->curvalue = curvalue; clamp( menuitem->curvalue, minvalue, maxvalue ); menuitem->height = height; if( menuitem->height < 3 ) menuitem->height = 3; return menuitem; } menucommon_t *UI_SetupSpinControl( menucommon_t *menuitem, char **item_names, int curvalue ) { int numitems; if( !menuitem || !item_names ) return NULL; //count item names numitems = 0; while( item_names[numitems] ) numitems++; menuitem->itemnames = item_names; menuitem->curvalue = curvalue; menuitem->minvalue = 0; menuitem->maxvalue = numitems-1; clamp( menuitem->curvalue, menuitem->minvalue, menuitem->maxvalue ); return menuitem; } menufield_t *UI_SetupField( menucommon_t *menuitem, char *string, size_t length, int width ) { menufield_t *field; if( !menuitem ) return NULL; field = menuitem->itemlocal; field->length = length; if( length < 1 ) length = 1; if( width >= ( 2*(int)trap_SCR_strWidth( "_", menuitem->font, 0) ) ) field->width = width; else field->width = (length+1) * trap_SCR_strWidth( "_", menuitem->font, 0); if( string ) { Q_strncpyz( field->buffer, string, sizeof(field->buffer) ); field->cursor = strlen( field->buffer ); } else { memset( field->buffer, 0, sizeof( field->buffer ) ); field->cursor = 0; } return field; }