You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
13 KiB
438 lines
13 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: A panel "metaclass" is a name given to a particular type of
|
||
|
// panel with particular instance data. Such panels tend to be dynamically
|
||
|
// added and removed from their parent panels.
|
||
|
//
|
||
|
// $Workfile: $
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "panelmetaclassmgr.h"
|
||
|
#include <KeyValues.h>
|
||
|
#include <vgui_controls/Panel.h>
|
||
|
#include "utldict.h"
|
||
|
#include "filesystem.h"
|
||
|
#include <KeyValues.h>
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Helper KeyValue parsing methods
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool ParseRGBA( KeyValues *pValues, const char* pFieldName, int& r, int& g, int& b, int& a )
|
||
|
{
|
||
|
r = g = b = a = 255;
|
||
|
const char *pColorString = pValues->GetString( pFieldName, "255 255 255 255" );
|
||
|
if ( !pColorString || !pColorString[ 0 ] )
|
||
|
return false;
|
||
|
|
||
|
// Try and scan them in
|
||
|
int scanned;
|
||
|
scanned = sscanf( pColorString, "%i %i %i %i", &r, &g, &b, &a );
|
||
|
if ( scanned != 4 )
|
||
|
{
|
||
|
Warning( "Couldn't scan four color values from %s\n", pColorString );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ParseRGBA( KeyValues* pValues, const char* pFieldName, Color& c )
|
||
|
{
|
||
|
int r, g, b, a;
|
||
|
if (!ParseRGBA( pValues, pFieldName, r, g, b, a ))
|
||
|
return false;
|
||
|
|
||
|
c.SetColor( r, g, b, a );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// FIXME: Why do we have vgui::KeyValues too!?!??! Bleah
|
||
|
/*-----------------------------------------------------------------------------
|
||
|
bool ParseRGBA( KeyValues *pValues, const char* pFieldName, int& r, int& g, int& b, int& a )
|
||
|
{
|
||
|
r = g = b = a = 255;
|
||
|
const char *pColorString = pValues->GetString( pFieldName, "255 255 255 255" );
|
||
|
if ( !pColorString || !pColorString[ 0 ] )
|
||
|
return false;
|
||
|
|
||
|
// Try and scan them in
|
||
|
int scanned;
|
||
|
scanned = sscanf( pColorString, "%i %i %i %i", &r, &g, &b, &a );
|
||
|
if ( scanned != 4 )
|
||
|
{
|
||
|
Warning( "Couldn't scan four color values from %s\n", pColorString );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ParseRGBA( KeyValues* pValues, const char* pFieldName, Color& c )
|
||
|
{
|
||
|
int r, g, b, a;
|
||
|
if (!ParseRGBA( pValues, pFieldName, r, g, b, a ))
|
||
|
return false;
|
||
|
|
||
|
c.SetColor( r, g, b, a );
|
||
|
return true;
|
||
|
} */
|
||
|
|
||
|
|
||
|
bool ParseCoord( KeyValues *pValues, const char* pFieldName, int& x, int& y )
|
||
|
{
|
||
|
x = y = 0;
|
||
|
const char *pCoordString = pValues->GetString( pFieldName, "0 0" );
|
||
|
if ( !pCoordString || !pCoordString[ 0 ] )
|
||
|
return false;
|
||
|
|
||
|
// Try and scan them in
|
||
|
int scanned;
|
||
|
scanned = sscanf( pCoordString, "%i %i", &x, &y );
|
||
|
if ( scanned != 2 )
|
||
|
{
|
||
|
Warning( "Couldn't scan 2d coordinate values from %s\n", pCoordString );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// coords are within 640x480 screen space
|
||
|
x = ( x * ( ( float )ScreenWidth() / 640.0 ) );
|
||
|
y = ( y * ( ( float )ScreenHeight() / 480.0 ) );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ParseRect( KeyValues *pValues, const char* pFieldName, int& x, int& y, int& w, int& h )
|
||
|
{
|
||
|
x = y = w = h = 0;
|
||
|
const char *pRectString = pValues->GetString( pFieldName, "0 0 0 0" );
|
||
|
if ( !pRectString || !pRectString[ 0 ] )
|
||
|
return false;
|
||
|
|
||
|
// Try and scan them in
|
||
|
int scanned;
|
||
|
scanned = sscanf( pRectString, "%i %i %i %i", &x, &y, &w, &h );
|
||
|
if ( scanned != 4 )
|
||
|
{
|
||
|
Warning( "Couldn't scan rectangle values from %s\n", pRectString );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// coords are within 640x480 screen space
|
||
|
x = ( x * ( ( float )ScreenWidth() / 640.0 ) );
|
||
|
y = ( y * ( ( float )ScreenHeight() / 480.0 ) );
|
||
|
w = ( w * ( ( float )ScreenWidth() / 640.0 ) );
|
||
|
h = ( h * ( ( float )ScreenHeight() / 480.0 ) );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Helper class to make meta class panels (for use in entities, so they autocleanup)
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CPanelWrapper::CPanelWrapper() : m_pPanel(NULL)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CPanelWrapper::~CPanelWrapper()
|
||
|
{
|
||
|
Deactivate();
|
||
|
}
|
||
|
|
||
|
void CPanelWrapper::Activate( char const* pMetaClassName, vgui::Panel *pParent, int sortorder, void *pVoidInitData )
|
||
|
{
|
||
|
if ( m_pPanel )
|
||
|
{
|
||
|
Deactivate();
|
||
|
}
|
||
|
|
||
|
m_pPanel = PanelMetaClassMgr()->CreatePanelMetaClass( pMetaClassName, sortorder, pVoidInitData, pParent );
|
||
|
}
|
||
|
|
||
|
void CPanelWrapper::Deactivate( void )
|
||
|
{
|
||
|
if ( m_pPanel )
|
||
|
{
|
||
|
PanelMetaClassMgr()->DestroyPanelMetaClass( m_pPanel );
|
||
|
m_pPanel = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vgui::Panel *CPanelWrapper::GetPanel( )
|
||
|
{
|
||
|
return m_pPanel;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Singleton class responsible for managing metaclass panels
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CPanelMetaClassMgrImp : public IPanelMetaClassMgr
|
||
|
{
|
||
|
public:
|
||
|
// constructor, destructor
|
||
|
CPanelMetaClassMgrImp();
|
||
|
virtual ~CPanelMetaClassMgrImp();
|
||
|
|
||
|
// Members of IPanelMetaClassMgr
|
||
|
virtual void LoadMetaClassDefinitionFile( const char* pLevelName );
|
||
|
virtual void InstallPanelType( const char* pPanelName, IPanelFactory* pFactory );
|
||
|
virtual vgui::Panel *CreatePanelMetaClass( const char* pMetaClassName,
|
||
|
int sortorder, void *pInitData, vgui::Panel *pParent, const char *pChainName );
|
||
|
virtual void DestroyPanelMetaClass( vgui::Panel *pPanel );
|
||
|
|
||
|
private:
|
||
|
struct MetaClassDict_t
|
||
|
{
|
||
|
unsigned short m_KeyValueIndex;
|
||
|
unsigned short m_TypeIndex;
|
||
|
KeyValues* m_pKeyValues;
|
||
|
};
|
||
|
|
||
|
// various parsing helper methods
|
||
|
bool ParseSingleMetaClass( const char* pFileName, const char* pInstanceName,
|
||
|
KeyValues* pMetaClass, int keyValueIndex );
|
||
|
bool ParseMetaClassList( const char* pFileName, KeyValues* pKeyValues, int keyValueIndex );
|
||
|
|
||
|
// No copy constructor
|
||
|
CPanelMetaClassMgrImp( const CPanelMetaClassMgrImp & );
|
||
|
|
||
|
// List of panel types...
|
||
|
CUtlDict< IPanelFactory*, unsigned short > m_PanelTypeDict;
|
||
|
|
||
|
// List of metaclass types
|
||
|
CUtlDict< MetaClassDict_t, unsigned short > m_MetaClassDict;
|
||
|
|
||
|
// Create key value accesor
|
||
|
CUtlDict< KeyValues*, unsigned short > m_MetaClassKeyValues;
|
||
|
};
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Returns the singleton panel metaclass mgr interface
|
||
|
//-----------------------------------------------------------------------------
|
||
|
IPanelMetaClassMgr* PanelMetaClassMgr()
|
||
|
{
|
||
|
// NOTE: the CPanelFactory implementation requires the local static here
|
||
|
// even though it means an extra check every time PanelMetaClassMgr is accessed
|
||
|
static CPanelMetaClassMgrImp s_MetaClassMgrImp;
|
||
|
return &s_MetaClassMgrImp;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// constructor, destructor
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CPanelMetaClassMgrImp::CPanelMetaClassMgrImp() : m_PanelTypeDict( true, 0, 32 )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CPanelMetaClassMgrImp::~CPanelMetaClassMgrImp()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Call this to install a new panel type
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CPanelMetaClassMgrImp::InstallPanelType( const char* pPanelName, IPanelFactory* pFactory )
|
||
|
{
|
||
|
Assert( pPanelName && pFactory );
|
||
|
|
||
|
// convert to lowercase
|
||
|
int len = Q_strlen(pPanelName) + 1;
|
||
|
char* pTemp = (char*)stackalloc( len );
|
||
|
Q_strncpy( pTemp, pPanelName, len );
|
||
|
Q_strnlwr( pTemp, len );
|
||
|
|
||
|
m_PanelTypeDict.Insert( pTemp, pFactory );
|
||
|
|
||
|
stackfree( pTemp );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Parse a single metaclass
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CPanelMetaClassMgrImp::ParseSingleMetaClass( const char* pFileName,
|
||
|
const char* pMetaClassName, KeyValues* pMetaClassValues, int keyValueIndex )
|
||
|
{
|
||
|
// Complain about duplicately defined metaclass names...
|
||
|
if ( m_MetaClassDict.Find( pMetaClassName ) != m_MetaClassDict.InvalidIndex() )
|
||
|
{
|
||
|
Warning( "Meta class %s duplicately defined (file %s)\n", pMetaClassName, pFileName );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// find the type...
|
||
|
const char* pPanelType = pMetaClassValues->GetString( "type" );
|
||
|
if (!pPanelType || !pPanelType[0])
|
||
|
{
|
||
|
Warning( "Unable to find type of meta class %s in file %s\n", pMetaClassName, pFileName );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
unsigned short i = m_PanelTypeDict.Find( pPanelType );
|
||
|
if (i == m_PanelTypeDict.InvalidIndex())
|
||
|
{
|
||
|
Warning( "Type %s of meta class %s undefined!\n", pPanelType, pMetaClassName );
|
||
|
stackfree(pLwrMetaClass);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Add it to the metaclass dictionary
|
||
|
MetaClassDict_t element;
|
||
|
element.m_TypeIndex = i;
|
||
|
element.m_KeyValueIndex = keyValueIndex;
|
||
|
element.m_pKeyValues = pMetaClassValues;
|
||
|
m_MetaClassDict.Insert( pMetaClassName, element );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Parse the metaclass list
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CPanelMetaClassMgrImp::ParseMetaClassList( const char* pFileName,
|
||
|
KeyValues* pKeyValues, int keyValueIdx )
|
||
|
{
|
||
|
// Iterate over all metaclasses...
|
||
|
KeyValues* pIter = pKeyValues->GetFirstSubKey();
|
||
|
while( pIter )
|
||
|
{
|
||
|
if (!ParseSingleMetaClass( pFileName, pIter->GetName(), pIter, keyValueIdx ))
|
||
|
{
|
||
|
// return false;
|
||
|
Warning( "MetaClass missing for %s\n", pIter->GetName() );
|
||
|
}
|
||
|
pIter = pIter->GetNextKey();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Loads up a file containing metaclass definitions
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CPanelMetaClassMgrImp::LoadMetaClassDefinitionFile( const char *pFileName )
|
||
|
{
|
||
|
MEM_ALLOC_CREDIT();
|
||
|
|
||
|
// Blat out previous metaclass definitions read in from this file...
|
||
|
int i = m_MetaClassKeyValues.Find( pFileName );
|
||
|
if (i != m_MetaClassKeyValues.InvalidIndex() )
|
||
|
{
|
||
|
// Blow away the previous keyvalues from that file
|
||
|
unsigned short j = m_MetaClassDict.First();
|
||
|
while ( j != m_MetaClassDict.InvalidIndex() )
|
||
|
{
|
||
|
unsigned short next = m_MetaClassDict.Next(j);
|
||
|
if ( m_MetaClassDict[j].m_KeyValueIndex == i)
|
||
|
{
|
||
|
m_MetaClassDict.RemoveAt(j);
|
||
|
}
|
||
|
|
||
|
j = next;
|
||
|
}
|
||
|
|
||
|
m_MetaClassKeyValues[i]->deleteThis();
|
||
|
m_MetaClassKeyValues.RemoveAt(i);
|
||
|
}
|
||
|
|
||
|
// Create a new keyvalues entry
|
||
|
KeyValues* pKeyValues = new KeyValues(pFileName);
|
||
|
int idx = m_MetaClassKeyValues.Insert( pFileName, pKeyValues );
|
||
|
|
||
|
// Read in all metaclass definitions...
|
||
|
|
||
|
// Load the file
|
||
|
if ( !pKeyValues->LoadFromFile( filesystem, pFileName ) )
|
||
|
{
|
||
|
Warning( "Couldn't find metaclass definition file %s\n", pFileName );
|
||
|
pKeyValues->deleteThis();
|
||
|
m_MetaClassKeyValues.RemoveAt(idx);
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Go ahead and parse the data now
|
||
|
if ( !ParseMetaClassList( pFileName, pKeyValues, idx ) )
|
||
|
{
|
||
|
Warning( "Detected one or more errors parsing %s\n", pFileName );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Performs key value chaining
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static void KeyValueChainRecursive( KeyValues* pKeyValues, const char *pSectionName )
|
||
|
{
|
||
|
KeyValues* pSection = pKeyValues->FindKey( pSectionName );
|
||
|
|
||
|
if (pSection)
|
||
|
{
|
||
|
pKeyValues->ChainKeyValue( pSection );
|
||
|
}
|
||
|
|
||
|
KeyValues* pIter = pKeyValues->GetFirstSubKey();
|
||
|
while (pIter)
|
||
|
{
|
||
|
// Don't both setting up links on a keyvalue that has no children
|
||
|
if (pIter->GetFirstSubKey())
|
||
|
KeyValueChainRecursive( pIter, pSectionName );
|
||
|
|
||
|
pIter = pIter->GetNextKey();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Create, destroy panel...
|
||
|
//-----------------------------------------------------------------------------
|
||
|
vgui::Panel *CPanelMetaClassMgrImp::CreatePanelMetaClass( const char* pMetaClassName,
|
||
|
int sortorder, void *pInitData, vgui::Panel *pParent, const char *pChainName )
|
||
|
{
|
||
|
// Search for the metaclass name
|
||
|
int i = m_MetaClassDict.Find( pMetaClassName );
|
||
|
if (i == m_MetaClassDict.InvalidIndex())
|
||
|
return NULL;
|
||
|
|
||
|
// Now that we've got the metaclass, we can figure out what kind of
|
||
|
// panel to instantiate...
|
||
|
MetaClassDict_t &metaClass = m_MetaClassDict[i];
|
||
|
IPanelFactory* pFactory = m_PanelTypeDict[metaClass.m_TypeIndex];
|
||
|
|
||
|
// Set up the key values for use in initialization
|
||
|
if (pChainName)
|
||
|
{
|
||
|
KeyValueChainRecursive( metaClass.m_pKeyValues, pChainName );
|
||
|
}
|
||
|
|
||
|
// Create and initialize the panel
|
||
|
vgui::Panel *pPanel = pFactory->Create( pMetaClassName, metaClass.m_pKeyValues, pInitData, pParent );
|
||
|
if ( pPanel )
|
||
|
{
|
||
|
pPanel->SetZPos( sortorder );
|
||
|
}
|
||
|
|
||
|
return pPanel;
|
||
|
}
|
||
|
|
||
|
void CPanelMetaClassMgrImp::DestroyPanelMetaClass( vgui::Panel *pPanel )
|
||
|
{
|
||
|
// if ( pPanel )
|
||
|
// pPanel->MarkForDeletion();
|
||
|
delete pPanel;
|
||
|
}
|
||
|
|
||
|
|