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.
375 lines
10 KiB
375 lines
10 KiB
#include "kv_editor.h" |
|
#include "kv_editor_base_panel.h" |
|
#include "kv_fit_children_panel.h" |
|
#include "ScrollingWindow.h" |
|
#include "filesystem.h" |
|
#include <vgui/ILocalize.h> |
|
#include <vgui_controls/MenuBar.h> |
|
#include <vgui_controls/MenuButton.h> |
|
#include <vgui_controls/Menu.h> |
|
#include "vgui/ISurface.h" |
|
#include <vgui_controls/FileOpenDialog.h> |
|
#include <vgui_controls/MessageBox.h> |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
using namespace vgui; |
|
|
|
//bool g_bAddedKVEditorLocalization = false; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor |
|
//----------------------------------------------------------------------------- |
|
CKV_Editor::CKV_Editor( Panel *parent, const char *name ) : BaseClass( parent, name ) |
|
{ |
|
//if ( !g_bAddedKVEditorLocalization ) |
|
//{ |
|
//g_pVGuiLocalize->AddFile( "resource/kv_editor_english.txt" ); |
|
//g_bAddedKVEditorLocalization = true; |
|
//} |
|
|
|
m_pKeys = NULL; |
|
m_pFileSpec = NULL; |
|
m_bRequireFileSpec = true; |
|
m_bShowSiblings = true; |
|
|
|
m_pScrollingWindow = new CScrollingWindow( this, "ScrollingWindow" ); |
|
m_pContainer = new CKV_Fit_Children_Panel( this, "Container" ); |
|
|
|
m_pScrollingWindow->SetChildPanel( m_pContainer ); |
|
m_pScrollingWindow->InvalidateLayout( true ); |
|
|
|
m_szFileDirectory[0] = 0; |
|
m_szFileFilter[0] = 0; |
|
m_szFileFilterName[0] = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Destructor |
|
//----------------------------------------------------------------------------- |
|
CKV_Editor::~CKV_Editor() |
|
{ |
|
|
|
} |
|
|
|
void CKV_Editor::PerformLayout() |
|
{ |
|
BaseClass::PerformLayout(); |
|
|
|
m_pScrollingWindow->SetBounds( 0, 0, GetWide(), GetTall() ); |
|
//m_pContainer->SetPos( 30, 30 ); |
|
} |
|
|
|
void CKV_Editor::ApplySchemeSettings(vgui::IScheme *pScheme) |
|
{ |
|
BaseClass::ApplySchemeSettings(pScheme); |
|
|
|
m_pContainer->SetBgColor( Color( 0, 0, 0, 255 ) ); |
|
} |
|
|
|
CKV_Editor_Base_Panel* CKV_Editor::FindPanel( KeyValues *pKey ) |
|
{ |
|
if ( !pKey ) |
|
return NULL; |
|
|
|
for ( int i = 0; i < m_Panels.Count(); i++ ) |
|
{ |
|
if ( m_Panels[i]->GetKey() == pKey ) |
|
{ |
|
return m_Panels[i]; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
void CKV_Editor::DeleteAllPanels() |
|
{ |
|
for ( int i = 0; i < m_Panels.Count(); i++ ) |
|
{ |
|
m_Panels[i]->SetVisible( false ); |
|
m_Panels[i]->MarkForDeletion(); |
|
CKV_Fit_Children_Panel *pFit = dynamic_cast< CKV_Fit_Children_Panel* >( m_Panels[i]->GetParent() ); |
|
if ( pFit ) |
|
{ |
|
pFit->RemoveAutoPositionPanel( m_Panels[i] ); |
|
} |
|
} |
|
m_Panels.Purge(); |
|
} |
|
|
|
CKV_Editor_Base_Panel* CKV_Editor::CreatePanel( const char *szClassName, CKV_Editor_Base_Panel *pParent, KeyValues *pFileSpecNode, KeyValues *pKey ) |
|
{ |
|
CKV_Editor_Base_Panel *pPanel = dynamic_cast<CKV_Editor_Base_Panel *>( CreateControlByName( szClassName ) ); |
|
if ( pPanel ) |
|
{ |
|
pPanel->SetParent( pParent ); |
|
pPanel->SetAllowDeletion( m_bAllowDeletion ); |
|
pPanel->SetFileSpecNode( pFileSpecNode ); |
|
pPanel->SetEditor( this ); |
|
pPanel->SetName( pKey->GetName() ); |
|
pPanel->SetKey( pKey ); |
|
pPanel->SetKeyParent( FindParentForKey( pKey ) ); |
|
pPanel->SetSortOrder( pFileSpecNode->GetFloat( "SortOrder" ) ); |
|
pPanel->AddActionSignalTarget( this ); |
|
CKV_Fit_Children_Panel *pFit = dynamic_cast< CKV_Fit_Children_Panel* >( pParent ); |
|
if ( pFit ) |
|
{ |
|
pFit->AddAutoPositionPanel( pPanel ); |
|
} |
|
m_Panels.AddToTail( pPanel ); |
|
} |
|
else |
|
{ |
|
Warning( "Failed to create panel %s\n", szClassName ); |
|
} |
|
return pPanel; |
|
} |
|
|
|
// creates panels for target key, all its siblings and all its children |
|
void CKV_Editor::UpdatePanels( KeyValues *pKV, CKV_Editor_Base_Panel *pParentPanel, bool bIncludeSiblings ) |
|
{ |
|
Assert( pParentPanel ); |
|
// go through each key, making sure it has a panel |
|
for ( KeyValues *pKey = pKV; pKey != NULL; pKey = pKey->GetNextKey() ) |
|
{ |
|
CKV_Editor_Base_Panel *pPanel = FindPanel( pKey ); |
|
KeyValues *pFileSpecNode = FindFileSpecNodeForKey( pKey ); |
|
if ( !pFileSpecNode && m_bRequireFileSpec ) |
|
{ |
|
Warning( "Key %s:%s has no FileSpecNode!\n", pKey->GetName(), pKey->GetString() ); |
|
return; |
|
} |
|
const char *szClassName = "CKV_Node_Panel"; |
|
if ( !pFileSpecNode ) |
|
{ |
|
//Warning( "Failed to FindFileSpecNodeForKey for key %s\n", pKey->GetName() ); |
|
|
|
bool bHasChildren = ( pKey->GetFirstSubKey() != NULL ); |
|
if ( bHasChildren ) |
|
{ |
|
szClassName = "CKV_Node_Panel"; |
|
} |
|
else |
|
{ |
|
szClassName = "CKV_Leaf_Panel"; |
|
} |
|
} |
|
else |
|
{ |
|
szClassName = pFileSpecNode->GetString( "Panel", "CKV_Node_Panel" ); |
|
} |
|
if ( !pPanel ) |
|
{ |
|
// create a panel of the appropriate type (as a child of pParentPanel) |
|
pPanel = CreatePanel( szClassName, pParentPanel, pFileSpecNode, pKey ); |
|
} |
|
else |
|
{ |
|
// make sure the panel is the right type. |
|
if ( Q_stricmp( pPanel->GetClassName(), szClassName ) ) |
|
{ |
|
// destroy and recreate if not (as a child of pParentPanel) |
|
pPanel->SetVisible( false ); |
|
pPanel->MarkForDeletion(); |
|
CKV_Fit_Children_Panel *pFit = dynamic_cast< CKV_Fit_Children_Panel* >( pPanel->GetParent() ); |
|
if ( pFit ) |
|
{ |
|
pFit->RemoveAutoPositionPanel( pPanel ); |
|
} |
|
pPanel = CreatePanel( szClassName, pParentPanel, pFileSpecNode, pKey ); |
|
} |
|
else |
|
{ |
|
pPanel->SetKey( pKey ); |
|
pPanel->SetKeyParent( FindParentForKey( pKey ) ); |
|
} |
|
} |
|
|
|
// if key has children |
|
if ( pKey->GetFirstSubKey() ) |
|
{ |
|
UpdatePanels( pKey->GetFirstSubKey(), pPanel, true ); |
|
} |
|
|
|
if ( !bIncludeSiblings ) |
|
break; |
|
} |
|
m_pContainer->InvalidateLayout( true ); |
|
} |
|
|
|
KeyValues* CKV_Editor::FindParentForKey( KeyValues *pSearchChild ) |
|
{ |
|
for ( KeyValues *pKey = m_pKeys; pKey != NULL; pKey = pKey->GetNextKey() ) |
|
{ |
|
if ( pKey == pSearchChild ) |
|
return NULL; |
|
|
|
KeyValues *pResult = FindParentForKey( pKey, pSearchChild ); |
|
if ( pResult ) |
|
return pResult; |
|
} |
|
return NULL; |
|
} |
|
|
|
KeyValues* CKV_Editor::FindParentForKey( KeyValues *pRoot, KeyValues *pSearchChild ) |
|
{ |
|
// see if pKV or any of our direct children are the search key |
|
for ( KeyValues *pKey = pRoot->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey() ) |
|
{ |
|
if ( pKey == pSearchChild ) |
|
return pRoot; |
|
} |
|
|
|
// if not, then ask each of our children to search deeper |
|
for ( KeyValues *pKey = pRoot->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey() ) |
|
{ |
|
KeyValues *pResult = FindParentForKey( pKey, pSearchChild ); |
|
if ( pResult ) |
|
return pResult; |
|
} |
|
return NULL; |
|
} |
|
|
|
KeyValues* CKV_Editor::FindFileSpecNodeForKey( KeyValues *pKey ) |
|
{ |
|
if ( !m_pFileSpec ) |
|
return NULL; |
|
// build a list of panels from us up to the parent |
|
CUtlVector<KeyValues*> chain; |
|
KeyValues *pSearchKey = pKey; |
|
while ( pSearchKey ) |
|
{ |
|
chain.AddToHead( pSearchKey ); |
|
pSearchKey = FindParentForKey( pSearchKey ); |
|
} |
|
|
|
int iChainDepth = chain.Count(); |
|
int iCurrentDepth = 0; |
|
|
|
KeyValues *pKeySpec = m_pFileSpec; |
|
while ( pKeySpec ) |
|
{ |
|
const char *szNodeName = "_unnamed_node"; |
|
|
|
if ( !Q_stricmp( pKeySpec->GetName(), "_Node" ) || !Q_stricmp( pKeySpec->GetName(), "_Leaf" ) ) |
|
{ |
|
szNodeName = pKeySpec->GetString( "Name", "_unnamed_node" ); |
|
} |
|
//const char *szChainName = chain[iCurrentDepth]->GetName(); |
|
if ( !Q_stricmp( szNodeName, chain[iCurrentDepth]->GetName() ) |
|
|| !Q_stricmp( szNodeName, "*" ) ) // at any level there can be a keyspec node with name "*" which will match any key |
|
{ |
|
iCurrentDepth++; |
|
if ( iCurrentDepth >= iChainDepth ) // found appropriate file spec node that describes this key |
|
{ |
|
return pKeySpec; |
|
} |
|
|
|
// Go down a level |
|
pKeySpec = pKeySpec->GetFirstSubKey(); |
|
} |
|
else |
|
{ |
|
// Check the next key in the mission spec |
|
pKeySpec = pKeySpec->GetNextKey(); |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
void CKV_Editor::SetFileSpec( const char *szFilename, const char *szPathID ) |
|
{ |
|
KeyValues *pKeys = new KeyValues( "FileSpec" ); |
|
if ( !pKeys->LoadFromFile( g_pFullFileSystem, szFilename, szPathID ) ) |
|
{ |
|
Warning( "Failed to load file spec %s\n", szFilename ); |
|
return; |
|
} |
|
SetFileSpec( pKeys ); |
|
} |
|
|
|
void CKV_Editor::SetFileSpec( KeyValues *pKeys ) |
|
{ |
|
m_pFileSpec = pKeys; |
|
DeleteAllPanels(); |
|
UpdatePanels( m_pKeys, m_pContainer, m_bShowSiblings ); |
|
} |
|
|
|
void CKV_Editor::SetFileFilter( const char *szFilter, const char *szFilterName ) |
|
{ |
|
Q_snprintf( m_szFileFilter, sizeof( m_szFileFilter ), szFilter ); |
|
Q_snprintf( m_szFileFilterName, sizeof( m_szFileFilterName ), szFilterName ); |
|
} |
|
|
|
void CKV_Editor::SetFileDirectory( const char *szDirName ) |
|
{ |
|
Q_snprintf( m_szFileDirectory, sizeof( m_szFileDirectory ), szDirName ); |
|
} |
|
|
|
void CKV_Editor::SetKeys( KeyValues *pKeys ) |
|
{ |
|
m_pKeys = pKeys; |
|
DeleteAllPanels(); |
|
UpdatePanels( m_pKeys, m_pContainer, m_bShowSiblings ); |
|
PostActionSignal( new KeyValues( "command", "command", "KeyValuesChanged" ) ); |
|
} |
|
|
|
void CKV_Editor::OnKeyDeleted() |
|
{ |
|
DeleteAllPanels(); |
|
UpdatePanels( m_pKeys, m_pContainer, m_bShowSiblings ); |
|
PostActionSignal( new KeyValues( "command", "command", "KeyValuesChanged" ) ); |
|
} |
|
|
|
void CKV_Editor::OnKeyAdded() |
|
{ |
|
DeleteAllPanels(); |
|
UpdatePanels( m_pKeys, m_pContainer, m_bShowSiblings ); |
|
PostActionSignal( new KeyValues( "command", "command", "KeyValuesChanged" ) ); |
|
} |
|
|
|
void CKV_Editor::AddToKey( KeyValues *pFileSpecNode, KeyValues *pKey, const char *szNewKeyName ) |
|
{ |
|
// go through all _Node or _Leaf children of our filespec node |
|
for ( KeyValues *pSubKey = pFileSpecNode->GetFirstSubKey(); pSubKey; pSubKey = pSubKey->GetNextKey() ) |
|
{ |
|
bool bNode = !Q_stricmp( pSubKey->GetName(), "_Node" ); |
|
bool bLeaf = !Q_stricmp( pSubKey->GetName(), "_Leaf" ); |
|
if ( !bLeaf && !bNode ) |
|
continue; |
|
|
|
if ( szNewKeyName && Q_stricmp( pSubKey->GetString( "Name" ), szNewKeyName ) ) |
|
continue; |
|
|
|
if ( !szNewKeyName && pSubKey->GetInt( "Autocreate" ) != 1 ) |
|
continue; |
|
|
|
if ( bLeaf ) |
|
{ |
|
bool bAlreadyExists = !!pKey->FindKey( pSubKey->GetString( "Name" ) ); |
|
bool bUnique = pSubKey->GetInt( "Unique" ) == 1; |
|
if ( !bUnique || !bAlreadyExists ) |
|
{ |
|
KeyValues *pNewKey = new KeyValues( pSubKey->GetString( "Name" ) ); |
|
Msg( "Added leaf %s\n", pSubKey->GetString( "Name" ) ); |
|
pNewKey->SetStringValue( pSubKey->GetString( "DefaultValue", "" ) ); |
|
pKey->AddSubKey( pNewKey ); |
|
} |
|
} |
|
else |
|
{ |
|
KeyValues *pNewKey = new KeyValues( pSubKey->GetString( "Name" ) ); |
|
Msg( "Added node %s\n", pSubKey->GetString( "Name" ) ); |
|
pKey->AddSubKey( pNewKey ); |
|
|
|
// recursively create child _nodes and any _leafs they have |
|
KeyValues *pNewSpec = FindFileSpecNodeForKey( pNewKey ); |
|
if ( pNewSpec ) |
|
{ |
|
AddToKey( pNewSpec, pNewKey, NULL ); |
|
} |
|
} |
|
} |
|
OnKeyAdded(); |
|
} |