/* 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. */ // ui_qmenu.c -- Quake menu framework #include "extdll.h" #include "basemenu.h" #include "utils.h" #include "keydefs.h" #include "menu_btnsbmp_table.h" //CR #include "ui_title_anim.h" #ifdef _DEBUG void DBG_AssertFunction( BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage ) { if( fExpr ) return; char szOut[512]; if( szMessage != NULL ) sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage ); else sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine ); HOST_ERROR( szOut ); } #endif // DEBUG int ColorStrlen( const char *str ) { const char *p; if( !str ) return 0; int len = 0; p = str; while( *p ) { if( IsColorString( p )) { p += 2; continue; } p++; len++; } return len; } int ColorPrexfixCount( const char *str ) { const char *p; if( !str ) return 0; int len = 0; p = str; while( *p ) { if( IsColorString( p )) { len += 2; p += 2; continue; } p++; } return len; } void StringConcat( char *dst, const char *src, size_t size ) { register char *d = dst; register const char *s = src; register size_t n = size; size_t dlen; if( !dst || !src || !size ) return; // find the end of dst and adjust bytes left but don't go past end while(n-- != 0 && *d != '\0') d++; dlen = d - dst; n = size - dlen; if ( n == 0 ) return; while ( *s != '\0' ) { if ( n != 1 ) { *d++ = *s; n--; } s++; } *d = '\0'; return; } char *StringCopy( const char *input ) { if( !input ) return NULL; char *out = (char *)MALLOC( strlen( input ) + 1 ); strcpy( out, input ); return out; } /* ============ COM_CompareSaves ============ */ int COM_CompareSaves( const void **a, const void **b ) { char *file1, *file2; file1 = (char *)*a; file2 = (char *)*b; int bResult; COMPARE_FILE_TIME( file2, file1, &bResult ); return bResult; } /* ============ COM_FileBase ============ */ // Extracts the base name of a file (no path, no extension, assumes '/' as path separator) void COM_FileBase ( const char *in, char *out ) { int len, start, end; len = strlen( in ); // scan backward for '.' end = len - 1; while ( end && in[end] != '.' && in[end] != '/' && in[end] != '\\' ) end--; if ( in[end] != '.' ) // no '.', copy to end end = len-1; else end--; // Found ',', copy to left of '.' // Scan backward for '/' start = len-1; while ( start >= 0 && in[start] != '/' && in[start] != '\\' ) start--; if ( in[start] != '/' && in[start] != '\\' ) start = 0; else start++; // Length of new sting len = end - start + 1; // Copy partial string strncpy( out, &in[start], len ); // Terminate it out[len] = 0; } /* =============== Info_ValueForKey Searches the string for the given key and returns the associated value, or an empty string. =============== */ char *Info_ValueForKey( const char *s, const char *key ) { static char value[MAX_INFO_STRING]; char pkey[MAX_INFO_STRING]; if ( *s == '\\' ) s++; while ( 1 ) { char *o = pkey; while ( *s != '\\' && *s != '\n' ) { if ( !*s ) return ""; *o++ = *s++; } *o = 0; s++; o = value; while ( *s != '\\' && *s != '\n' && *s ) { if ( !*s ) return ""; *o++ = *s++; } *o = 0; if ( !strcmp( key, pkey )) return value; if ( !*s ) return ""; s++; } } /* =================== Key_GetKey =================== */ int KEY_GetKey( const char *binding ) { const char *b; if ( !binding ) return -1; for ( int i = 0; i < 256; i++ ) { b = KEY_GetBinding( i ); if( !b ) continue; if( !stricmp( binding, b )) return i; } return -1; } /* ================ UI_FadeAlpha ================ */ int UI_FadeAlpha( int starttime, int endtime ) { int time, fade_time; if( starttime == 0 ) { return 0xFFFFFFFF; } time = ( gpGlobals->time * 1000 ) - starttime; if( time >= endtime ) { return 0x00FFFFFF; } // fade time is 1/4 of endtime fade_time = endtime / 4; fade_time = bound( 300, fade_time, 10000 ); int alpha; // fade out if(( endtime - time ) < fade_time ) alpha = bound( 0, (( endtime - time ) * 1.0f / fade_time ) * 255, 255 ); else alpha = 255; return PackRGBA( 255, 255, 255, alpha ); } void UI_UtilSetupPicButton( menuPicButton_s *pic, int ID ) { if( ID < 0 || ID > PC_BUTTONCOUNT ) return; // bad id #if 0 // too different results on various games. disabled pic->generic.width = PicButtonWidth( ID ) * UI_BUTTON_CHARWIDTH; #else pic->generic.width = UI_BUTTONS_WIDTH; #endif pic->generic.height = UI_BUTTONS_HEIGHT; pic->pic = uiStatic.buttonsPics[ID]; pic->button_id = ID; if( pic->pic ) // text buttons not use it pic->generic.flags|= QMF_ACT_ONRELEASE; } /* ================= UI_ScrollList_Init ================= */ void UI_ScrollList_Init( menuScrollList_s *sl ) { if( !sl->generic.name ) sl->generic.name = ""; if( sl->generic.flags & QMF_BIGFONT ) { sl->generic.charWidth = UI_BIG_CHAR_WIDTH; sl->generic.charHeight = UI_BIG_CHAR_HEIGHT; } else if( sl->generic.flags & QMF_SMALLFONT ) { sl->generic.charWidth = UI_SMALL_CHAR_WIDTH; sl->generic.charHeight = UI_SMALL_CHAR_HEIGHT; } else { if( sl->generic.charWidth < 1 ) sl->generic.charWidth = UI_MED_CHAR_WIDTH; if( sl->generic.charHeight < 1 ) sl->generic.charHeight = UI_MED_CHAR_HEIGHT; } UI_ScaleCoords( NULL, NULL, &sl->generic.charWidth, &sl->generic.charHeight ); if(!(sl->generic.flags & (QMF_LEFT_JUSTIFY|QMF_CENTER_JUSTIFY|QMF_RIGHT_JUSTIFY))) sl->generic.flags |= QMF_LEFT_JUSTIFY; if( !sl->generic.color ) sl->generic.color = uiPromptTextColor; if( !sl->generic.focusColor ) sl->generic.focusColor = uiPromptFocusColor; if( !sl->upArrow ) sl->upArrow = UI_UPARROW; if( !sl->upArrowFocus ) sl->upArrowFocus = UI_UPARROWFOCUS; if( !sl->downArrow ) sl->downArrow = UI_DOWNARROW; if( !sl->downArrowFocus ) sl->downArrowFocus = UI_DOWNARROWFOCUS; // sl->curItem = 0; sl->topItem = 0; sl->numItems = 0; // count number of items while( sl->itemNames[sl->numItems] ) sl->numItems++; // scale the center box sl->generic.x2 = sl->generic.x; sl->generic.y2 = sl->generic.y; sl->generic.width2 = sl->generic.width; sl->generic.height2 = sl->generic.height; UI_ScaleCoords( &sl->generic.x2, &sl->generic.y2, &sl->generic.width2, &sl->generic.height2 ); // calculate number of visible rows sl->numRows = (sl->generic.height2 / sl->generic.charHeight) - 2; if( sl->numRows > sl->numItems ) sl->numRows = sl->numItems; // extend the height so it has room for the arrows sl->generic.height += (sl->generic.width / 4); // calculate new Y for the control sl->generic.y -= (sl->generic.width / 8); UI_ScaleCoords( &sl->generic.x, &sl->generic.y, &sl->generic.width, &sl->generic.height ); } /* ================= UI_ScrollList_Key ================= */ const char *UI_ScrollList_Key( menuScrollList_s *sl, int key, int down ) { const char *sound = 0; int arrowWidth, arrowHeight, upX, upY, downX, downY; int i, y; if( !down ) { sl->scrollBarSliding = false; return uiSoundNull; } switch( key ) { case K_MOUSE1: if(!( sl->generic.flags & QMF_HASMOUSEFOCUS )) break; // use fixed size for arrows arrowWidth = 24; arrowHeight = 24; UI_ScaleCoords( NULL, NULL, &arrowWidth, &arrowHeight ); // glue with right top and right bottom corners upX = sl->generic.x2 + sl->generic.width2 - arrowWidth; upY = sl->generic.y2 + UI_OUTLINE_WIDTH; downX = sl->generic.x2 + sl->generic.width2 - arrowWidth; downY = sl->generic.y2 + (sl->generic.height2 - arrowHeight) - UI_OUTLINE_WIDTH; // ADAMIX if( UI_CursorInRect( sl->scrollBarX, sl->scrollBarY, sl->scrollBarWidth, sl->scrollBarHeight )) { sl->scrollBarSliding = true; break; } // ADAMIX END // Now see if either up or down has focus if( UI_CursorInRect( upX, upY, arrowWidth, arrowHeight )) { if( sl->curItem != 0 ) { sl->curItem--; sound = uiSoundMove; } else sound = uiSoundBuzz; break; } else if( UI_CursorInRect( downX, downY, arrowWidth, arrowHeight )) { if( sl->curItem != sl->numItems - 1 ) { sl->curItem++; sound = uiSoundMove; } else sound = uiSoundBuzz; break; } // see if an item has been selected y = sl->generic.y2 + sl->generic.charHeight; for( i = sl->topItem; i < sl->topItem + sl->numRows; i++, y += sl->generic.charHeight ) { if( !sl->itemNames[i] ) break; // done if( UI_CursorInRect( sl->generic.x, y, sl->generic.width, sl->generic.charHeight )) { sl->curItem = i; sound = uiSoundNull; break; } } break; case K_HOME: case K_KP_HOME: if( sl->curItem != 0 ) { sl->curItem = 0; sound = uiSoundMove; } else sound = uiSoundBuzz; break; case K_END: case K_KP_END: if( sl->curItem != sl->numItems - 1 ) { sl->curItem = sl->numItems - 1; sound = uiSoundMove; } else sound = uiSoundBuzz; break; case K_PGUP: case K_KP_PGUP: if( sl->curItem != 0 ) { sl->curItem -= 2; if( sl->curItem < 0 ) sl->curItem = 0; sound = uiSoundMove; } else sound = uiSoundBuzz; break; case K_PGDN: case K_KP_PGDN: if( sl->curItem != sl->numItems - 1 ) { sl->curItem += 2; if( sl->curItem > sl->numItems - 1 ) sl->curItem = sl->numItems - 1; sound = uiSoundMove; } else sound = uiSoundBuzz; break; case K_UPARROW: case K_KP_UPARROW: case K_MWHEELUP: if( sl->curItem != 0 ) { sl->curItem--; sound = uiSoundMove; } else sound = uiSoundBuzz; break; case K_DOWNARROW: case K_MWHEELDOWN: if( sl->numItems > 0 && sl->curItem != sl->numItems - 1 ) { sl->curItem++; sound = uiSoundMove; } else sound = uiSoundBuzz; break; } sl->topItem = sl->curItem - sl->numRows + 1; if( sl->topItem < 0 ) sl->topItem = 0; if( sl->topItem > sl->numItems - sl->numRows ) sl->topItem = sl->numItems - sl->numRows; if( sound && ( sl->generic.flags & QMF_SILENT )) sound = uiSoundNull; if( sound && sl->generic.callback ) { if( sound != uiSoundBuzz ) sl->generic.callback( sl, QM_CHANGED ); } return sound; } /* ================= UI_ScrollList_Draw ================= */ void UI_ScrollList_Draw( menuScrollList_s *sl ) { int justify; int shadow; int i, x, y, w, h; int selColor = 0xFF503818; // Red 80, Green 56, Blue 24, Alpha 255 int arrowWidth, arrowHeight, upX, upY, downX, downY; int upFocus, downFocus, scrollbarFocus; if( sl->generic.flags & QMF_LEFT_JUSTIFY ) justify = 0; else if( sl->generic.flags & QMF_CENTER_JUSTIFY ) justify = 1; else if( sl->generic.flags & QMF_RIGHT_JUSTIFY ) justify = 2; shadow = (sl->generic.flags & QMF_DROPSHADOW); // use fixed size for arrows arrowWidth = 24; arrowHeight = 24; UI_ScaleCoords( NULL, NULL, &arrowWidth, &arrowHeight ); x = sl->generic.x2; y = sl->generic.y2; w = sl->generic.width2; h = sl->generic.height2; if( !sl->background ) { // draw the opaque outlinebox first UI_FillRect( x, y, w, h, uiColorBlack ); } // hightlight the selected item if( !( sl->generic.flags & QMF_GRAYED )) { y = sl->generic.y2 + sl->generic.charHeight; for( i = sl->topItem; i < sl->topItem + sl->numRows; i++, y += sl->generic.charHeight ) { if( !sl->itemNames[i] ) break; // Done if( i == sl->curItem ) { UI_FillRect( sl->generic.x, y, sl->generic.width - arrowWidth, sl->generic.charHeight, selColor ); break; } } } if( sl->background ) { // get size and position for the center box UI_DrawPic( x, y, w, h, uiColorWhite, sl->background ); } else { x = sl->generic.x2 - UI_OUTLINE_WIDTH; y = sl->generic.y2; w = UI_OUTLINE_WIDTH; h = sl->generic.height2; // draw left UI_FillRect( x, y, w, h, uiInputFgColor ); x = sl->generic.x2 + sl->generic.width2; y = sl->generic.y2; w = UI_OUTLINE_WIDTH; h = sl->generic.height2; // draw right UI_FillRect( x, y, w, h, uiInputFgColor ); x = sl->generic.x2; y = sl->generic.y2; w = sl->generic.width2 + UI_OUTLINE_WIDTH; h = UI_OUTLINE_WIDTH; // draw top UI_FillRect( x, y, w, h, uiInputFgColor ); // draw bottom x = sl->generic.x2; y = sl->generic.y2 + sl->generic.height2 - UI_OUTLINE_WIDTH; w = sl->generic.width2 + UI_OUTLINE_WIDTH; h = UI_OUTLINE_WIDTH; UI_FillRect( x, y, w, h, uiInputFgColor ); } // glue with right top and right bottom corners upX = sl->generic.x2 + sl->generic.width2 - arrowWidth; upY = sl->generic.y2 + UI_OUTLINE_WIDTH; downX = sl->generic.x2 + sl->generic.width2 - arrowWidth; downY = sl->generic.y2 + (sl->generic.height2 - arrowHeight) - UI_OUTLINE_WIDTH; // draw the arrows base UI_FillRect( upX, upY + arrowHeight, arrowWidth, downY - upY - arrowHeight, uiInputFgColor ); // ADAMIX sl->scrollBarX = upX + sl->generic.charHeight/4; sl->scrollBarWidth = arrowWidth - sl->generic.charHeight/4; int step = (sl->numItems <= 1 ) ? 1 : (downY - upY - arrowHeight) / (sl->numItems - 1); if(((downY - upY - arrowHeight) - (((sl->numItems-1)*sl->generic.charHeight)/2)) < 2) { sl->scrollBarHeight = (downY - upY - arrowHeight) - (step * (sl->numItems-1)); sl->scrollBarY = upY + arrowHeight + (step*sl->curItem); } else { sl->scrollBarHeight = downY - upY - arrowHeight - (((sl->numItems-1) * sl->generic.charHeight) / 2); sl->scrollBarY = upY + arrowHeight + (((sl->curItem) * sl->generic.charHeight)/2); } if( sl->scrollBarSliding ) { int dist = uiStatic.cursorY - sl->scrollBarY - (sl->scrollBarHeight>>2); if((((dist / 2) > (sl->generic.charHeight / 2)) || ((dist / 2) < (sl->generic.charHeight / 2))) && sl->curItem <= (sl->numItems - 1) && sl->curItem >= 0) { if(sl->generic.callback) sl->generic.callback( sl, QM_CHANGED ); if((dist / 2) > ( sl->generic.charHeight / 2 ) && sl->curItem < ( sl->numItems - 1 )) { sl->curItem++; } if((dist / 2) < -(sl->generic.charHeight / 2) && sl->curItem > 0 ) { sl->curItem--; } } sl->topItem = sl->curItem - sl->numRows + 1; if( sl->topItem < 0 ) sl->topItem = 0; if( sl->topItem > ( sl->numItems - sl->numRows )) sl->topItem = sl->numItems - sl->numRows; } if( sl->scrollBarSliding ) { // Draw scrollbar background UI_FillRect ( sl->scrollBarX, upY + arrowHeight, sl->scrollBarWidth, downY - upY - arrowHeight, uiColorBlack); } // ADAMIX END // draw the arrows if( sl->generic.flags & QMF_GRAYED ) { UI_DrawPic( upX, upY, arrowWidth, arrowHeight, uiColorDkGrey, sl->upArrow ); UI_DrawPic( downX, downY, arrowWidth, arrowHeight, uiColorDkGrey, sl->downArrow ); } else { scrollbarFocus = UI_CursorInRect( sl->scrollBarX, sl->scrollBarY, sl->scrollBarWidth, sl->scrollBarHeight ); // special case if we sliding but lost focus if( sl->scrollBarSliding ) scrollbarFocus = true; // Draw scrollbar itself UI_FillRect( sl->scrollBarX, sl->scrollBarY, sl->scrollBarWidth, sl->scrollBarHeight, scrollbarFocus ? uiInputTextColor : uiColorBlack ); if((menuCommon_s *)sl != (menuCommon_s *)UI_ItemAtCursor(sl->generic.parent)) { UI_DrawPic( upX, upY, arrowWidth, arrowHeight, uiColorWhite, sl->upArrow ); UI_DrawPic( downX, downY, arrowWidth, arrowHeight, uiColorWhite, sl->downArrow ); } else { // see which arrow has the mouse focus upFocus = UI_CursorInRect( upX, upY, arrowWidth, arrowHeight ); downFocus = UI_CursorInRect( downX, downY, arrowWidth, arrowHeight ); if(!( sl->generic.flags & QMF_FOCUSBEHIND )) { UI_DrawPic( upX, upY, arrowWidth, arrowHeight, uiColorWhite, sl->upArrow ); UI_DrawPic( downX, downY, arrowWidth, arrowHeight, uiColorWhite, sl->downArrow ); } if( sl->generic.flags & QMF_HIGHLIGHTIFFOCUS ) { UI_DrawPic( upX, upY, arrowWidth, arrowHeight, uiColorWhite, (upFocus) ? sl->upArrowFocus : sl->upArrow ); UI_DrawPic( downX, downY, arrowWidth, arrowHeight, uiColorWhite, (downFocus) ? sl->downArrowFocus : sl->downArrow ); } else if( sl->generic.flags & QMF_PULSEIFFOCUS ) { int color; color = PackAlpha( sl->generic.color, 255 * (0.5 + 0.5 * sin( uiStatic.realTime / UI_PULSE_DIVISOR ))); UI_DrawPic( upX, upY, arrowWidth, arrowHeight, (upFocus) ? color : sl->generic.color, (upFocus) ? sl->upArrowFocus : sl->upArrow ); UI_DrawPic( downX, downY, arrowWidth, arrowHeight, (downFocus) ? color : sl->generic.color, (downFocus) ? sl->downArrowFocus : sl->downArrow ); } if( sl->generic.flags & QMF_FOCUSBEHIND ) { UI_DrawPic( upX, upY, arrowWidth, arrowHeight, sl->generic.color, sl->upArrow ); UI_DrawPic( downX, downY, arrowWidth, arrowHeight, sl->generic.color, sl->downArrow ); } } } // Draw the list x = sl->generic.x2; w = sl->generic.width2; h = sl->generic.charHeight; y = sl->generic.y2 + sl->generic.charHeight; // prevent the columns out of rectangle bounds PIC_EnableScissor( x, y, sl->generic.width - arrowWidth - uiStatic.outlineWidth, sl->generic.height ); for( i = sl->topItem; i < sl->topItem + sl->numRows; i++, y += sl->generic.charHeight ) { if( !sl->itemNames[i] ) break; // done if( sl->generic.flags & QMF_GRAYED ) { UI_DrawString( x, y, w, h, sl->itemNames[i], uiColorDkGrey, true, sl->generic.charWidth, sl->generic.charHeight, justify, shadow ); continue; // grayed } if( i != sl->curItem ) { UI_DrawString( x, y, w, h, sl->itemNames[i], sl->generic.color, false, sl->generic.charWidth, sl->generic.charHeight, justify, shadow ); continue; // no focus } if(!( sl->generic.flags & QMF_FOCUSBEHIND )) UI_DrawString( x, y, w, h, sl->itemNames[i], sl->generic.color, false, sl->generic.charWidth, sl->generic.charHeight, justify, shadow ); if( sl->generic.flags & QMF_HIGHLIGHTIFFOCUS ) UI_DrawString( x, y, w, h, sl->itemNames[i], sl->generic.focusColor, false, sl->generic.charWidth, sl->generic.charHeight, justify, shadow ); else if( sl->generic.flags & QMF_PULSEIFFOCUS ) { int color; color = PackAlpha( sl->generic.color, 255 * (0.5 + 0.5 * sin( uiStatic.realTime / UI_PULSE_DIVISOR ))); UI_DrawString( x, y, w, h, sl->itemNames[i], color, false, sl->generic.charWidth, sl->generic.charHeight, justify, shadow ); } if( sl->generic.flags & QMF_FOCUSBEHIND ) UI_DrawString( x, y, w, h, sl->itemNames[i], sl->generic.color, false, sl->generic.charWidth, sl->generic.charHeight, justify, shadow ); } PIC_DisableScissor(); } /* ================= UI_SpinControl_Init ================= */ void UI_SpinControl_Init( menuSpinControl_s *sc ) { if( !sc->generic.name ) sc->generic.name = ""; // this is also the text displayed if( sc->generic.flags & QMF_BIGFONT ) { sc->generic.charWidth = UI_BIG_CHAR_WIDTH; sc->generic.charHeight = UI_BIG_CHAR_HEIGHT; } else if( sc->generic.flags & QMF_SMALLFONT ) { sc->generic.charWidth = UI_SMALL_CHAR_WIDTH; sc->generic.charHeight = UI_SMALL_CHAR_HEIGHT; } else { if( sc->generic.charWidth < 1 ) sc->generic.charWidth = UI_MED_CHAR_WIDTH; if( sc->generic.charHeight < 1 ) sc->generic.charHeight = UI_MED_CHAR_HEIGHT; } UI_ScaleCoords( NULL, NULL, &sc->generic.charWidth, &sc->generic.charHeight ); if(!( sc->generic.flags & (QMF_LEFT_JUSTIFY|QMF_CENTER_JUSTIFY|QMF_RIGHT_JUSTIFY))) sc->generic.flags |= QMF_LEFT_JUSTIFY; if( !sc->generic.color ) sc->generic.color = uiColorHelp; if( !sc->generic.focusColor ) sc->generic.focusColor = uiPromptTextColor; if( !sc->leftArrow ) sc->leftArrow = UI_LEFTARROW; if( !sc->leftArrowFocus ) sc->leftArrowFocus = UI_LEFTARROWFOCUS; if( !sc->rightArrow ) sc->rightArrow = UI_RIGHTARROW; if( !sc->rightArrowFocus ) sc->rightArrowFocus = UI_RIGHTARROWFOCUS; // scale the center box sc->generic.x2 = sc->generic.x; sc->generic.y2 = sc->generic.y; sc->generic.width2 = sc->generic.width; sc->generic.height2 = sc->generic.height; UI_ScaleCoords( &sc->generic.x2, &sc->generic.y2, &sc->generic.width2, &sc->generic.height2 ); // extend the width so it has room for the arrows sc->generic.width += (sc->generic.height * 3); // calculate new X for the control sc->generic.x -= (sc->generic.height + (sc->generic.height/2)); UI_ScaleCoords( &sc->generic.x, &sc->generic.y, &sc->generic.width, &sc->generic.height ); } /* ================= UI_SpinControl_Key ================= */ const char *UI_SpinControl_Key( menuSpinControl_s *sc, int key, int down ) { const char *sound = 0; int arrowWidth, arrowHeight, leftX, leftY, rightX, rightY; if( !down ) return uiSoundNull; switch( key ) { case K_MOUSE1: case K_MOUSE2: if( !( sc->generic.flags & QMF_HASMOUSEFOCUS )) break; // calculate size and position for the arrows arrowWidth = sc->generic.height + (UI_OUTLINE_WIDTH * 2); arrowHeight = sc->generic.height + (UI_OUTLINE_WIDTH * 2); leftX = sc->generic.x + UI_OUTLINE_WIDTH; leftY = sc->generic.y - UI_OUTLINE_WIDTH; rightX = sc->generic.x + (sc->generic.width - arrowWidth) - UI_OUTLINE_WIDTH; rightY = sc->generic.y - UI_OUTLINE_WIDTH; // now see if either left or right arrow has focus if( UI_CursorInRect( leftX, leftY, arrowWidth, arrowHeight )) { if( sc->curValue > sc->minValue ) { sc->curValue -= sc->range; if( sc->curValue < sc->minValue ) sc->curValue = sc->minValue; sound = uiSoundMove; } else sound = uiSoundBuzz; } else if( UI_CursorInRect( rightX, rightY, arrowWidth, arrowHeight )) { if( sc->curValue < sc->maxValue ) { sc->curValue += sc->range; if( sc->curValue > sc->maxValue ) sc->curValue = sc->maxValue; sound = uiSoundMove; } else sound = uiSoundBuzz; } break; case K_LEFTARROW: case K_KP_LEFTARROW: if( sc->generic.flags & QMF_MOUSEONLY ) break; if( sc->curValue > sc->minValue ) { sc->curValue -= sc->range; if( sc->curValue < sc->minValue ) sc->curValue = sc->minValue; sound = uiSoundMove; } else sound = uiSoundBuzz; break; case K_RIGHTARROW: case K_KP_RIGHTARROW: if( sc->generic.flags & QMF_MOUSEONLY ) break; if( sc->curValue < sc->maxValue ) { sc->curValue += sc->range; if( sc->curValue > sc->maxValue ) sc->curValue = sc->maxValue; sound = uiSoundMove; } else sound = uiSoundBuzz; break; } if( sound && ( sc->generic.flags & QMF_SILENT )) sound = uiSoundNull; if( sound && sc->generic.callback ) { if( sound != uiSoundBuzz ) sc->generic.callback( sc, QM_CHANGED ); } return sound; } /* ================= UI_SpinControl_Draw ================= */ void UI_SpinControl_Draw( menuSpinControl_s *sc ) { int justify; int shadow; int x, y, w, h; int arrowWidth, arrowHeight, leftX, leftY, rightX, rightY; int leftFocus, rightFocus; if( sc->generic.flags & QMF_LEFT_JUSTIFY ) justify = 0; else if( sc->generic.flags & QMF_CENTER_JUSTIFY ) justify = 1; else if( sc->generic.flags & QMF_RIGHT_JUSTIFY ) justify = 2; shadow = (sc->generic.flags & QMF_DROPSHADOW); // calculate size and position for the arrows arrowWidth = sc->generic.height + (UI_OUTLINE_WIDTH * 2); arrowHeight = sc->generic.height + (UI_OUTLINE_WIDTH * 2); leftX = sc->generic.x + UI_OUTLINE_WIDTH; leftY = sc->generic.y - UI_OUTLINE_WIDTH; rightX = sc->generic.x + (sc->generic.width - arrowWidth) - UI_OUTLINE_WIDTH; rightY = sc->generic.y - UI_OUTLINE_WIDTH; // get size and position for the center box w = sc->generic.width2; h = sc->generic.height2; x = sc->generic.x2; y = sc->generic.y2; if( sc->background ) { UI_DrawPic( x, y, w, h, uiColorWhite, sc->background ); } else { // draw the background UI_FillRect( x, y, w, h, uiColorBlack ); // draw the rectangle UI_DrawRectangle( x, y, w, h, uiInputFgColor ); } if( sc->generic.flags & QMF_GRAYED ) { UI_DrawString( x, y, w, h, sc->generic.name, uiColorDkGrey, true, sc->generic.charWidth, sc->generic.charHeight, justify, shadow ); UI_DrawPic( leftX, leftY, arrowWidth, arrowHeight, uiColorDkGrey, sc->leftArrow ); UI_DrawPic( rightX, rightY, arrowWidth, arrowHeight, uiColorDkGrey, sc->rightArrow ); return; // grayed } if((menuCommon_s *)sc != (menuCommon_s *)UI_ItemAtCursor(sc->generic.parent )) { UI_DrawString(x, y, w, h, sc->generic.name, sc->generic.color, false, sc->generic.charWidth, sc->generic.charHeight, justify, shadow ); UI_DrawPic(leftX, leftY, arrowWidth, arrowHeight, sc->generic.color, sc->leftArrow); UI_DrawPic(rightX, rightY, arrowWidth, arrowHeight, sc->generic.color, sc->rightArrow); return; // No focus } // see which arrow has the mouse focus leftFocus = UI_CursorInRect( leftX, leftY, arrowWidth, arrowHeight ); rightFocus = UI_CursorInRect( rightX, rightY, arrowWidth, arrowHeight ); if( !( sc->generic.flags & QMF_FOCUSBEHIND )) { UI_DrawString( x, y, w, h, sc->generic.name, sc->generic.color, false, sc->generic.charWidth, sc->generic.charHeight, justify, shadow ); UI_DrawPic( leftX, leftY, arrowWidth, arrowHeight, sc->generic.color, sc->leftArrow ); UI_DrawPic( rightX, rightY, arrowWidth, arrowHeight, sc->generic.color, sc->rightArrow ); } if( sc->generic.flags & QMF_HIGHLIGHTIFFOCUS ) { UI_DrawString( x, y, w, h, sc->generic.name, sc->generic.focusColor, false, sc->generic.charWidth, sc->generic.charHeight, justify, shadow ); UI_DrawPic( leftX, leftY, arrowWidth, arrowHeight, (leftFocus) ? sc->generic.color : sc->generic.color, (leftFocus) ? sc->leftArrowFocus : sc->leftArrow ); UI_DrawPic( rightX, rightY, arrowWidth, arrowHeight, (rightFocus) ? sc->generic.color : sc->generic.color, (rightFocus) ? sc->rightArrowFocus : sc->rightArrow ); } else if( sc->generic.flags & QMF_PULSEIFFOCUS ) { int color; color = PackAlpha( sc->generic.color, 255 * (0.5 + 0.5 * sin( uiStatic.realTime / UI_PULSE_DIVISOR ))); UI_DrawString( x, y, w, h, sc->generic.name, color, false, sc->generic.charWidth, sc->generic.charHeight, justify, shadow ); UI_DrawPic( leftX, leftY, arrowWidth, arrowHeight, (leftFocus) ? color : sc->generic.color, (leftFocus) ? sc->leftArrowFocus : sc->leftArrow ); UI_DrawPic( rightX, rightY, arrowWidth, arrowHeight, (rightFocus) ? color : sc->generic.color, (rightFocus) ? sc->rightArrowFocus : sc->rightArrow ); } if( sc->generic.flags & QMF_FOCUSBEHIND ) { UI_DrawString( x, y, w, h, sc->generic.name, sc->generic.color, false, sc->generic.charWidth, sc->generic.charHeight, justify, shadow ); UI_DrawPic( leftX, leftY, arrowWidth, arrowHeight, sc->generic.color, sc->leftArrow ); UI_DrawPic( rightX, rightY, arrowWidth, arrowHeight, sc->generic.color, sc->rightArrow ); } } /* ================= UI_Slider_Init ================= */ void UI_Slider_Init( menuSlider_s *sl ) { if( !sl->generic.name ) sl->generic.name = ""; // this is also the text displayed if( !sl->generic.width ) sl->generic.width = 200; if( !sl->generic.height) sl->generic.height = 4; if( !sl->generic.color ) sl->generic.color = uiColorWhite; if( !sl->generic.focusColor ) sl->generic.focusColor = uiColorWhite; if( !sl->range ) sl->range = 1.0f; if( sl->range < 0.05f ) sl->range = 0.05f; if( sl->generic.flags & QMF_BIGFONT ) { sl->generic.charWidth = UI_BIG_CHAR_WIDTH; sl->generic.charHeight = UI_BIG_CHAR_HEIGHT; } else if( sl->generic.flags & QMF_SMALLFONT ) { sl->generic.charWidth = UI_SMALL_CHAR_WIDTH; sl->generic.charHeight = UI_SMALL_CHAR_HEIGHT; } else { if( sl->generic.charWidth < 1 ) sl->generic.charWidth = 12; if( sl->generic.charHeight < 1 ) sl->generic.charHeight = 24; } UI_ScaleCoords( NULL, NULL, &sl->generic.charWidth, &sl->generic.charHeight ); if(!(sl->generic.flags & (QMF_LEFT_JUSTIFY|QMF_CENTER_JUSTIFY|QMF_RIGHT_JUSTIFY))) sl->generic.flags |= QMF_LEFT_JUSTIFY; // scale the center box sl->generic.x2 = sl->generic.x; sl->generic.y2 = sl->generic.y; sl->generic.width2 = sl->generic.width / 5; sl->generic.height2 = 4; UI_ScaleCoords( &sl->generic.x2, &sl->generic.y2, &sl->generic.width2, &sl->generic.height2 ); UI_ScaleCoords( &sl->generic.x, &sl->generic.y, &sl->generic.width, &sl->generic.height ); sl->generic.y -= uiStatic.sliderWidth; sl->generic.height += uiStatic.sliderWidth * 2; sl->generic.y2 -= uiStatic.sliderWidth; sl->drawStep = (sl->generic.width - sl->generic.width2) / ((sl->maxValue - sl->minValue) / sl->range); sl->numSteps = ((sl->maxValue - sl->minValue) / sl->range) + 1; } /* ================= UI_Slider_Key ================= */ const char *UI_Slider_Key( menuSlider_s *sl, int key, int down ) { int sliderX; if( !down ) { if( sl->keepSlider ) { // tell menu about changes if( sl->generic.callback ) sl->generic.callback( sl, QM_CHANGED ); sl->keepSlider = false; // button released } return uiSoundNull; } switch( key ) { case K_MOUSE1: if(!( sl->generic.flags & QMF_HASMOUSEFOCUS )) return uiSoundNull; // find the current slider position sliderX = sl->generic.x2 + (sl->drawStep * (sl->curValue * sl->numSteps)); if( UI_CursorInRect( sliderX, sl->generic.y2, sl->generic.width2, sl->generic.height )) { sl->keepSlider = true; } else { int dist, numSteps; // immediately move slider into specified place dist = uiStatic.cursorX - sl->generic.x2 - (sl->generic.width2>>2); numSteps = dist / (int)sl->drawStep; sl->curValue = bound( sl->minValue, numSteps * sl->range, sl->maxValue ); // tell menu about changes if( sl->generic.callback ) sl->generic.callback( sl, QM_CHANGED ); } break; } return uiSoundNull; } /* ================= UI_Slider_Draw ================= */ void UI_Slider_Draw( menuSlider_s *sl ) { int justify; int shadow; int textHeight, sliderX; if( sl->generic.flags & QMF_LEFT_JUSTIFY ) justify = 0; else if( sl->generic.flags & QMF_CENTER_JUSTIFY ) justify = 1; else if( sl->generic.flags & QMF_RIGHT_JUSTIFY ) justify = 2; shadow = (sl->generic.flags & QMF_DROPSHADOW); if( sl->keepSlider ) { int dist, numSteps; // move slider follow the holded mouse button dist = uiStatic.cursorX - sl->generic.x2 - (sl->generic.width2>>2); numSteps = dist / (int)sl->drawStep; sl->curValue = bound( sl->minValue, numSteps * sl->range, sl->maxValue ); // tell menu about changes if( sl->generic.callback ) sl->generic.callback( sl, QM_CHANGED ); } // keep value in range sl->curValue = bound( sl->minValue, sl->curValue, sl->maxValue ); // calc slider position sliderX = sl->generic.x2 + (sl->drawStep * (sl->curValue * sl->numSteps)); UI_DrawRectangleExt( sl->generic.x, sl->generic.y + uiStatic.sliderWidth, sl->generic.width, sl->generic.height2, uiInputBgColor, uiStatic.sliderWidth ); UI_DrawPic( sliderX, sl->generic.y2, sl->generic.width2, sl->generic.height, uiColorWhite, UI_SLIDER_MAIN ); textHeight = sl->generic.y - (sl->generic.charHeight * 1.5f); UI_DrawString( sl->generic.x, textHeight, sl->generic.width, sl->generic.charHeight, sl->generic.name, uiColorHelp, true, sl->generic.charWidth, sl->generic.charHeight, justify, shadow ); } /* ================= UI_CheckBox_Init ================= */ void UI_CheckBox_Init( menuCheckBox_s *cb ) { if( !cb->generic.name ) cb->generic.name = ""; if( cb->generic.flags & QMF_BIGFONT ) { cb->generic.charWidth = UI_BIG_CHAR_WIDTH; cb->generic.charHeight = UI_BIG_CHAR_HEIGHT; } else if( cb->generic.flags & QMF_SMALLFONT ) { cb->generic.charWidth = UI_SMALL_CHAR_WIDTH; cb->generic.charHeight = UI_SMALL_CHAR_HEIGHT; } else { if( cb->generic.charWidth < 1 ) cb->generic.charWidth = 12; if( cb->generic.charHeight < 1 ) cb->generic.charHeight = 24; } UI_ScaleCoords( NULL, NULL, &cb->generic.charWidth, &cb->generic.charHeight ); if(!(cb->generic.flags & (QMF_LEFT_JUSTIFY|QMF_CENTER_JUSTIFY|QMF_RIGHT_JUSTIFY))) cb->generic.flags |= QMF_LEFT_JUSTIFY; if( !cb->emptyPic ) cb->emptyPic = UI_CHECKBOX_EMPTY; if( !cb->focusPic ) cb->focusPic = UI_CHECKBOX_FOCUS; if( !cb->checkPic ) cb->checkPic = UI_CHECKBOX_ENABLED; if( !cb->grayedPic ) cb->grayedPic = UI_CHECKBOX_GRAYED; if( !cb->generic.color ) cb->generic.color = uiColorWhite; if( !cb->generic.focusColor ) cb->generic.focusColor = uiColorWhite; if( !cb->generic.width ) cb->generic.width = 32; if( !cb->generic.height ) cb->generic.height = 32; UI_ScaleCoords( &cb->generic.x, &cb->generic.y, &cb->generic.width, &cb->generic.height ); } /* ================= UI_CheckBox_Key ================= */ const char *UI_CheckBox_Key( menuCheckBox_s *cb, int key, int down ) { const char *sound = 0; switch( key ) { case K_MOUSE1: if(!( cb->generic.flags & QMF_HASMOUSEFOCUS )) break; sound = uiSoundGlow; break; case K_ENTER: case K_KP_ENTER: if( !down ) return sound; if( cb->generic.flags & QMF_MOUSEONLY ) break; sound = uiSoundGlow; break; } if( sound && ( cb->generic.flags & QMF_SILENT )) sound = uiSoundNull; if( cb->generic.flags & QMF_ACT_ONRELEASE ) { if( sound && cb->generic.callback ) { int event; if( down ) { event = QM_PRESSED; cb->generic.bPressed = true; } else event = QM_CHANGED; if( !down ) cb->enabled = !cb->enabled; // apply on release cb->generic.callback( cb, event ); } } else if( down ) { if( sound && cb->generic.callback ) { cb->enabled = !cb->enabled; cb->generic.callback( cb, QM_CHANGED ); } } return sound; } /* ================= UI_CheckBox_Draw ================= */ void UI_CheckBox_Draw( menuCheckBox_s *cb ) { int justify; int shadow; int textOffset, y; if( cb->generic.flags & QMF_LEFT_JUSTIFY ) justify = 0; else if( cb->generic.flags & QMF_CENTER_JUSTIFY ) justify = 1; else if( cb->generic.flags & QMF_RIGHT_JUSTIFY ) justify = 2; shadow = (cb->generic.flags & QMF_DROPSHADOW); y = cb->generic.y + (cb->generic.height>>2); textOffset = cb->generic.x + (cb->generic.width * 1.7f); UI_DrawString( textOffset, y, strlen( cb->generic.name ) * cb->generic.charWidth, cb->generic.charHeight, cb->generic.name, uiColorHelp, true, cb->generic.charWidth, cb->generic.charHeight, justify, shadow ); if( cb->generic.statusText && cb->generic.flags & QMF_NOTIFY ) { int charW, charH; int x, w; charW = UI_SMALL_CHAR_WIDTH; charH = UI_SMALL_CHAR_HEIGHT; UI_ScaleCoords( NULL, NULL, &charW, &charH ); x = 250; w = UI_SMALL_CHAR_WIDTH * strlen( cb->generic.statusText ); UI_ScaleCoords( &x, NULL, &w, NULL ); x += cb->generic.x; int r, g, b; UnpackRGB( r, g, b, uiColorHelp ); TextMessageSetColor( r, g, b ); DrawConsoleString( x, cb->generic.y, cb->generic.statusText ); } if( cb->generic.flags & QMF_GRAYED ) { UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, uiColorWhite, cb->grayedPic ); return; // grayed } if(( cb->generic.flags & QMF_MOUSEONLY ) && !( cb->generic.flags & QMF_HASMOUSEFOCUS )) { if( !cb->enabled ) UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, cb->generic.color, cb->emptyPic ); else UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, cb->generic.color, cb->checkPic ); return; // no focus } if((menuCommon_s *)cb != (menuCommon_s *)UI_ItemAtCursor( cb->generic.parent )) { if( !cb->enabled ) UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, cb->generic.color, cb->emptyPic ); else UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, cb->generic.color, cb->checkPic ); return; // no focus } if( cb->generic.flags & QMF_HIGHLIGHTIFFOCUS && !cb->enabled ) { UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, cb->generic.focusColor, cb->focusPic ); } else { if( !cb->enabled ) UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, cb->generic.color, cb->emptyPic ); else UI_DrawPic( cb->generic.x, cb->generic.y, cb->generic.width, cb->generic.height, cb->generic.color, cb->checkPic ); } } /* ================= UI_Field_Init ================= */ void UI_Field_Init( menuField_s *f ) { if( !f->generic.name ) f->generic.name = ""; if( f->generic.flags & QMF_BIGFONT ) { f->generic.charWidth = UI_BIG_CHAR_WIDTH; f->generic.charHeight = UI_BIG_CHAR_HEIGHT; } else if( f->generic.flags & QMF_SMALLFONT ) { f->generic.charWidth = UI_SMALL_CHAR_WIDTH; f->generic.charHeight = UI_SMALL_CHAR_HEIGHT; } else { if( f->generic.charWidth < 1 ) f->generic.charWidth = UI_MED_CHAR_WIDTH; if( f->generic.charHeight < 1 ) f->generic.charHeight = UI_MED_CHAR_HEIGHT; } UI_ScaleCoords( NULL, NULL, &f->generic.charWidth, &f->generic.charHeight ); if( !(f->generic.flags & (QMF_LEFT_JUSTIFY|QMF_CENTER_JUSTIFY|QMF_RIGHT_JUSTIFY))) f->generic.flags |= QMF_LEFT_JUSTIFY; if( !f->generic.color ) f->generic.color = uiInputTextColor; if( !f->generic.focusColor ) f->generic.focusColor = uiInputTextColor; f->maxLength++; if( f->maxLength <= 1 || f->maxLength >= UI_MAX_FIELD_LINE ) f->maxLength = UI_MAX_FIELD_LINE - 1; UI_ScaleCoords( &f->generic.x, &f->generic.y, &f->generic.width, &f->generic.height ); // calculate number of visible characters f->widthInChars = (f->generic.width / f->generic.charWidth); f->cursor = strlen( f->buffer ); } /* ================ UI_Field_Paste ================ */ void UI_Field_Paste( void ) { char *str; int pasteLen, i; str = GET_CLIPBOARD (); if( !str ) return; // send as if typed, so insert / overstrike works properly pasteLen = strlen( str ); for( i = 0; i < pasteLen; i++ ) UI_CharEvent( str[i] ); FREE( str ); } /* ================ UI_Field_Clear ================ */ void UI_Field_Clear( menuField_s *f ) { memset( f->buffer, 0, UI_MAX_FIELD_LINE ); f->cursor = 0; f->scroll = 0; } /* ================= UI_Field_Key ================= */ const char *UI_Field_Key( menuField_s *f, int key, int down ) { int len; if( !down ) return 0; // clipboard paste if((( key == K_INS ) || ( key == K_KP_INS )) && KEY_IsDown( K_SHIFT )) { UI_Field_Paste(); return 0; } len = strlen( f->buffer ); if( key == K_INS ) { // toggle overstrike mode KEY_SetOverstrike( !KEY_GetOverstrike( )); return uiSoundNull; // handled } // previous character if( key == K_LEFTARROW ) { if( f->cursor > 0 ) f->cursor--; if( f->cursor < f->scroll ) f->scroll--; return uiSoundNull; } // next character if( key == K_RIGHTARROW ) { if( f->cursor < len ) f->cursor++; if( f->cursor >= f->scroll + f->widthInChars && f->cursor <= len ) f->scroll++; return uiSoundNull; } // first character if( key == K_HOME ) { f->cursor = 0; return uiSoundNull; } // last character if( key == K_END ) { f->cursor = len; return uiSoundNull; } if( key == K_BACKSPACE ) { if( f->cursor > 0 ) { memmove( f->buffer + f->cursor - 1, f->buffer + f->cursor, len - f->cursor + 1 ); f->cursor--; if( f->scroll ) f->scroll--; } } if( key == K_DEL ) { if( f->cursor < len ) memmove( f->buffer + f->cursor, f->buffer + f->cursor + 1, len - f->cursor ); } if( f->generic.callback ) f->generic.callback( f, QM_CHANGED ); return 0; } /* ================= UI_Field_Char ================= */ void UI_Field_Char( menuField_s *f, int key ) { int len; if( key == 'v' - 'a' + 1 ) { // ctrl-v is paste UI_Field_Paste(); return; } if( key == 'c' - 'a' + 1 ) { // ctrl-c clears the field UI_Field_Clear( f ); return; } len = strlen( f->buffer ); if( key == 'a' - 'a' + 1 ) { // ctrl-a is home f->cursor = 0; f->scroll = 0; return; } if( key == 'e' - 'a' + 1 ) { // ctrl-e is end f->cursor = len; f->scroll = f->cursor - f->widthInChars; return; } // ignore any other non printable chars if( key < 32 ) return; if( key == '^' && !( f->generic.flags & QMF_ALLOW_COLORSTRINGS )) { // ignore color key-symbol return; } if( f->generic.flags & QMF_NUMBERSONLY ) { if( key < '0' || key > '9' ) return; } if( f->generic.flags & QMF_LOWERCASE ) key = tolower( key ); else if( f->generic.flags & QMF_UPPERCASE ) key = toupper( key ); if( KEY_GetOverstrike( )) { if( f->cursor == f->maxLength - 1 ) return; f->buffer[f->cursor] = key; f->cursor++; } else { // insert mode if( len == f->maxLength - 1 ) return; // all full memmove( f->buffer + f->cursor + 1, f->buffer + f->cursor, len + 1 - f->cursor ); f->buffer[f->cursor] = key; f->cursor++; } if( f->cursor >= f->widthInChars ) f->scroll++; if( f->cursor == len + 1 ) f->buffer[f->cursor] = 0; if( f->generic.callback ) f->generic.callback( f, QM_CHANGED ); } /* ================= UI_Field_Draw ================= */ void UI_Field_Draw( menuField_s *f ) { int justify; int shadow; char text[UI_MAX_FIELD_LINE]; int len, drawLen, prestep; int cursor, x, textHeight; char cursor_char[3]; if( f->generic.flags & QMF_LEFT_JUSTIFY ) justify = 0; else if( f->generic.flags & QMF_CENTER_JUSTIFY ) justify = 1; else if( f->generic.flags & QMF_RIGHT_JUSTIFY ) justify = 2; shadow = (f->generic.flags & QMF_DROPSHADOW); cursor_char[1] = '\0'; if( KEY_GetOverstrike( )) cursor_char[0] = 11; else cursor_char[0] = 95; drawLen = f->widthInChars; len = strlen( f->buffer ) + 1; // guarantee that cursor will be visible if( len <= drawLen ) { prestep = 0; } else { if( f->scroll + drawLen > len ) { f->scroll = len - drawLen; if( f->scroll < 0 ) f->scroll = 0; } prestep = f->scroll; } if( prestep + drawLen > len ) drawLen = len - prestep; // extract characters from the field at if( drawLen >= UI_MAX_FIELD_LINE ) HOST_ERROR( "UI_Field_Draw: drawLen >= UI_MAX_FIELD_LINE\n" ); memcpy( text, f->buffer + prestep, drawLen ); text[drawLen] = 0; if( f->generic.flags & QMF_HIDEINPUT ) { for( int i = 0; i < drawLen; i++ ) if( text[i] ) text[i] = '*'; } // find cursor position x = drawLen - (ColorStrlen( text ) + 1 ); if( x < 0 ) x = 0; cursor = ( f->cursor - prestep - x ); if( cursor < 0 ) cursor = 0; if( justify == 0 ) x = f->generic.x; else if( justify == 1 ) x = f->generic.x + ((f->generic.width - (ColorStrlen( text ) * f->generic.charWidth )) / 2 ); else if( justify == 2 ) x = f->generic.x + (f->generic.width - (ColorStrlen( text ) * f->generic.charWidth )); if( f->background ) { UI_DrawPic( f->generic.x, f->generic.y, f->generic.width, f->generic.height, uiColorWhite, f->background ); } else { // draw the background UI_FillRect( f->generic.x, f->generic.y, f->generic.width, f->generic.height, uiInputBgColor ); // draw the rectangle UI_DrawRectangle( f->generic.x, f->generic.y, f->generic.width, f->generic.height, uiInputFgColor ); } textHeight = f->generic.y - (f->generic.charHeight * 1.5f); UI_DrawString( f->generic.x, textHeight, f->generic.width, f->generic.charHeight, f->generic.name, uiColorHelp, true, f->generic.charWidth, f->generic.charHeight, 0, shadow ); if( f->generic.flags & QMF_GRAYED ) { UI_DrawString( f->generic.x, f->generic.y, f->generic.width, f->generic.height, text, uiColorDkGrey, true, f->generic.charWidth, f->generic.charHeight, justify, shadow ); return; // grayed } if((menuCommon_s *)f != (menuCommon_s *)UI_ItemAtCursor( f->generic.parent )) { UI_DrawString( f->generic.x, f->generic.y, f->generic.width, f->generic.height, text, f->generic.color, false, f->generic.charWidth, f->generic.charHeight, justify, shadow ); return; // no focus } if( !( f->generic.flags & QMF_FOCUSBEHIND )) { UI_DrawString( f->generic.x, f->generic.y, f->generic.width, f->generic.height, text, f->generic.color, false, f->generic.charWidth, f->generic.charHeight, justify, shadow ); if(( uiStatic.realTime & 499 ) < 250 ) UI_DrawString( x + (cursor * f->generic.charWidth), f->generic.y, f->generic.charWidth, f->generic.height, cursor_char, f->generic.color, true, f->generic.charWidth, f->generic.charHeight, 0, shadow ); } if( f->generic.flags & QMF_HIGHLIGHTIFFOCUS ) { UI_DrawString( f->generic.x, f->generic.y, f->generic.width, f->generic.height, text, f->generic.focusColor, false, f->generic.charWidth, f->generic.charHeight, justify, shadow ); if(( uiStatic.realTime & 499 ) < 250 ) UI_DrawString( x + (cursor * f->generic.charWidth), f->generic.y, f->generic.charWidth, f->generic.height, cursor_char, f->generic.focusColor, true, f->generic.charWidth, f->generic.charHeight, 0, shadow ); } else if( f->generic.flags & QMF_PULSEIFFOCUS ) { int color; color = PackAlpha( f->generic.color, 255 * (0.5 + 0.5 * sin( uiStatic.realTime / UI_PULSE_DIVISOR ))); UI_DrawString( f->generic.x, f->generic.y, f->generic.width, f->generic.height, text, color, false, f->generic.charWidth, f->generic.charHeight, justify, shadow ); if(( uiStatic.realTime & 499 ) < 250 ) UI_DrawString( x + (cursor * f->generic.charWidth), f->generic.y, f->generic.charWidth, f->generic.height, cursor_char, color, true, f->generic.charWidth, f->generic.charHeight, 0, shadow ); } if( f->generic.flags & QMF_FOCUSBEHIND ) { UI_DrawString( f->generic.x, f->generic.y, f->generic.width, f->generic.height, text, f->generic.color, false, f->generic.charWidth, f->generic.charHeight, justify, shadow ); if(( uiStatic.realTime & 499 ) < 250 ) UI_DrawString( x + (cursor * f->generic.charWidth), f->generic.y, f->generic.charWidth, f->generic.height, cursor_char, f->generic.color, true, f->generic.charWidth, f->generic.charHeight, 0, shadow ); } } /* ================= UI_Action_Init ================= */ void UI_Action_Init( menuAction_s *a ) { if( !a->generic.name ) a->generic.name = ""; // this is also the text displayed if( a->generic.flags & QMF_BIGFONT ) { a->generic.charWidth = UI_BIG_CHAR_WIDTH; a->generic.charHeight = UI_BIG_CHAR_HEIGHT; } else if( a->generic.flags & QMF_SMALLFONT ) { a->generic.charWidth = UI_SMALL_CHAR_WIDTH; a->generic.charHeight = UI_SMALL_CHAR_HEIGHT; } else { if( a->generic.charWidth < 1 ) a->generic.charWidth = UI_MED_CHAR_WIDTH; if( a->generic.charHeight < 1 ) a->generic.charHeight = UI_MED_CHAR_HEIGHT; } if(!( a->generic.flags & ( QMF_LEFT_JUSTIFY|QMF_CENTER_JUSTIFY|QMF_RIGHT_JUSTIFY ))) a->generic.flags |= QMF_LEFT_JUSTIFY; if( !a->generic.color ) a->generic.color = uiPromptTextColor; if( !a->generic.focusColor ) a->generic.focusColor = uiPromptFocusColor; if( a->generic.width < 1 || a->generic.height < 1 ) { if( a->background ) { HIMAGE handle = PIC_Load( a->background ); a->generic.width = PIC_Width( handle ); a->generic.height = PIC_Height( handle ); } else { if( a->generic.width < 1 ) a->generic.width = a->generic.charWidth * strlen( a->generic.name ); if( a->generic.height < 1 ) a->generic.height = a->generic.charHeight * 1.5; } } UI_ScaleCoords( NULL, NULL, &a->generic.charWidth, &a->generic.charHeight ); UI_ScaleCoords( &a->generic.x, &a->generic.y, &a->generic.width, &a->generic.height ); } /* ================= UI_Action_Key ================= */ const char *UI_Action_Key( menuAction_s *a, int key, int down ) { const char *sound = 0; switch( key ) { case K_MOUSE1: if(!( a->generic.flags & QMF_HASMOUSEFOCUS )) break; sound = uiSoundLaunch; break; case K_ENTER: case K_KP_ENTER: if( !down ) return sound; if( a->generic.flags & QMF_MOUSEONLY ) break; sound = uiSoundLaunch; break; } if( sound && ( a->generic.flags & QMF_SILENT )) sound = uiSoundNull; if( a->generic.flags & QMF_ACT_ONRELEASE ) { if( sound && a->generic.callback ) { int event; if( down ) { event = QM_PRESSED; a->generic.bPressed = true; } else event = QM_ACTIVATED; a->generic.callback( a, event ); } } else if( down ) { if( sound && a->generic.callback ) a->generic.callback( a, QM_ACTIVATED ); } return sound; } /* ================= UI_Action_Draw ================= */ void UI_Action_Draw( menuAction_s *a ) { int justify; int shadow; if( a->generic.flags & QMF_LEFT_JUSTIFY ) justify = 0; else if( a->generic.flags & QMF_CENTER_JUSTIFY ) justify = 1; else if( a->generic.flags & QMF_RIGHT_JUSTIFY ) justify = 2; shadow = (a->generic.flags & QMF_DROPSHADOW); if( a->background ) UI_DrawPic( a->generic.x, a->generic.y, a->generic.width, a->generic.height, uiColorWhite, a->background ); if( a->generic.statusText && a->generic.flags & QMF_NOTIFY ) { int charW, charH; int x, w; charW = UI_SMALL_CHAR_WIDTH; charH = UI_SMALL_CHAR_HEIGHT; UI_ScaleCoords( NULL, NULL, &charW, &charH ); x = 290; w = UI_SMALL_CHAR_WIDTH * strlen( a->generic.statusText ); UI_ScaleCoords( &x, NULL, &w, NULL ); x += a->generic.x; int r, g, b; UnpackRGB( r, g, b, uiColorHelp ); TextMessageSetColor( r, g, b ); DrawConsoleString( x, a->generic.y, a->generic.statusText ); } if( a->generic.flags & QMF_GRAYED ) { UI_DrawString( a->generic.x, a->generic.y, a->generic.width, a->generic.height, a->generic.name, uiColorDkGrey, true, a->generic.charWidth, a->generic.charHeight, justify, shadow ); return; // grayed } if((menuCommon_s *)a != (menuCommon_s *)UI_ItemAtCursor( a->generic.parent )) { UI_DrawString( a->generic.x, a->generic.y, a->generic.width, a->generic.height, a->generic.name, a->generic.color, false, a->generic.charWidth, a->generic.charHeight, justify, shadow ); return; // no focus } if(!( a->generic.flags & QMF_FOCUSBEHIND )) UI_DrawString( a->generic.x, a->generic.y, a->generic.width, a->generic.height, a->generic.name, a->generic.color, false, a->generic.charWidth, a->generic.charHeight, justify, shadow ); if( a->generic.flags & QMF_HIGHLIGHTIFFOCUS ) UI_DrawString( a->generic.x, a->generic.y, a->generic.width, a->generic.height, a->generic.name, a->generic.focusColor, false, a->generic.charWidth, a->generic.charHeight, justify, shadow ); else if( a->generic.flags & QMF_PULSEIFFOCUS ) { int color; color = PackAlpha( a->generic.color, 255 * (0.5 + 0.5 * sin( uiStatic.realTime / UI_PULSE_DIVISOR ))); UI_DrawString( a->generic.x, a->generic.y, a->generic.width, a->generic.height, a->generic.name, color, false, a->generic.charWidth, a->generic.charHeight, justify, shadow ); } if( a->generic.flags & QMF_FOCUSBEHIND ) UI_DrawString( a->generic.x, a->generic.y, a->generic.width, a->generic.height, a->generic.name, a->generic.color, false, a->generic.charWidth, a->generic.charHeight, justify, shadow ); } /* ================= UI_Bitmap_Init ================= */ void UI_Bitmap_Init( menuBitmap_s *b ) { if( !b->generic.name ) b->generic.name = ""; if( !b->focusPic ) b->focusPic = b->pic; if( !b->generic.color ) b->generic.color = uiColorWhite; UI_ScaleCoords( &b->generic.x, &b->generic.y, &b->generic.width, &b->generic.height ); } /* ================= UI_Bitmap_Key ================= */ const char *UI_Bitmap_Key( menuBitmap_s *b, int key, int down ) { const char *sound = 0; switch( key ) { case K_MOUSE1: if(!( b->generic.flags & QMF_HASMOUSEFOCUS )) break; sound = uiSoundLaunch; break; case K_ENTER: case K_KP_ENTER: if( !down ) return sound; if( b->generic.flags & QMF_MOUSEONLY ) break; sound = uiSoundLaunch; break; } if( sound && ( b->generic.flags & QMF_SILENT )) sound = uiSoundNull; if( b->generic.flags & QMF_ACT_ONRELEASE ) { if( sound && b->generic.callback ) { int event; if( down ) { event = QM_PRESSED; b->generic.bPressed = true; } else event = QM_ACTIVATED; b->generic.callback( b, event ); } } else if( down ) { if( sound && b->generic.callback ) b->generic.callback( b, QM_ACTIVATED ); } return sound; } /* ================= UI_Bitmap_Draw ================= */ void UI_Bitmap_Draw( menuBitmap_s *b ) { if( b->generic.id == ID_BACKGROUND ) // background is always 0! { if( CVAR_GET_FLOAT( "cl_background" )) return; // has background map disable images // UGLY HACK for replace all backgrounds UI_DrawBackground_Callback( b ); return; } //CR if( b->generic.id == 1 ) { // don't draw banners until transition is done #ifdef TA_ALT_MODE if( UI_GetTitleTransFraction() != 10 ) return; #else if( UI_GetTitleTransFraction() < 1.0f ) return; #endif } if( b->generic.flags & QMF_GRAYED ) { UI_DrawPic( b->generic.x, b->generic.y, b->generic.width, b->generic.height, uiColorDkGrey, b->pic ); return; // grayed } if(( b->generic.flags & QMF_MOUSEONLY ) && !( b->generic.flags & QMF_HASMOUSEFOCUS )) { UI_DrawPic( b->generic.x, b->generic.y, b->generic.width, b->generic.height, b->generic.color, b->pic ); return; // no focus } if((menuCommon_s *)b != (menuCommon_s *)UI_ItemAtCursor( b->generic.parent )) { // UNDONE: only inactive bitmaps supported if( b->generic.flags & QMF_DRAW_ADDITIVE ) UI_DrawPicAdditive( b->generic.x, b->generic.y, b->generic.width, b->generic.height, b->generic.color, b->pic ); else UI_DrawPic( b->generic.x, b->generic.y, b->generic.width, b->generic.height, b->generic.color, b->pic ); return; // no focus } if(!( b->generic.flags & QMF_FOCUSBEHIND )) UI_DrawPic( b->generic.x, b->generic.y, b->generic.width, b->generic.height, b->generic.color, b->pic ); if( b->generic.flags & QMF_HIGHLIGHTIFFOCUS ) UI_DrawPic( b->generic.x, b->generic.y, b->generic.width, b->generic.height, b->generic.color, b->focusPic ); else if( b->generic.flags & QMF_PULSEIFFOCUS ) { int color; color = PackAlpha( b->generic.color, 255 * (0.5 + 0.5 * sin( uiStatic.realTime / UI_PULSE_DIVISOR ))); UI_DrawPic( b->generic.x, b->generic.y, b->generic.width, b->generic.height, color, b->focusPic ); } if( b->generic.flags & QMF_FOCUSBEHIND ) UI_DrawPic( b->generic.x, b->generic.y, b->generic.width, b->generic.height, b->generic.color, b->pic ); } /* ================= UI_PicButton_Init ================= */ void UI_PicButton_Init( menuPicButton_s *pb ) { if( !pb->generic.name ) pb->generic.name = ""; if( pb->generic.flags & QMF_BIGFONT ) { pb->generic.charWidth = UI_BIG_CHAR_WIDTH; pb->generic.charHeight = UI_BIG_CHAR_HEIGHT; } else if( pb->generic.flags & QMF_SMALLFONT ) { pb->generic.charWidth = UI_SMALL_CHAR_WIDTH; pb->generic.charHeight = UI_SMALL_CHAR_HEIGHT; } else { if( pb->generic.charWidth < 1 ) pb->generic.charWidth = UI_MED_CHAR_WIDTH; if( pb->generic.charHeight < 1 ) pb->generic.charHeight = UI_MED_CHAR_HEIGHT; } if(!( pb->generic.flags & ( QMF_LEFT_JUSTIFY|QMF_CENTER_JUSTIFY|QMF_RIGHT_JUSTIFY ))) pb->generic.flags |= QMF_LEFT_JUSTIFY; if( !pb->generic.color ) pb->generic.color = uiPromptTextColor; if( !pb->generic.focusColor ) pb->generic.focusColor = uiPromptFocusColor; if( pb->generic.width < 1 || pb->generic.height < 1 ) { if( pb->generic.width < 1 ) pb->generic.width = pb->generic.charWidth * strlen( pb->generic.name ); if( pb->generic.height < 1 ) pb->generic.height = pb->generic.charHeight * 1.5; } UI_ScaleCoords( &pb->generic.x, &pb->generic.y, &pb->generic.width, &pb->generic.height ); UI_ScaleCoords( NULL, NULL, &pb->generic.charWidth, &pb->generic.charHeight ); } /* ================= UI_PicButton_Key ================= */ const char *UI_PicButton_Key( menuPicButton_s *b, int key, int down ) { const char *sound = 0; switch( key ) { case K_MOUSE1: if(!( b->generic.flags & QMF_HASMOUSEFOCUS )) break; sound = uiSoundLaunch; break; case K_ENTER: case K_KP_ENTER: if( b->generic.flags & QMF_MOUSEONLY ) break; sound = uiSoundLaunch; break; } if( sound && ( b->generic.flags & QMF_SILENT )) sound = uiSoundNull; if( b->generic.flags & QMF_ACT_ONRELEASE ) { if( sound && b->generic.callback ) { int event; if( down ) { event = QM_PRESSED; b->generic.bPressed = true; } else event = QM_ACTIVATED; //CR UI_TACheckMenuDepth(); b->generic.callback( b, event ); UI_SetTitleAnim( AS_TO_TITLE, b ); } } else if( down ) { if( sound && b->generic.callback ) b->generic.callback( b, QM_ACTIVATED ); } return sound; } /* ================= UI_PicButton_Draw ================= */ void UI_PicButton_Draw( menuPicButton_s *item ) { int state = BUTTON_NOFOCUS; if( item->generic.flags & QMF_HASMOUSEFOCUS ) state = BUTTON_FOCUS; // make sure what cursor in rect if( item->generic.bPressed ) state = BUTTON_PRESSED; #if 0 if( item->generic.statusText && item->generic.flags & QMF_NOTIFY ) { int charW, charH; int x, w; charW = UI_SMALL_CHAR_WIDTH; charH = UI_SMALL_CHAR_HEIGHT; UI_ScaleCoords( NULL, NULL, &charW, &charH ); x = 290; w = UI_SMALL_CHAR_WIDTH * strlen( item->generic.statusText ); UI_ScaleCoords( &x, NULL, &w, NULL ); x += item->generic.x; int r, g, b; UnpackRGB( r, g, b, uiColorHelp ); TextMessageSetColor( r, g, b ); DrawConsoleString( x, item->generic.y, item->generic.statusText ); } #endif if( item->pic ) { int r, g, b, a; UnpackRGB( r, g, b, item->generic.flags & QMF_GRAYED ? uiColorDkGrey : uiColorWhite ); wrect_t rects[]= { { 0, uiStatic.buttons_width, 0, 26 }, { 0, uiStatic.buttons_width, 26, 52 }, { 0, uiStatic.buttons_width, 52, 78 } }; PIC_Set( item->pic, r, g, b, 255 ); PIC_EnableScissor( item->generic.x, item->generic.y, uiStatic.buttons_draw_width, uiStatic.buttons_draw_height - 2 ); PIC_DrawAdditive( item->generic.x, item->generic.y, uiStatic.buttons_draw_width, uiStatic.buttons_draw_height, &rects[state] ); a = (512 - (uiStatic.realTime - item->generic.lastFocusTime)) >> 1; if( state == BUTTON_NOFOCUS && a > 0 ) { PIC_Set( item->pic, r, g, b, a ); PIC_DrawAdditive( item->generic.x, item->generic.y, uiStatic.buttons_draw_width, uiStatic.buttons_draw_height, &rects[BUTTON_FOCUS] ); } PIC_DisableScissor(); } else { int justify; int shadow; if( item->generic.flags & QMF_LEFT_JUSTIFY ) justify = 0; else if( item->generic.flags & QMF_CENTER_JUSTIFY ) justify = 1; else if( item->generic.flags & QMF_RIGHT_JUSTIFY ) justify = 2; shadow = (item->generic.flags & QMF_DROPSHADOW); if( item->generic.flags & QMF_GRAYED ) { UI_DrawString( item->generic.x, item->generic.y, item->generic.width, item->generic.height, item->generic.name, uiColorDkGrey, true, item->generic.charWidth, item->generic.charHeight, justify, shadow ); return; // grayed } if((menuCommon_s *)item != (menuCommon_s *)UI_ItemAtCursor( item->generic.parent )) { UI_DrawString( item->generic.x, item->generic.y, item->generic.width, item->generic.height, item->generic.name, item->generic.color, false, item->generic.charWidth, item->generic.charHeight, justify, shadow ); return; // no focus } if(!( item->generic.flags & QMF_FOCUSBEHIND )) UI_DrawString( item->generic.x, item->generic.y, item->generic.width, item->generic.height, item->generic.name, item->generic.color, false, item->generic.charWidth, item->generic.charHeight, justify, shadow ); if( item->generic.flags & QMF_HIGHLIGHTIFFOCUS ) UI_DrawString( item->generic.x, item->generic.y, item->generic.width, item->generic.height, item->generic.name, item->generic.focusColor, false, item->generic.charWidth, item->generic.charHeight, justify, shadow ); else if( item->generic.flags & QMF_PULSEIFFOCUS ) { int color; color = PackAlpha( item->generic.color, 255 * (0.5 + 0.5 * sin( uiStatic.realTime / UI_PULSE_DIVISOR ))); UI_DrawString( item->generic.x, item->generic.y, item->generic.width, item->generic.height, item->generic.name, color, false, item->generic.charWidth, item->generic.charHeight, justify, shadow ); } if( item->generic.flags & QMF_FOCUSBEHIND ) UI_DrawString( item->generic.x, item->generic.y, item->generic.width, item->generic.height, item->generic.name, item->generic.color, false, item->generic.charWidth, item->generic.charHeight, justify, shadow ); } }