hl2_src-leak-2017/src/vgui2/src/Scheme.cpp

1533 lines
45 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <stdio.h>
#include <math.h>
#include <vgui/VGUI.h>
#include <vgui/IScheme.h>
#include <KeyValues.h>
#include <vgui/ISurface.h>
#include <vgui/IPanel.h>
#include <vgui/ISystem.h>
#include <vstdlib/IKeyValuesSystem.h>
#include <vgui/IVGui.h>
#include "tier1/utlvector.h"
#include "tier1/utlrbtree.h"
#include "tier1/utldict.h"
#include "VGUI_Border.h"
#include "ScalableImageBorder.h"
#include "ImageBorder.h"
#include "vgui_internal.h"
#include "bitmap.h"
#include "filesystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
using namespace vgui;
#define FONT_ALIAS_NAME_LENGTH 64
//-----------------------------------------------------------------------------
// Purpose: Implementation of global scheme interface
//-----------------------------------------------------------------------------
class CScheme : public IScheme
{
public:
CScheme();
// gets a string from the default settings section
virtual const char *GetResourceString(const char *stringName);
// returns a pointer to an existing border
virtual IBorder *GetBorder(const char *borderName);
// returns a pointer to an existing font
virtual HFont GetFont(const char *fontName, bool proportional);
// m_pkvColors
virtual Color GetColor( const char *colorName, Color defaultColor);
void Shutdown( bool full );
void LoadFromFile( VPANEL sizingPanel, const char *filename, const char *tag, KeyValues *inKeys );
// Gets at the scheme's name
const char *GetName() { return tag; }
const char *GetFileName() { return fileName; }
char const *GetFontName( const HFont& font );
void ReloadFontGlyphs();
VPANEL GetSizingPanel() { return m_SizingPanel; }
void SpewFonts();
bool GetFontRange( const char *fontname, int &nMin, int &nMax );
void SetFontRange( const char *fontname, int nMin, int nMax );
// Get the number of borders
virtual int GetBorderCount() const;
// Get the border at the given index
virtual IBorder *GetBorderAtIndex( int iIndex );
// Get the number of fonts
virtual int GetFontCount() const;
// Get the font at the given index
virtual HFont GetFontAtIndex( int iIndex );
// Get color data
virtual const KeyValues *GetColorData() const;
private:
const char *LookupSchemeSetting(const char *pchSetting);
const char *GetMungedFontName( const char *fontName, const char *scheme, bool proportional);
void LoadFonts();
void LoadBorders();
HFont FindFontInAliasList( const char *fontName );
int GetMinimumFontHeightForCurrentLanguage();
char fileName[256];
char tag[64];
KeyValues *m_pData;
KeyValues *m_pkvBaseSettings;
KeyValues *m_pkvColors;
int m_nColorCount;
struct SchemeBorder_t
{
IBorder *border;
int borderSymbol;
bool bSharedBorder;
};
CUtlVector<SchemeBorder_t> m_BorderList;
IBorder *m_pBaseBorder; // default border to use if others not found
KeyValues *m_pkvBorders;
#pragma pack(1)
struct fontalias_t
{
CUtlSymbol _trueFontName;
unsigned short _font : 15;
unsigned short m_bProportional : 1;
};
#pragma pack()
friend struct fontalias_t;
CUtlDict< fontalias_t, int > m_FontAliases;
VPANEL m_SizingPanel;
int m_nScreenWide;
int m_nScreenTall;
struct fontrange_t
{
int _min;
int _max;
};
CUtlDict< fontrange_t, int > m_FontRanges;
};
//-----------------------------------------------------------------------------
// Purpose: Implementation of global scheme interface
//-----------------------------------------------------------------------------
class CSchemeManager : public ISchemeManager
{
public:
CSchemeManager();
~CSchemeManager();
// loads a scheme from a file
// first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that
// tag is friendly string representing the name of the loaded scheme
virtual HScheme LoadSchemeFromFile(const char *fileName, const char *tag);
// first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that
virtual HScheme LoadSchemeFromFileEx( VPANEL sizingPanel, const char *fileName, const char *tag);
// reloads the schemes from the file
virtual void ReloadSchemes();
virtual void ReloadFonts();
// returns a handle to the default (first loaded) scheme
virtual HScheme GetDefaultScheme();
// returns a handle to the scheme identified by "tag"
virtual HScheme GetScheme(const char *tag);
// returns a pointer to an image
virtual IImage *GetImage(const char *imageName, bool hardwareFiltered);
virtual HTexture GetImageID(const char *imageName, bool hardwareFiltered);
virtual IScheme *GetIScheme( HScheme scheme );
virtual void Shutdown( bool full );
// gets the proportional coordinates for doing screen-size independant panel layouts
// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
virtual int GetProportionalScaledValue(int normalizedValue);
virtual int GetProportionalNormalizedValue(int scaledValue);
// gets the proportional coordinates for doing screen-size independant panel layouts
// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
virtual int GetProportionalScaledValueEx( HScheme scheme, int normalizedValue );
virtual int GetProportionalNormalizedValueEx( HScheme scheme, int scaledValue );
virtual bool DeleteImage( const char *pImageName );
// gets the proportional coordinates for doing screen-size independant panel layouts
// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
int GetProportionalScaledValueEx( CScheme *pScheme, int normalizedValue );
int GetProportionalNormalizedValueEx( CScheme *pScheme, int scaledValue );
void SpewFonts();
private:
int GetProportionalScaledValue_( int rootWide, int rootTall, int normalizedValue );
int GetProportionalNormalizedValue_( int rootWide, int rootTall, int scaledValue );
// Search for already-loaded schemes
HScheme FindLoadedScheme(const char *fileName);
CUtlVector<CScheme *> m_Schemes;
static const char *s_pszSearchString;
struct CachedBitmapHandle_t
{
Bitmap *pBitmap;
};
static bool BitmapHandleSearchFunc(const CachedBitmapHandle_t &, const CachedBitmapHandle_t &);
CUtlRBTree<CachedBitmapHandle_t, int> m_Bitmaps;
};
const char *CSchemeManager::s_pszSearchString = NULL;
//-----------------------------------------------------------------------------
// Purpose: search function for stored bitmaps
//-----------------------------------------------------------------------------
bool CSchemeManager::BitmapHandleSearchFunc(const CachedBitmapHandle_t &lhs, const CachedBitmapHandle_t &rhs)
{
// a NULL bitmap indicates to use the search string instead
if (lhs.pBitmap && rhs.pBitmap)
{
return stricmp(lhs.pBitmap->GetName(), rhs.pBitmap->GetName()) > 0;
}
else if (lhs.pBitmap)
{
return stricmp(lhs.pBitmap->GetName(), s_pszSearchString) > 0;
}
return stricmp(s_pszSearchString, rhs.pBitmap->GetName()) > 0;
}
CSchemeManager g_Scheme;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CSchemeManager, ISchemeManager, VGUI_SCHEME_INTERFACE_VERSION, g_Scheme);
namespace vgui
{
vgui::ISchemeManager *g_pScheme = &g_Scheme;
} // namespace vgui
CON_COMMAND( vgui_spew_fonts, "" )
{
g_Scheme.SpewFonts();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSchemeManager::CSchemeManager()
{
// 0th element is null, since that would be an invalid handle
CScheme *nullScheme = new CScheme();
m_Schemes.AddToTail(nullScheme);
m_Bitmaps.SetLessFunc(&BitmapHandleSearchFunc);
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CSchemeManager::~CSchemeManager()
{
int i;
for ( i = 0; i < m_Schemes.Count(); i++ )
{
delete m_Schemes[i];
}
m_Schemes.RemoveAll();
for ( i = 0; i < m_Bitmaps.MaxElement(); i++ )
{
if (m_Bitmaps.IsValidIndex(i))
{
delete m_Bitmaps[i].pBitmap;
}
}
m_Bitmaps.RemoveAll();
Shutdown( false );
}
//-----------------------------------------------------------------------------
// Purpose: Reload the fonts in all schemes
//-----------------------------------------------------------------------------
void CSchemeManager::ReloadFonts()
{
for (int i = 1; i < m_Schemes.Count(); i++)
{
m_Schemes[i]->ReloadFontGlyphs();
}
}
//-----------------------------------------------------------------------------
// Converts the handle into an interface
//-----------------------------------------------------------------------------
IScheme *CSchemeManager::GetIScheme( HScheme scheme )
{
if ( scheme >= (unsigned long)m_Schemes.Count() )
{
AssertOnce( !"Invalid scheme requested." );
return NULL;
}
else
{
return m_Schemes[scheme];
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSchemeManager::Shutdown( bool full )
{
// Full shutdown kills the null scheme
for( int i = full ? 0 : 1; i < m_Schemes.Count(); i++ )
{
m_Schemes[i]->Shutdown( full );
}
if ( full )
{
m_Schemes.RemoveAll();
}
}
//-----------------------------------------------------------------------------
// Purpose: loads a scheme from disk
//-----------------------------------------------------------------------------
HScheme CSchemeManager::FindLoadedScheme(const char *fileName)
{
// Find the scheme in the list of already loaded schemes
for (int i = 1; i < m_Schemes.Count(); i++)
{
char const *schemeFileName = m_Schemes[i]->GetFileName();
if (!stricmp(schemeFileName, fileName))
return i;
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CScheme::CScheme()
{
fileName[ 0 ] = 0;
tag[ 0 ] = 0;
m_pData = NULL;
m_pkvBaseSettings = NULL;
m_pkvColors = NULL;
m_nColorCount = 0;
m_pBaseBorder = NULL; // default border to use if others not found
m_pkvBorders = NULL;
m_SizingPanel = 0;
m_nScreenWide = -1;
m_nScreenTall = -1;
}
// first scheme loaded becomes the default scheme, and all subsequent loaded scheme are derivitives of that
HScheme CSchemeManager::LoadSchemeFromFileEx( VPANEL sizingPanel, const char *fileName, const char *tag)
{
// Look to see if we've already got this scheme...
HScheme hScheme = FindLoadedScheme(fileName);
if (hScheme != 0)
{
CScheme *pScheme = static_cast< CScheme * >( GetIScheme( hScheme ) );
if ( IsPC() && pScheme )
{
pScheme->ReloadFontGlyphs();
}
return hScheme;
}
KeyValues *data;
data = new KeyValues("Scheme");
data->UsesEscapeSequences( true ); // VGUI uses this
// Look first in game directory
bool result = data->LoadFromFile( g_pFullFileSystem, fileName, "GAME" );
if ( !result )
{
// look in any directory
result = data->LoadFromFile( g_pFullFileSystem, fileName, NULL );
}
if (!result)
{
data->deleteThis();
return 0;
}
if ( IsX360() )
{
data->ProcessResolutionKeys( g_pSurface->GetResolutionKey() );
}
if ( IsPC() )
{
ConVarRef cl_hud_minmode( "cl_hud_minmode", true );
if ( cl_hud_minmode.IsValid() && cl_hud_minmode.GetBool() )
{
data->ProcessResolutionKeys( "_minmode" );
}
}
if( g_pIVgui->GetVRMode() )
{
data->ProcessResolutionKeys( "_vrmode" );
}
CScheme *newScheme = new CScheme();
newScheme->LoadFromFile( sizingPanel, fileName, tag, data );
return m_Schemes.AddToTail(newScheme);
}
//-----------------------------------------------------------------------------
// Purpose: loads a scheme from disk
//-----------------------------------------------------------------------------
HScheme CSchemeManager::LoadSchemeFromFile(const char *fileName, const char *tag)
{
return LoadSchemeFromFileEx( 0, fileName, tag );
}
//-----------------------------------------------------------------------------
// Purpose: Table of scheme file entries, translation from old goldsrc schemes to new src schemes
//-----------------------------------------------------------------------------
struct SchemeEntryTranslation_t
{
const char *pchNewEntry;
const char *pchOldEntry;
const char *pchDefaultValue;
};
SchemeEntryTranslation_t g_SchemeTranslation[] =
{
{ "Border.Bright", "BorderBright", "200 200 200 196" }, // the lit side of a control
{ "Border.Dark" "BorderDark", "40 40 40 196" }, // the dark/unlit side of a control
{ "Border.Selection" "BorderSelection", "0 0 0 196" }, // the additional border color for displaying the default/selected button
{ "Button.TextColor", "ControlFG", "White" },
{ "Button.BgColor", "ControlBG", "Blank" },
{ "Button.ArmedTextColor", "ControlFG" },
{ "Button.ArmedBgColor", "ControlBG" },
{ "Button.DepressedTextColor", "ControlFG" },
{ "Button.DepressedBgColor", "ControlBG" },
{ "Button.FocusBorderColor", "0 0 0 255" },
{ "CheckButton.TextColor", "BaseText" },
{ "CheckButton.SelectedTextColor", "BrightControlText" },
{ "CheckButton.BgColor", "CheckBgColor" },
{ "CheckButton.Border1", "CheckButtonBorder1" },
{ "CheckButton.Border2", "CheckButtonBorder2" },
{ "CheckButton.Check", "CheckButtonCheck" },
{ "ComboBoxButton.ArrowColor", "LabelDimText" },
{ "ComboBoxButton.ArmedArrowColor", "MenuButton/ArmedArrowColor" },
{ "ComboBoxButton.BgColor", "MenuButton/ButtonBgColor" },
{ "ComboBoxButton.DisabledBgColor", "ControlBG" },
{ "Frame.TitleTextInsetX", NULL, "32" },
{ "Frame.ClientInsetX", NULL, "8" },
{ "Frame.ClientInsetY", NULL, "6" },
{ "Frame.BgColor", "BgColor" },
{ "Frame.OutOfFocusBgColor", "BgColor" },
{ "Frame.FocusTransitionEffectTime",NULL, "0" },
{ "Frame.TransitionEffectTime", NULL, "0" },
{ "Frame.AutoSnapRange", NULL, "8" },
{ "FrameGrip.Color1", "BorderBright" },
{ "FrameGrip.Color2", "BorderSelection" },
{ "FrameTitleButton.FgColor", "TitleButtonFgColor" },
{ "FrameTitleButton.BgColor", "TitleButtonBgColor" },
{ "FrameTitleButton.DisabledFgColor", "TitleButtonDisabledFgColor" },
{ "FrameTitleButton.DisabledBgColor", "TitleButtonDisabledBgColor" },
{ "FrameSystemButton.FgColor", "TitleBarBgColor" },
{ "FrameSystemButton.BgColor", "TitleBarBgColor" },
{ "FrameSystemButton.Icon", "TitleBarIcon" },
{ "FrameSystemButton.DisabledIcon", "TitleBarDisabledIcon" },
{ "FrameTitleBar.Font", NULL, "Default" },
{ "FrameTitleBar.TextColor", "TitleBarFgColor" },
{ "FrameTitleBar.BgColor", "TitleBarBgColor" },
{ "FrameTitleBar.DisabledTextColor","TitleBarDisabledFgColor" },
{ "FrameTitleBar.DisabledBgColor", "TitleBarDisabledBgColor" },
{ "GraphPanel.FgColor", "BrightControlText" },
{ "GraphPanel.BgColor", "WindowBgColor" },
{ "Label.TextDullColor", "LabelDimText" },
{ "Label.TextColor", "BaseText" },
{ "Label.TextBrightColor", "BrightControlText" },
{ "Label.SelectedTextColor", "BrightControlText" },
{ "Label.BgColor", "LabelBgColor" },
{ "Label.DisabledFgColor1", "DisabledFgColor1" },
{ "Label.DisabledFgColor2", "DisabledFgColor2" },
{ "ListPanel.TextColor", "WindowFgColor" },
{ "ListPanel.TextBgColor", "Menu/ArmedBgColor" },
{ "ListPanel.BgColor", "ListBgColor" },
{ "ListPanel.SelectedTextColor", "ListSelectionFgColor" },
{ "ListPanel.SelectedBgColor", "Menu/ArmedBgColor" },
{ "ListPanel.SelectedOutOfFocusBgColor","SelectionBG2" },
{ "ListPanel.EmptyListInfoTextColor", "LabelDimText" },
{ "ListPanel.DisabledTextColor", "LabelDimText" },
{ "ListPanel.DisabledSelectedTextColor","ListBgColor" },
{ "Menu.TextColor", "Menu/FgColor" },
{ "Menu.BgColor", "Menu/BgColor" },
{ "Menu.ArmedTextColor", "Menu/ArmedFgColor" },
{ "Menu.ArmedBgColor", "Menu/ArmedBgColor" },
{ "Menu.TextInset", NULL, "6" },
{ "Panel.FgColor", "FgColor" },
{ "Panel.BgColor", "BgColor" },
{ "ProgressBar.FgColor", "BrightControlText" },
{ "ProgressBar.BgColor", "WindowBgColor" },
{ "PropertySheet.TextColor", "FgColorDim" },
{ "PropertySheet.SelectedTextColor", "BrightControlText" },
{ "PropertySheet.TransitionEffectTime", NULL, "0" },
{ "RadioButton.TextColor", "FgColor" },
{ "RadioButton.SelectedTextColor", "BrightControlText" },
{ "RichText.TextColor", "WindowFgColor" },
{ "RichText.BgColor", "WindowBgColor" },
{ "RichText.SelectedTextColor", "SelectionFgColor" },
{ "RichText.SelectedBgColor", "SelectionBgColor" },
{ "ScrollBar.Wide", NULL, "19" },
{ "ScrollBarButton.FgColor", "DimBaseText" },
{ "ScrollBarButton.BgColor", "ControlBG" },
{ "ScrollBarButton.ArmedFgColor", "BaseText" },
{ "ScrollBarButton.ArmedBgColor", "ControlBG" },
{ "ScrollBarButton.DepressedFgColor", "BaseText" },
{ "ScrollBarButton.DepressedBgColor", "ControlBG" },
{ "ScrollBarSlider.FgColor", "ScrollBarSlider/ScrollBarSliderFgColor" },
{ "ScrollBarSlider.BgColor", "ScrollBarSlider/ScrollBarSliderBgColor" },
{ "SectionedListPanel.HeaderTextColor", "SectionTextColor" },
{ "SectionedListPanel.HeaderBgColor", "BuddyListBgColor" },
{ "SectionedListPanel.DividerColor", "SectionDividerColor" },
{ "SectionedListPanel.TextColor", "BuddyButton/FgColor1" },
{ "SectionedListPanel.BrightTextColor", "BuddyButton/ArmedFgColor1" },
{ "SectionedListPanel.BgColor", "BuddyListBgColor" },
{ "SectionedListPanel.SelectedTextColor", "BuddyButton/ArmedFgColor1" },
{ "SectionedListPanel.SelectedBgColor", "BuddyButton/ArmedBgColor" },
{ "SectionedListPanel.OutOfFocusSelectedTextColor", "BuddyButton/ArmedFgColor2" },
{ "SectionedListPanel.OutOfFocusSelectedBgColor", "SelectionBG2" },
{ "Slider.NobColor", "SliderTickColor" },
{ "Slider.TextColor", "Slider/SliderFgColor" },
{ "Slider.TrackColor", "SliderTrackColor"},
{ "Slider.DisabledTextColor1", "DisabledFgColor1" },
{ "Slider.DisabledTextColor2", "DisabledFgColor2" },
{ "TextEntry.TextColor", "WindowFgColor" },
{ "TextEntry.BgColor", "WindowBgColor" },
{ "TextEntry.CursorColor", "TextCursorColor" },
{ "TextEntry.DisabledTextColor","WindowDisabledFgColor" },
{ "TextEntry.DisabledBgColor", "ControlBG" },
{ "TextEntry.SelectedTextColor","SelectionFgColor" },
{ "TextEntry.SelectedBgColor", "SelectionBgColor" },
{ "TextEntry.OutOfFocusSelectedBgColor", "SelectionBG2" },
{ "TextEntry.FocusEdgeColor", "BorderSelection" },
{ "ToggleButton.SelectedTextColor", "BrightControlText" },
{ "Tooltip.TextColor", "BorderSelection" },
{ "Tooltip.BgColor", "SelectionBG" },
{ "TreeView.BgColor", "ListBgColor" },
{ "WizardSubPanel.BgColor", "SubPanelBgColor" },
};
//-----------------------------------------------------------------------------
// Purpose: loads a scheme from from disk into memory
//-----------------------------------------------------------------------------
void CScheme::LoadFromFile( VPANEL sizingPanel, const char *inFilename, const char *inTag, KeyValues *inKeys )
{
COM_TimestampedLog( "CScheme::LoadFromFile( %s )", inFilename );
Q_strncpy(fileName, inFilename, sizeof(fileName) );
m_SizingPanel = sizingPanel;
m_pData = inKeys;
m_pkvBaseSettings = m_pData->FindKey("BaseSettings", true);
m_pkvColors = m_pData->FindKey("Colors", true);
// override the scheme name with the tag name
KeyValues *name = m_pData->FindKey("Name", true);
name->SetString("Name", inTag);
if ( inTag )
{
Q_strncpy( tag, inTag, sizeof( tag ) );
}
else
{
Assert( "You need to name the scheme!" );
Q_strncpy( tag, "default", sizeof( tag ) );
}
// translate format from goldsrc scheme to new scheme
for (int i = 0; i < ARRAYSIZE(g_SchemeTranslation); i++)
{
if (!m_pkvBaseSettings->FindKey(g_SchemeTranslation[i].pchNewEntry, false))
{
const char *pchColor;
if (g_SchemeTranslation[i].pchOldEntry)
{
pchColor = LookupSchemeSetting(g_SchemeTranslation[i].pchOldEntry);
}
else
{
pchColor = g_SchemeTranslation[i].pchDefaultValue;
}
Assert( pchColor );
m_pkvBaseSettings->SetString(g_SchemeTranslation[i].pchNewEntry, pchColor);
}
}
// need to copy tag before loading fonts
LoadFonts();
LoadBorders();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CScheme::GetFontRange( const char *fontname, int &nMin, int &nMax )
{
int i = m_FontRanges.Find( fontname );
if ( i != m_FontRanges.InvalidIndex() )
{
nMin = m_FontRanges[i]._min;
nMax = m_FontRanges[i]._max;
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CScheme::SetFontRange( const char *fontname, int nMin, int nMax )
{
int i = m_FontRanges.Find( fontname );
if ( i != m_FontRanges.InvalidIndex() )
{
m_FontRanges[i]._min = nMin;
m_FontRanges[i]._max = nMax;
return;
}
// not already in our list
int iNew = m_FontRanges.Insert( fontname );
m_FontRanges[iNew]._min = nMin;
m_FontRanges[iNew]._max = nMax;
}
//-----------------------------------------------------------------------------
// Purpose: adds all the font specifications to the surface
//-----------------------------------------------------------------------------
void CScheme::LoadFonts()
{
bool bValid = false;
char language[64];
memset( language, 0, sizeof( language ) );
// get our language
if ( IsPC() )
{
bValid = vgui::g_pSystem->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof( language ) - 1 );
}
else
{
Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) );
bValid = true;
}
if ( !bValid )
{
Q_strncpy( language, "english", sizeof( language ) );
}
// add our custom fonts
for (KeyValues *kv = m_pData->FindKey("CustomFontFiles", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
{
const char *fontFile = kv->GetString();
if (fontFile && *fontFile)
{
g_pSurface->AddCustomFontFile( NULL, fontFile );
}
else
{
// we have a block to read
int nRangeMin = 0, nRangeMax = 0;
const char *pszName = NULL;
bool bUseRange = false;
for ( KeyValues *pData = kv->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() )
{
const char *pszKey = pData->GetName();
if ( !Q_stricmp( pszKey, "font" ) )
{
fontFile = pData->GetString();
}
else if ( !Q_stricmp( pszKey, "name" ) )
{
pszName = pData->GetString();
}
else
{
// we must have a language
if ( Q_stricmp( language, pszKey ) == 0 ) // matches the language we're running?
{
// get the range
KeyValues *pRange = pData->FindKey( "range" );
if ( pRange )
{
bUseRange = true;
sscanf( pRange->GetString(), "%x %x", &nRangeMin, &nRangeMax );
if ( nRangeMin > nRangeMax )
{
int nTemp = nRangeMin;
nRangeMin = nRangeMax;
nRangeMax = nTemp;
}
}
}
}
}
if ( fontFile && *fontFile )
{
g_pSurface->AddCustomFontFile( pszName, fontFile );
if ( bUseRange )
{
SetFontRange( pszName, nRangeMin, nRangeMax );
}
}
}
}
// add bitmap fonts
for (KeyValues *kv = m_pData->FindKey("BitmapFontFiles", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
{
const char *fontFile = kv->GetString();
if (fontFile && *fontFile)
{
bool bSuccess = g_pSurface->AddBitmapFontFile( fontFile );
if ( bSuccess )
{
// refer to the font by a user specified symbol
g_pSurface->SetBitmapFontName( kv->GetName(), fontFile );
}
}
}
// create the fonts
for (KeyValues *kv = m_pData->FindKey("Fonts", true)->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
{
for ( int i = 0; i < 2; i++ )
{
// create the base font
bool proportionalFont = static_cast<bool>( i );
const char *fontName = GetMungedFontName( kv->GetName(), tag, proportionalFont ); // first time it adds a normal font, and then a proportional one
HFont font = g_pSurface->CreateFont();
int j = m_FontAliases.Insert( fontName );
m_FontAliases[j]._trueFontName = kv->GetName();
m_FontAliases[j]._font = font;
m_FontAliases[j].m_bProportional = proportionalFont;
}
}
// load in the font glyphs
ReloadFontGlyphs();
}
//-----------------------------------------------------------------------------
// Purpose: Reloads all scheme fonts
//-----------------------------------------------------------------------------
void CScheme::ReloadFontGlyphs()
{
COM_TimestampedLog( "ReloadFontGlyphs(): Start" );
// get our current resolution
if ( m_SizingPanel != 0 )
{
g_pIPanel->GetSize( m_SizingPanel, m_nScreenWide, m_nScreenTall );
}
else
{
g_pSurface->GetScreenSize( m_nScreenWide, m_nScreenTall );
}
// check our language; some have minimum sizes
int minimumFontHeight = GetMinimumFontHeightForCurrentLanguage();
// add the data to all the fonts
KeyValues *fonts = m_pData->FindKey("Fonts", true);
FOR_EACH_DICT_FAST( m_FontAliases, i )
{
KeyValues *kv = fonts->FindKey( m_FontAliases[i]._trueFontName.String(), true );
// walk through creating adding the first matching glyph set to the font
for (KeyValues *fontdata = kv->GetFirstSubKey(); fontdata != NULL; fontdata = fontdata->GetNextKey())
{
// skip over fonts not meant for this resolution
int fontYResMin = 0, fontYResMax = 0;
sscanf(fontdata->GetString("yres", ""), "%d %d", &fontYResMin, &fontYResMax);
if (fontYResMin)
{
if (!fontYResMax)
{
fontYResMax = fontYResMin;
}
// check the range
if (m_nScreenTall < fontYResMin || m_nScreenTall > fontYResMax)
continue;
}
int flags = 0;
if (fontdata->GetInt( "italic" ))
{
flags |= ISurface::FONTFLAG_ITALIC;
}
if (fontdata->GetInt( "underline" ))
{
flags |= ISurface::FONTFLAG_UNDERLINE;
}
if (fontdata->GetInt( "strikeout" ))
{
flags |= ISurface::FONTFLAG_STRIKEOUT;
}
if (fontdata->GetInt( "symbol" ))
{
flags |= ISurface::FONTFLAG_SYMBOL;
}
if (fontdata->GetInt( "antialias" ) && g_pSurface->SupportsFeature(ISurface::ANTIALIASED_FONTS))
{
flags |= ISurface::FONTFLAG_ANTIALIAS;
}
if (fontdata->GetInt( "dropshadow" ) && g_pSurface->SupportsFeature(ISurface::DROPSHADOW_FONTS))
{
flags |= ISurface::FONTFLAG_DROPSHADOW;
}
if (fontdata->GetInt( "outline" ) && g_pSurface->SupportsFeature(ISurface::OUTLINE_FONTS))
{
flags |= ISurface::FONTFLAG_OUTLINE;
}
if (fontdata->GetInt( "custom" ))
{
flags |= ISurface::FONTFLAG_CUSTOM;
}
if (fontdata->GetInt( "bitmap" ))
{
flags |= ISurface::FONTFLAG_BITMAP;
}
if (fontdata->GetInt( "rotary" ))
{
flags |= ISurface::FONTFLAG_ROTARY;
}
if (fontdata->GetInt( "additive" ))
{
flags |= ISurface::FONTFLAG_ADDITIVE;
}
int tall = fontdata->GetInt( "tall" );
int blur = fontdata->GetInt( "blur" );
int scanlines = fontdata->GetInt( "scanlines" );
float scalex = fontdata->GetFloat( "scalex", 1.0f );
float scaley = fontdata->GetFloat( "scaley", 1.0f );
// only grow this font if it doesn't have a resolution filter specified
if ( ( !fontYResMin && !fontYResMax ) && m_FontAliases[i].m_bProportional )
{
tall = g_Scheme.GetProportionalScaledValueEx( this, tall );
blur = g_Scheme.GetProportionalScaledValueEx( this, blur );
scanlines = g_Scheme.GetProportionalScaledValueEx( this, scanlines );
scalex = g_Scheme.GetProportionalScaledValueEx( this, scalex * 10000.0f ) * 0.0001f;
scaley = g_Scheme.GetProportionalScaledValueEx( this, scaley * 10000.0f ) * 0.0001f;
}
// clip the font size so that fonts can't be too big
if ( tall > 127 )
{
tall = 127;
}
// check our minimum font height
if ( tall < minimumFontHeight )
{
tall = minimumFontHeight;
}
if ( flags & ISurface::FONTFLAG_BITMAP )
{
// add the new set
g_pSurface->SetBitmapFontGlyphSet(
m_FontAliases[i]._font,
g_pSurface->GetBitmapFontName( fontdata->GetString( "name" ) ),
scalex,
scaley,
flags);
}
else
{
int nRangeMin, nRangeMax;
if ( GetFontRange( fontdata->GetString( "name" ), nRangeMin, nRangeMax ) )
{
// add the new set
g_pSurface->SetFontGlyphSet(
m_FontAliases[i]._font,
fontdata->GetString( "name" ),
tall,
fontdata->GetInt( "weight" ),
blur,
scanlines,
flags,
nRangeMin,
nRangeMax);
}
else
{
// add the new set
g_pSurface->SetFontGlyphSet(
m_FontAliases[i]._font,
fontdata->GetString( "name" ),
tall,
fontdata->GetInt( "weight" ),
blur,
scanlines,
flags);
}
}
// don't add any more
break;
}
}
COM_TimestampedLog( "ReloadFontGlyphs(): End" );
}
//-----------------------------------------------------------------------------
// Purpose: create the Border objects from the scheme data
//-----------------------------------------------------------------------------
void CScheme::LoadBorders()
{
m_pkvBorders = m_pData->FindKey("Borders", true);
{for ( KeyValues *kv = m_pkvBorders->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
{
if (kv->GetDataType() == KeyValues::TYPE_STRING)
{
// it's referencing another border, ignore for now
}
else
{
int i = m_BorderList.AddToTail();
IBorder *border = NULL;
const char *pszBorderType = kv->GetString( "bordertype", NULL );
if ( pszBorderType && pszBorderType[0] )
{
if ( !stricmp(pszBorderType,"image") )
{
border = new ImageBorder();
}
else if ( !stricmp(pszBorderType,"scalable_image") )
{
border = new ScalableImageBorder();
}
else
{
Assert(0);
// Fall back to the base border type. See below.
pszBorderType = NULL;
}
}
if ( !pszBorderType || !pszBorderType[0] )
{
border = new Border();
}
border->SetName(kv->GetName());
border->ApplySchemeSettings(this, kv);
m_BorderList[i].border = border;
m_BorderList[i].bSharedBorder = false;
m_BorderList[i].borderSymbol = kv->GetNameSymbol();
}
}}
// iterate again to get the border references
for ( KeyValues *kv = m_pkvBorders->GetFirstSubKey(); kv != NULL; kv = kv->GetNextKey())
{
if (kv->GetDataType() == KeyValues::TYPE_STRING)
{
// it's referencing another border, find it
Border *border = (Border *)GetBorder(kv->GetString());
// const char *str = kv->GetName();
Assert(border);
// add an entry that just references the existing border
int i = m_BorderList.AddToTail();
m_BorderList[i].border = border;
m_BorderList[i].bSharedBorder = true;
m_BorderList[i].borderSymbol = kv->GetNameSymbol();
}
}
m_pBaseBorder = GetBorder("BaseBorder");
}
void CScheme::SpewFonts( void )
{
Msg( "Scheme: %s (%s)\n", GetName(), GetFileName() );
FOR_EACH_DICT_FAST( m_FontAliases, i )
{
const fontalias_t& FontAlias = m_FontAliases[ i ];
uint32 Font = FontAlias._font;
const char *szFontName = g_pSurface->GetFontName( Font );
const char *szFontFamilyName = g_pSurface->GetFontFamilyName( Font );
const char *szTrueFontName = FontAlias._trueFontName.String();
const char *szFontAlias = m_FontAliases.GetElementName( i );
Msg( " %2d: HFont:0x%8.8x, %s, %s, font:%s, tall:%d(%d). %s\n",
i,
Font,
szTrueFontName ? szTrueFontName : "??",
szFontAlias ? szFontAlias : "??",
szFontName ? szFontName : "??",
g_pSurface->GetFontTall( Font ),
g_pSurface->GetFontTallRequested( Font ),
szFontFamilyName ? szFontFamilyName : "" );
}
}
//-----------------------------------------------------------------------------
// Purpose: reloads the scheme from the file
//-----------------------------------------------------------------------------
void CSchemeManager::ReloadSchemes()
{
int count = m_Schemes.Count();
Shutdown( false );
// reload the scheme
for (int i = 1; i < count; i++)
{
LoadSchemeFromFile(m_Schemes[i]->GetFileName(), m_Schemes[i]->GetName());
}
}
//-----------------------------------------------------------------------------
// Purpose: kills all the schemes
//-----------------------------------------------------------------------------
void CScheme::Shutdown( bool full )
{
for (int i = 0; i < m_BorderList.Count(); i++)
{
// delete if it's not shared
if (!m_BorderList[i].bSharedBorder)
{
IBorder *border = m_BorderList[i].border;
delete border;
}
}
m_pBaseBorder = NULL;
m_BorderList.RemoveAll();
m_pkvBorders = NULL;
if (full && m_pData)
{
m_pData->deleteThis();
m_pData = NULL;
delete this;
}
}
//-----------------------------------------------------------------------------
// Purpose: returns a handle to the default (first loaded) scheme
//-----------------------------------------------------------------------------
HScheme CSchemeManager::GetDefaultScheme()
{
return 1;
}
//-----------------------------------------------------------------------------
// Purpose: returns a handle to the scheme identified by "tag"
//-----------------------------------------------------------------------------
HScheme CSchemeManager::GetScheme(const char *tag)
{
for (int i=1;i<m_Schemes.Count();i++)
{
if ( !stricmp(tag,m_Schemes[i]->GetName()) )
{
return i;
}
}
return 1; // default scheme
}
int CSchemeManager::GetProportionalScaledValue_( int rootWide, int rootTall, int normalizedValue )
{
int proH, proW;
g_pSurface->GetProportionalBase( proW, proH );
double scale = (double)rootTall / (double)proH;
return (int)( normalizedValue * scale );
}
int CSchemeManager::GetProportionalNormalizedValue_( int rootWide, int rootTall, int scaledValue )
{
int proH, proW;
g_pSurface->GetProportionalBase( proW, proH );
float scale = (float)rootTall / (float)proH;
return (int)( scaledValue / scale );
}
//-----------------------------------------------------------------------------
// Purpose: converts a value into proportional mode
//-----------------------------------------------------------------------------
int CSchemeManager::GetProportionalScaledValue(int normalizedValue)
{
int wide, tall;
g_pSurface->GetScreenSize( wide, tall );
return GetProportionalScaledValue_( wide, tall, normalizedValue );
}
//-----------------------------------------------------------------------------
// Purpose: converts a value out of proportional mode
//-----------------------------------------------------------------------------
int CSchemeManager::GetProportionalNormalizedValue(int scaledValue)
{
int wide, tall;
g_pSurface->GetScreenSize( wide, tall );
return GetProportionalNormalizedValue_( wide, tall, scaledValue );
}
// gets the proportional coordinates for doing screen-size independant panel layouts
// use these for font, image and panel size scaling (they all use the pixel height of the display for scaling)
int CSchemeManager::GetProportionalScaledValueEx( CScheme *pScheme, int normalizedValue )
{
VPANEL sizing = pScheme->GetSizingPanel();
if ( !sizing )
{
return GetProportionalScaledValue( normalizedValue );
}
int w, h;
g_pIPanel->GetSize( sizing, w, h );
return GetProportionalScaledValue_( w, h, normalizedValue );
}
int CSchemeManager::GetProportionalNormalizedValueEx( CScheme *pScheme, int scaledValue )
{
VPANEL sizing = pScheme->GetSizingPanel();
if ( !sizing )
{
return GetProportionalNormalizedValue( scaledValue );
}
int w, h;
g_pIPanel->GetSize( sizing, w, h );
return GetProportionalNormalizedValue_( w, h, scaledValue );
}
int CSchemeManager::GetProportionalScaledValueEx( HScheme scheme, int normalizedValue )
{
IScheme *pscheme = GetIScheme( scheme );
if ( !pscheme )
{
Assert( 0 );
return GetProportionalScaledValue( normalizedValue );
}
CScheme *p = static_cast< CScheme * >( pscheme );
return GetProportionalScaledValueEx( p, normalizedValue );
}
int CSchemeManager::GetProportionalNormalizedValueEx( HScheme scheme, int scaledValue )
{
IScheme *pscheme = GetIScheme( scheme );
if ( !pscheme )
{
Assert( 0 );
return GetProportionalNormalizedValue( scaledValue );
}
CScheme *p = static_cast< CScheme * >( pscheme );
return GetProportionalNormalizedValueEx( p, scaledValue );
}
void CSchemeManager::SpewFonts( void )
{
for ( int i = 1; i < m_Schemes.Count(); i++ )
{
m_Schemes[i]->SpewFonts();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *CScheme::GetResourceString(const char *stringName)
{
return m_pkvBaseSettings->GetString(stringName);
}
//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an image
//-----------------------------------------------------------------------------
IImage *CSchemeManager::GetImage(const char *imageName, bool hardwareFiltered)
{
if ( !imageName || strlen(imageName) <= 0 ) // frame icons and the like are in the scheme file and may not be defined, so if this is null then fail silently
{
return NULL;
}
// set up to search for the bitmap
CachedBitmapHandle_t searchBitmap;
searchBitmap.pBitmap = NULL;
// Prepend 'vgui/'. Resource files try to load images assuming they live in the vgui directory.
// Used to do this in Bitmap::Bitmap, moved so that the s_pszSearchString is searching for the
// filename with 'vgui/' already added.
char szFileName[MAX_PATH];
//if ( Q_IsAbsolutePath(imageName) )
//{
// Q_strncpy( szFileName, imageName, sizeof(szFileName) );
//}
//else
{
if ( Q_stristr( imageName, ".pic" ) )
{
Q_snprintf( szFileName, sizeof(szFileName), "%s", imageName );
}
else
{
Q_snprintf( szFileName, sizeof(szFileName), "vgui/%s", imageName );
}
}
s_pszSearchString = szFileName;
int i = m_Bitmaps.Find( searchBitmap );
if (m_Bitmaps.IsValidIndex( i ) )
{
return m_Bitmaps[i].pBitmap;
}
// couldn't find the image, try and load it
CachedBitmapHandle_t hBitmap = { new Bitmap( szFileName, hardwareFiltered ) };
m_Bitmaps.Insert( hBitmap );
return hBitmap.pBitmap;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
HTexture CSchemeManager::GetImageID(const char *imageName, bool hardwareFiltered)
{
IImage *img = GetImage(imageName, hardwareFiltered);
return ((Bitmap *)img)->GetID();
}
//-----------------------------------------------------------------------------
// Delete a managed image
//-----------------------------------------------------------------------------
bool CSchemeManager::DeleteImage( const char *pImageName )
{
if ( !pImageName )
{
// nothing to do
return false;
}
// set up to search for the bitmap
CachedBitmapHandle_t searchBitmap;
searchBitmap.pBitmap = NULL;
// Prepend 'vgui/'. Resource files try to load images assuming they live in the vgui directory.
// Used to do this in Bitmap::Bitmap, moved so that the s_pszSearchString is searching for the
// filename with 'vgui/' already added.
char szFileName[256];
if ( Q_stristr( pImageName, ".pic" ) )
{
Q_snprintf( szFileName, sizeof(szFileName), "%s", pImageName );
}
else
{
Q_snprintf( szFileName, sizeof(szFileName), "vgui/%s", pImageName );
}
s_pszSearchString = szFileName;
int i = m_Bitmaps.Find( searchBitmap );
if ( !m_Bitmaps.IsValidIndex( i ) )
{
// not found
return false;
}
// no way to know if eviction occured, assume it does
m_Bitmaps[i].pBitmap->Evict();
delete m_Bitmaps[i].pBitmap;
m_Bitmaps.RemoveAt( i );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an existing border
//-----------------------------------------------------------------------------
IBorder *CScheme::GetBorder(const char *borderName)
{
int symbol = KeyValuesSystem()->GetSymbolForString(borderName);
for (int i = 0; i < m_BorderList.Count(); i++)
{
if (m_BorderList[i].borderSymbol == symbol)
{
return m_BorderList[i].border;
}
}
return m_pBaseBorder;
}
//-----------------------------------------------------------------------------
// Purpose: Get the number of borders
//-----------------------------------------------------------------------------
int CScheme::GetBorderCount() const
{
return m_BorderList.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Get the border at the given index
//-----------------------------------------------------------------------------
IBorder *CScheme::GetBorderAtIndex( int iIndex )
{
if ( !m_BorderList.IsValidIndex( iIndex ) )
return NULL;
return m_BorderList[ iIndex ].border;
}
//-----------------------------------------------------------------------------
// Finds a font in the alias list
//-----------------------------------------------------------------------------
HFont CScheme::FindFontInAliasList( const char *fontName )
{
int i = m_FontAliases.Find( fontName );
if ( i != m_FontAliases.InvalidIndex() )
{
return m_FontAliases[i]._font;
}
// No dice
return 0;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : font -
// Output : char const
//-----------------------------------------------------------------------------
char const *CScheme::GetFontName( const HFont& font )
{
for (int i = m_FontAliases.Count(); --i >= 0; )
{
HFont fnt = (HFont)m_FontAliases[i]._font;
if ( fnt == font )
return m_FontAliases[i]._trueFontName.String();
}
return "<Unknown font>";
}
//-----------------------------------------------------------------------------
// Purpose: returns a pointer to an existing font, proportional=false means use default
//-----------------------------------------------------------------------------
HFont CScheme::GetFont( const char *fontName, bool proportional )
{
// First look in the list of aliases...
return FindFontInAliasList( GetMungedFontName( fontName, tag, proportional ) );
}
//-----------------------------------------------------------------------------
// Purpose:Get the number of fonts
//-----------------------------------------------------------------------------
int CScheme::GetFontCount() const
{
return m_FontAliases.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Get the font at the given index
//-----------------------------------------------------------------------------
HFont CScheme::GetFontAtIndex( int iIndex )
{
if ( !m_FontAliases.IsValidIndex( iIndex ) )
return INVALID_FONT;
return m_FontAliases[ iIndex ]._font;
}
//-----------------------------------------------------------------------------
// Purpose: returns a char string of the munged name this font is stored as in the font manager
//-----------------------------------------------------------------------------
const char *CScheme::GetMungedFontName( const char *fontName, const char *scheme, bool proportional )
{
static char mungeBuffer[ 64 ];
if ( scheme )
{
Q_snprintf( mungeBuffer, sizeof( mungeBuffer ), "%s%s-%s", fontName, scheme, proportional ? "p" : "no" );
}
else
{
Q_snprintf( mungeBuffer, sizeof( mungeBuffer ), "%s-%s", fontName, proportional ? "p" : "no" ); // we don't want the "(null)" snprintf appends
}
return mungeBuffer;
}
//-----------------------------------------------------------------------------
// Purpose: Gets a color from the scheme file
//-----------------------------------------------------------------------------
Color CScheme::GetColor(const char *colorName, Color defaultColor)
{
const char *pchT = LookupSchemeSetting(colorName);
if (!pchT)
return defaultColor;
int r = 0, g = 0, b = 0, a = 0;
if (sscanf(pchT, "%d %d %d %d", &r, &g, &b, &a) >= 3)
return Color(r, g, b, a);
return defaultColor;
}
//-----------------------------------------------------------------------------
// Purpose: Get the color at the given index
//-----------------------------------------------------------------------------
const KeyValues *CScheme::GetColorData() const
{
return m_pkvColors;
}
//-----------------------------------------------------------------------------
// Purpose: recursively looks up a setting
//-----------------------------------------------------------------------------
const char *CScheme::LookupSchemeSetting(const char *pchSetting)
{
// try parse out the color
int r, g, b, a = 0;
int res = sscanf(pchSetting, "%d %d %d %d", &r, &g, &b, &a);
if (res >= 3)
{
return pchSetting;
}
// check the color area first
const char *colStr = m_pkvColors->GetString(pchSetting, NULL);
if (colStr)
return colStr;
// check base settings
colStr = m_pkvBaseSettings->GetString(pchSetting, NULL);
if (colStr)
{
return LookupSchemeSetting(colStr);
}
return pchSetting;
}
//-----------------------------------------------------------------------------
// Purpose: gets the minimum font height for the current language
//-----------------------------------------------------------------------------
int CScheme::GetMinimumFontHeightForCurrentLanguage()
{
char language[64];
bool bValid;
if ( IsPC() )
{
bValid = vgui::g_pSystem->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof(language)-1 );
}
else
{
Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) );
bValid = true;
}
if ( bValid )
{
if (!stricmp(language, "korean")
|| !stricmp(language, "tchinese")
|| !stricmp(language, "schinese")
|| !stricmp(language, "japanese"))
{
// the bitmap-based fonts for these languages simply don't work with a pt. size of less than 9 (13 pixels)
return 13;
}
if ( !stricmp(language, "thai" ) )
{
// thai has problems below 18 pts
return 18;
}
}
// no special minimum height required
return 0;
}