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.
4829 lines
131 KiB
4829 lines
131 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "stdafx.h" |
|
#include "hammer.h" |
|
#include "EntityHelpDlg.h" |
|
#include "History.h" |
|
#include "MainFrm.h" |
|
#include "MapWorld.h" |
|
#include "OP_Entity.h" |
|
#include "CustomMessages.h" |
|
#include "NewKeyValue.h" |
|
#include "GlobalFunctions.h" |
|
#include "MapDoc.h" |
|
#include "MapEntity.h" |
|
#include "ObjectProperties.h" |
|
#include "TargetNameCombo.h" |
|
#include "TextureBrowser.h" |
|
#include "TextureSystem.h" |
|
#include "ToolPickAngles.h" |
|
#include "ToolPickEntity.h" |
|
#include "ToolPickFace.h" |
|
#include "ToolManager.h" |
|
#include "SoundBrowser.h" |
|
#include "ifilesystemopendialog.h" |
|
#include "filesystem_tools.h" |
|
#include "tier0/icommandline.h" |
|
#include "HammerVGui.h" |
|
#include "mapview3d.h" |
|
#include "camera.h" |
|
#include "Selection.h" |
|
#include "options.h" |
|
#include "op_flags.h" |
|
#include "MapInstance.h" |
|
|
|
extern GameData *pGD; // current game data |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
|
|
#pragma warning( disable : 4355 ) |
|
|
|
|
|
#define IDC_SMARTCONTROL 1 |
|
#define IDC_SMARTCONTROL_TARGETNAME 2 // We have a different define for this because we rely 100% |
|
// on CTargetNameComboBox for the info about its updates. |
|
#define IDC_SMARTCONTROL_INSTANCE_VARIABLE 3 |
|
#define IDC_SMARTCONTROL_INSTANCE_VALUE 4 |
|
#define IDC_SMARTCONTROL_INSTANCE_PARM 5 |
|
|
|
#define SPAWNFLAGS_KEYNAME "spawnflags" |
|
|
|
#define INSTANCE_VAR_MAP_START -10 |
|
|
|
|
|
static WCKeyValues kvClipboard; |
|
static BOOL bKvClipEmpty = TRUE; |
|
|
|
// Colors used for the keyvalues list control. |
|
static COLORREF g_BgColor_Edited = RGB( 239, 239, 255 ); // blue |
|
static COLORREF g_BgColor_Default = RGB( 255, 255, 255 ); // white |
|
static COLORREF g_BgColor_Added = RGB( 255, 239, 239 ); // pink |
|
static COLORREF g_BgColor_InstanceParm = RGB( 239, 255, 239 ); // green |
|
static COLORREF g_TextColor_Normal = RGB( 0, 0, 0 ); |
|
static COLORREF g_TextColor_MissingTarget = RGB( 255, 0, 0 ); // dark red |
|
|
|
static int g_DumbEditControls[] = {IDC_DELETEKEYVALUE, IDC_KEY, IDC_VALUE, IDC_ADDKEYVALUE, IDC_KEY_LABEL, IDC_VALUE_LABEL}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Less function for use with CString |
|
//----------------------------------------------------------------------------- |
|
bool CStringLessFunc(const CString &lhs, const CString &rhs) |
|
{ |
|
return (Q_strcmp(lhs, rhs) < 0); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if the string specifies the name of an entity in the world. |
|
//----------------------------------------------------------------------------- |
|
static bool IsValidTargetName( const char *pTestName ) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
CMapWorld *pWorld = pDoc->GetMapWorld(); |
|
const CMapEntityList *pList = pWorld->EntityList_GetList(); |
|
|
|
for ( int i=0; i < pList->Count(); i++ ) |
|
{ |
|
CMapEntity *pEntity = pList->Element( i ); |
|
const char *pszTargetName = pEntity->GetKeyValue("targetname"); |
|
if ( pszTargetName && Q_stricmp( pszTargetName, pTestName ) == 0 ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
static CString StripDirPrefix( const char *pFilename, const char *pPrefix ) |
|
{ |
|
int prefixLen = V_strlen( pPrefix ); |
|
if ( V_stristr( pFilename, pPrefix ) == pFilename ) |
|
{ |
|
if ( pFilename[prefixLen] == '/' || pFilename[prefixLen] == '\\' ) |
|
return pFilename + prefixLen + 1; |
|
} |
|
|
|
return pFilename; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// CColoredListCtrl implementation. |
|
//----------------------------------------------------------------------------- |
|
|
|
CColoredListCtrl::CColoredListCtrl( IItemColorCallback *pCallback ) |
|
{ |
|
m_pCallback = pCallback; |
|
} |
|
|
|
|
|
void CColoredListCtrl::DrawItem( LPDRAWITEMSTRUCT p ) |
|
{ |
|
CDC *pDC = CDC::FromHandle( p->hDC ); |
|
|
|
COLORREF bgColor, txtColor; |
|
m_pCallback->GetItemColor( p->itemID, &bgColor, &txtColor ); |
|
|
|
// Draw the background. |
|
CBrush br; |
|
CPen pen( PS_SOLID, 0, bgColor ); |
|
|
|
// Selected? Draw a dotted border. |
|
LOGBRUSH logBrush; |
|
logBrush.lbColor = RGB(0,0,0); |
|
logBrush.lbHatch = HS_CROSS; |
|
logBrush.lbStyle = BS_SOLID; |
|
CPen dashedPen( PS_ALTERNATE, 1, &logBrush ); |
|
|
|
if ( p->itemState & ODS_SELECTED ) |
|
pDC->SelectObject( &dashedPen ); |
|
else |
|
pDC->SelectObject( &pen ); |
|
|
|
br.CreateSolidBrush( bgColor ); |
|
pDC->SelectObject( &br ); |
|
RECT rcFill = p->rcItem; |
|
rcFill.bottom -= 1; |
|
pDC->Rectangle( &rcFill ); |
|
|
|
// Setup for drawing text. |
|
pDC->SetTextColor( txtColor ); |
|
|
|
// Draw the first column. |
|
RECT rcItem = p->rcItem; |
|
rcItem.left += 3; |
|
pDC->DrawText( GetItemText( p->itemID, 0 ), &rcItem, DT_LEFT | DT_VCENTER ); |
|
|
|
// Draw the second column. |
|
LVCOLUMN columnInfo; |
|
columnInfo.mask = LVCF_WIDTH; |
|
GetColumn( 0, &columnInfo ); |
|
rcItem = p->rcItem; |
|
rcItem.left += columnInfo.cx; |
|
|
|
// Give our owner a chance to draw the second column. |
|
if ( !m_pCallback->CustomDrawItemValue( p, &rcItem ) ) |
|
{ |
|
rcItem.left += 3; |
|
pDC->DrawText( GetItemText( p->itemID, 1 ), &rcItem, DT_LEFT | DT_VCENTER ); |
|
} |
|
} |
|
|
|
|
|
class CMyEdit : public CEdit |
|
{ |
|
public: |
|
void SetParentPage(COP_Entity* pPage) |
|
{ |
|
m_pParent = pPage; |
|
} |
|
|
|
afx_msg void OnChar(UINT, UINT, UINT); |
|
|
|
COP_Entity *m_pParent; |
|
DECLARE_MESSAGE_MAP() |
|
}; |
|
|
|
|
|
BEGIN_MESSAGE_MAP(CMyEdit, CEdit) |
|
ON_WM_CHAR() |
|
END_MESSAGE_MAP() |
|
|
|
|
|
class CMyComboBox : public CComboBox |
|
{ |
|
public: |
|
void SetParentPage(COP_Entity* pPage) |
|
{ |
|
m_pParent = pPage; |
|
} |
|
|
|
afx_msg void OnChar(UINT, UINT, UINT); |
|
|
|
COP_Entity *m_pParent; |
|
DECLARE_MESSAGE_MAP() |
|
}; |
|
|
|
|
|
BEGIN_MESSAGE_MAP(CMyComboBox, CComboBox) |
|
ON_WM_CHAR() |
|
END_MESSAGE_MAP() |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by the angles picker tool when a target point is picked. This |
|
// stuffs the angles into the smartedit control so that the entity |
|
// points at the target position. |
|
//----------------------------------------------------------------------------- |
|
void CPickAnglesTarget::OnNotifyPickAngles(const Vector &vecPos) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
if (!pDoc) |
|
{ |
|
return; |
|
} |
|
|
|
GetHistory()->MarkUndoPosition(pDoc->GetSelection()->GetList(), "Point At"); |
|
|
|
// |
|
// Update the edit control text with the entity name. This text will be |
|
// stuffed into the local keyvalue storage in OnChangeSmartControl. |
|
// |
|
FOR_EACH_OBJ( *m_pDlg->m_pObjectList, pos ) |
|
{ |
|
CMapClass *pObject = m_pDlg->m_pObjectList->Element(pos); |
|
CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject); |
|
Assert(pEntity != NULL); |
|
if (pEntity != NULL) |
|
{ |
|
GetHistory()->Keep(pEntity); |
|
|
|
// Calculate the angles to point this entity at the chosen spot. |
|
Vector vecOrigin; |
|
pEntity->GetOrigin(vecOrigin); |
|
Vector vecForward = vecPos - vecOrigin; |
|
|
|
QAngle angFace; |
|
VectorAngles(vecForward, angFace); |
|
|
|
// HACK: lights negate pitch |
|
if (pEntity->GetClassName() && (!strnicmp(pEntity->GetClassName(), "light_", 6))) |
|
{ |
|
angFace[PITCH] *= -1; |
|
} |
|
|
|
// Update the edit control with the calculated angles. |
|
char szAngles[80]; |
|
sprintf(szAngles, "%.0f %.0f %.0f", angFace[PITCH], angFace[YAW], angFace[ROLL]); |
|
pEntity->SetKeyValue("angles", szAngles); |
|
|
|
// HACK: lights have a separate "pitch" key |
|
if (pEntity->GetClassName() && (!strnicmp(pEntity->GetClassName(), "light_", 6))) |
|
{ |
|
char szPitch[20]; |
|
sprintf(szPitch, "%.0f", angFace[PITCH]); |
|
pEntity->SetKeyValue("pitch", szPitch); |
|
} |
|
|
|
// FIXME: this should be called automatically, but it isn't |
|
m_pDlg->OnChangeSmartcontrol(); |
|
} |
|
} |
|
m_pDlg->StopPicking(); |
|
|
|
GetMainWnd()->pObjectProperties->MarkDataDirty(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by the entity picker tool when an entity is picked. This |
|
// stuffs the entity name into the smartedit control. |
|
//----------------------------------------------------------------------------- |
|
void CPickEntityTarget::OnNotifyPickEntity(CToolPickEntity *pTool) |
|
{ |
|
// |
|
// Update the edit control text with the entity name. This text will be |
|
// stuffed into the local keyvalue storage in OnChangeSmartControl. |
|
// |
|
CMapEntityList Full; |
|
CMapEntityList Partial; |
|
pTool->GetSelectedEntities(Full, Partial); |
|
CMapEntity *pEntity = Full.Element(0); |
|
if (pEntity) |
|
{ |
|
const char *pszValue = pEntity->GetKeyValue(m_szKey); |
|
if (!pszValue) |
|
{ |
|
pszValue = ""; |
|
} |
|
|
|
m_pDlg->SetSmartControlText(pszValue); |
|
} |
|
|
|
m_pDlg->StopPicking(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by the face picker tool when the face selection changes. |
|
// Input : pTool - The face picker tool that is notifying us. |
|
//----------------------------------------------------------------------------- |
|
void CPickFaceTarget::OnNotifyPickFace(CToolPickFace *pTool) |
|
{ |
|
m_pDlg->UpdatePickFaceText(pTool); |
|
} |
|
|
|
|
|
CSmartControlTargetNameRouter::CSmartControlTargetNameRouter( COP_Entity *pDlg ) |
|
{ |
|
m_pDlg = pDlg; |
|
} |
|
|
|
void CSmartControlTargetNameRouter::OnTextChanged( const char *pText ) |
|
{ |
|
m_pDlg->OnSmartControlTargetNameChanged( pText ); |
|
} |
|
|
|
|
|
BEGIN_MESSAGE_MAP(COP_Entity, CObjectPage) |
|
//{{AFX_MSG_MAP(COP_Entity) |
|
ON_NOTIFY(LVN_ITEMCHANGED, IDC_KEYVALUES, OnItemChangedKeyValues) |
|
ON_NOTIFY(NM_DBLCLK, IDC_KEYVALUES, OnDblClickKeyValues) |
|
|
|
ON_BN_CLICKED(IDC_ADDKEYVALUE, OnAddkeyvalue) |
|
ON_BN_CLICKED(IDC_REMOVEKEYVALUE, OnRemovekeyvalue) |
|
ON_BN_CLICKED(IDC_SMARTEDIT, OnSmartedit) |
|
ON_EN_CHANGE(IDC_VALUE, OnChangeKeyorValue) |
|
ON_BN_CLICKED(IDC_COPY, OnCopy) |
|
ON_BN_CLICKED(IDC_PASTE, OnPaste) |
|
ON_BN_CLICKED(IDC_PICKCOLOR, OnPickColor) |
|
ON_WM_SIZE() |
|
ON_EN_SETFOCUS(IDC_KEY, OnSetfocusKey) |
|
ON_EN_KILLFOCUS(IDC_KEY, OnKillfocusKey) |
|
ON_MESSAGE(ABN_CHANGED, OnChangeAngleBox) |
|
ON_CBN_SELCHANGE(IDC_SMARTCONTROL, OnChangeSmartcontrolSel) |
|
ON_CBN_EDITUPDATE(IDC_SMARTCONTROL, OnChangeSmartcontrol) |
|
ON_EN_CHANGE(IDC_SMARTCONTROL, OnChangeSmartcontrol) |
|
ON_BN_CLICKED(IDC_BROWSE, OnBrowse) |
|
ON_BN_CLICKED(IDC_BROWSE_INSTANCE, OnBrowseInstance) |
|
ON_BN_CLICKED(IDC_PLAY_SOUND, OnPlaySound) |
|
ON_BN_CLICKED(IDC_MARK, OnMark) |
|
ON_BN_CLICKED(IDC_MARK_AND_ADD, OnMarkAndAdd) |
|
ON_BN_CLICKED(IDC_PICK_FACES, OnPickFaces) |
|
ON_BN_CLICKED(IDC_ENTITY_HELP, OnEntityHelp) |
|
ON_BN_CLICKED(IDC_PICK_ANGLES, OnPickAngles) |
|
ON_BN_CLICKED(IDC_PICK_ENTITY, OnPickEntity) |
|
ON_BN_CLICKED(IDC_CAMERA_DISTANCE, OnCameraDistance) |
|
ON_EN_CHANGE(IDC_SMARTCONTROL_INSTANCE_VARIABLE, OnChangeInstanceVariableControl) |
|
ON_EN_CHANGE(IDC_SMARTCONTROL_INSTANCE_VALUE, OnChangeInstanceVariableControl) |
|
ON_EN_CHANGE(IDC_SMARTCONTROL_INSTANCE_PARM, OnChangeInstanceParmControl) |
|
ON_CBN_SELCHANGE(IDC_SMARTCONTROL_INSTANCE_PARM, OnChangeInstanceParmControl) |
|
ON_CBN_EDITUPDATE(IDC_SMARTCONTROL_INSTANCE_PARM, OnChangeInstanceParmControl) |
|
//}}AFX_MSG_MAP |
|
END_MESSAGE_MAP() |
|
|
|
|
|
IMPLEMENT_DYNCREATE(COP_Entity, CObjectPage) |
|
|
|
|
|
typedef int (CALLBACK *ColumnSortFn)( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam ); |
|
|
|
int InternalSortByColumn( COP_Entity *pDlg, const char *pShortName1, const char *pShortName2, int iColumn ) |
|
{ |
|
int i1 = pDlg->GetKeyValueRowByShortName( pShortName1 ); |
|
int i2 = pDlg->GetKeyValueRowByShortName( pShortName2 ); |
|
if ( i1 == -1 || i2 == -1 ) |
|
return 0; |
|
|
|
CString str1 = pDlg->m_VarList.GetItemText( i1, iColumn ); |
|
CString str2 = pDlg->m_VarList.GetItemText( i2, iColumn ); |
|
|
|
return Q_stricmp( str1, str2 ); |
|
} |
|
|
|
int CALLBACK SortByItemEditedState( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam ) |
|
{ |
|
COP_Entity *pDlg = (COP_Entity*)lpParam; |
|
if ( !pDlg->m_pDisplayClass ) |
|
return 0; |
|
|
|
const char *pShortName1 = (const char*)iItem1; |
|
const char *pShortName2 = (const char*)iItem2; |
|
|
|
EKeyState s1, s2; |
|
bool b1, b2; |
|
pDlg->GetKeyState( pShortName1, &s1, &b1 ); |
|
pDlg->GetKeyState( pShortName2, &s2, &b2 ); |
|
|
|
bool bNew1 = (s1 == k_EKeyState_AddedManually); |
|
bool bNew2 = (s2 == k_EKeyState_AddedManually); |
|
|
|
return bNew1 < bNew2; |
|
} |
|
|
|
static int CALLBACK SortByColumn0( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam ) |
|
{ |
|
return InternalSortByColumn( (COP_Entity*)lpParam, (const char*)iItem1, (const char*)iItem2, 0 ); |
|
} |
|
|
|
static int CALLBACK SortByColumn1( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam ) |
|
{ |
|
return InternalSortByColumn( (COP_Entity*)lpParam, (const char*)iItem1, (const char*)iItem2, 1 ); |
|
} |
|
|
|
ColumnSortFn g_ColumnSortFunctions[] = |
|
{ |
|
SortByItemEditedState, |
|
SortByColumn0, |
|
SortByColumn1 |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
COP_Entity::COP_Entity() |
|
: CObjectPage(COP_Entity::IDD), |
|
m_cClasses( this ), |
|
m_SmartControlTargetNameRouter( this ), |
|
m_VarList( this ), |
|
m_InstanceParmData( CStringLessFunc ) |
|
{ |
|
//{{AFX_DATA_INIT(COP_Entity) |
|
// NOTE: the ClassWizard will add member initialization here |
|
//}}AFX_DATA_INIT |
|
|
|
m_iLastClassListSolidClasses = -9999; |
|
m_bAllowPresentProperties = true; |
|
m_nPresentPropertiesCalls = 0; |
|
m_bClassSelectionEmpty = false; |
|
m_cClasses.SetOnlyProvideSuggestions( true ); |
|
|
|
m_bPicking = false; |
|
m_bChangingKeyName = false; |
|
|
|
m_pSmartBrowseButton = NULL; |
|
m_pLastSmartControlVar = NULL; |
|
|
|
m_pEditInstanceVariable = NULL; |
|
m_pEditInstanceValue = NULL; |
|
m_pComboInstanceParmType = NULL; |
|
|
|
m_bIgnoreKVChange = false; |
|
m_bSmartedit = true; |
|
m_pSmartControl = NULL; |
|
m_pDisplayClass = NULL; |
|
m_pEditClass = NULL; |
|
m_eEditType = ivString; |
|
|
|
m_nNewKeyCount = 0; |
|
m_iSortColumn = -1; |
|
|
|
m_bEnableControlUpdate = true; |
|
|
|
m_pEditObjectRuntimeClass = RUNTIME_CLASS(editCEditGameClass); |
|
|
|
pModelBrowser = NULL; |
|
m_pInstanceVar = NULL; |
|
|
|
m_bCustomColorsLoaded = false; //Make sure they get loaded! |
|
memset(CustomColors, 0, sizeof(CustomColors)); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. |
|
//----------------------------------------------------------------------------- |
|
COP_Entity::~COP_Entity(void) |
|
{ |
|
DestroySmartControls(); |
|
|
|
delete pModelBrowser; |
|
pModelBrowser = NULL; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pDX - |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::DoDataExchange(CDataExchange* pDX) |
|
{ |
|
CObjectPage::DoDataExchange(pDX); |
|
//{{AFX_DATA_MAP(COP_Entity) |
|
DDX_Control(pDX, IDC_VALUE, m_cValue); |
|
DDX_Control(pDX, IDC_KEYVALUES, m_VarList); |
|
DDX_Control(pDX, IDC_KEY, m_cKey); |
|
DDX_Control(pDX, IDC_ENTITY_COMMENTS, m_Comments); |
|
DDX_Control(pDX, IDC_KEYVALUE_HELP, m_KeyValueHelpText); |
|
DDX_Control(pDX, IDC_PASTE, m_PasteControl); |
|
//}}AFX_DATA_MAP |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles notifications.. |
|
//----------------------------------------------------------------------------- |
|
BOOL COP_Entity::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) |
|
{ |
|
NMHDR *pHdr = (NMHDR*)lParam; |
|
if ( pHdr->idFrom == IDC_KEYVALUES ) |
|
{ |
|
if ( pHdr->code == LVN_COLUMNCLICK ) |
|
{ |
|
LPNMLISTVIEW pListView = (LPNMLISTVIEW)lParam; |
|
|
|
// Now sort by this column. |
|
m_iSortColumn = max( 0, min( pListView->iSubItem, ARRAYSIZE( g_ColumnSortFunctions ) - 1 ) ); |
|
ResortItems(); |
|
} |
|
} |
|
|
|
return CObjectPage::OnNotify(wParam, lParam, pResult); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Determine how/if this key's value has been modified relative |
|
// to its default in the FGD file. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::GetKeyState( const char *pShortName, EKeyState *pState, bool *pMissingTarget ) |
|
{ |
|
*pMissingTarget = false; |
|
|
|
// If we're in multiedit mode with various types of entities selected, then don't look at m_pDisplayClass. |
|
if ( !m_pDisplayClass ) |
|
{ |
|
*pState = k_EKeyState_DefaultFGDValue; |
|
return; |
|
} |
|
|
|
const char *pszCurValue = m_kv.GetValue( pShortName ); |
|
if ( !pszCurValue ) |
|
pszCurValue = ""; |
|
|
|
// Now see if this var is even in the FGD. |
|
GDinputvariable *pVar = m_pDisplayClass->VarForName( pShortName ); |
|
if ( !pVar ) |
|
{ |
|
if ( m_InstanceParmData.Find( pShortName ) != m_InstanceParmData.InvalidIndex() ) |
|
{ |
|
*pState = k_EKeyState_InstanceParm; |
|
} |
|
else |
|
{ |
|
*pState = k_EKeyState_AddedManually; |
|
} |
|
return; |
|
} |
|
|
|
// Missing targetname? |
|
if ((pVar->GetType() == ivTargetSrc) || (pVar->GetType() == ivTargetDest)) |
|
{ |
|
if ( pszCurValue[0] && !IsValidTargetName( pszCurValue ) ) |
|
*pMissingTarget = true; |
|
} |
|
|
|
// Now we know it's in the FGD, so see if the value has been modified from the default. |
|
GDinputvariable varCopy; |
|
varCopy = *pVar; |
|
MDkeyvalue tmpkv; |
|
varCopy.ResetDefaults(); |
|
varCopy.ToKeyValue( &tmpkv ); |
|
|
|
if ( Q_stricmp( pszCurValue, tmpkv.szValue ) == 0 ) |
|
*pState = k_EKeyState_DefaultFGDValue; |
|
else |
|
*pState = k_EKeyState_Modified; |
|
} |
|
|
|
|
|
void COP_Entity::ResortItems() |
|
{ |
|
m_VarList.SortItems( g_ColumnSortFunctions[m_iSortColumn+1], (LPARAM)this ); |
|
|
|
// Update m_VarMap if in smart edit mode. |
|
if ( m_bSmartedit ) |
|
{ |
|
for ( int i=0; i < m_VarList.GetItemCount(); i++ ) |
|
{ |
|
const char *pShortName = (const char*)m_VarList.GetItemData( i ); |
|
if ( !pShortName ) |
|
continue; |
|
|
|
int index = -1; |
|
if ( m_pDisplayClass && m_pDisplayClass->VarForName( pShortName, &index ) ) |
|
{ |
|
m_VarMap[i] = index; |
|
} |
|
else |
|
{ |
|
index = m_InstanceParmData.Find( pShortName ); |
|
if ( index != m_InstanceParmData.InvalidIndex() ) |
|
{ |
|
m_VarMap[i] = INSTANCE_VAR_MAP_START - index; |
|
} |
|
else |
|
{ |
|
m_VarMap[i] = -1; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void DumpKeyvalues(WCKeyValues &kv) |
|
{ |
|
for (int i = kv.GetFirst(); i != kv.GetInvalidIndex(); i=kv.GetNext( i ) ) |
|
{ |
|
DBG(" %d: %s\n", i, kv.GetKey(i)); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds an object's keys to our list of keys. If a given key is already |
|
// in the list, it is either ignored or set to a "different" if the value |
|
// is different from the value in our list. |
|
// Input : pEdit - Object whose keys are to be added to our list. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::MergeObjectKeyValues(CEditGameClass *pEdit) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::MergeObjectKeyValues", "Object Properties" ); |
|
for ( int i=pEdit->GetFirstKeyValue(); i != pEdit->GetInvalidKeyValue(); i=pEdit->GetNextKeyValue( i ) ) |
|
{ |
|
LPCTSTR pszCurValue = m_kv.GetValue(pEdit->GetKey(i)); |
|
if (pszCurValue == NULL) |
|
{ |
|
// |
|
// Doesn't exist yet - set current value. |
|
// |
|
m_kv.SetValue(pEdit->GetKey(i), pEdit->GetKeyValue(i)); |
|
} |
|
else if (strcmp(pszCurValue, pEdit->GetKeyValue(i))) |
|
{ |
|
// |
|
// Already present - we need to merge the value with the existing data. |
|
// |
|
MergeKeyValue(pEdit->GetKey(i)); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Updates the dialog's keyvalue data with a given keyvalue. If the |
|
// data can be merged in with existing data in a meaningful manner, |
|
// that will be done. If not, VALUE_DIFFERENT_STRING will be set to |
|
// indicate that not all objects have the same value for the key. |
|
// Input : pszKey - |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::MergeKeyValue(char const *pszKey) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::MergeKeyValue", "Object Properties" ); |
|
|
|
Assert(pszKey); |
|
if (!pszKey) |
|
{ |
|
return; |
|
} |
|
|
|
bool bHandled = false; |
|
|
|
if (m_pEditClass != NULL) |
|
{ |
|
GDinputvariable *pVar = m_pEditClass->VarForName(pszKey); |
|
if (pVar != NULL) |
|
{ |
|
switch (pVar->GetType()) |
|
{ |
|
case ivSideList: |
|
{ |
|
// |
|
// Merging sidelist keys is a little complicated. We build a string |
|
// representing the merged sidelists. |
|
// |
|
CMapFaceIDList FaceIDListFull; |
|
CMapFaceIDList FaceIDListPartial; |
|
|
|
GetFaceIDListsForKey(FaceIDListFull, FaceIDListPartial, pszKey); |
|
|
|
char szValue[KEYVALUE_MAX_VALUE_LENGTH]; |
|
CMapWorld::FaceID_FaceIDListsToString(szValue, sizeof(szValue), &FaceIDListFull, &FaceIDListPartial); |
|
m_kv.SetValue(pszKey, szValue); |
|
|
|
bHandled = true; |
|
break; |
|
} |
|
|
|
case ivAngle: |
|
{ |
|
// |
|
// We can't merge angles, so set the appropriate angles control to "different". |
|
// We'll catch the main angles control below, since it's supported even |
|
// for objects of an unknown class. |
|
// |
|
if (stricmp(pVar->GetName(), "angles")) |
|
{ |
|
m_SmartAngle.SetDifferent(true); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (!bHandled) |
|
{ |
|
// |
|
// Can't merge with current value - show a "different" string. |
|
// |
|
m_kv.SetValue(pszKey, VALUE_DIFFERENT_STRING); |
|
|
|
if (!stricmp(pszKey, "angles")) |
|
{ |
|
// We can't merge angles, so set the main angles control to "different". |
|
m_Angle.SetDifferent(true); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : Mode - |
|
// pData - |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::UpdateData( int Mode, PVOID pData, bool bCanEdit ) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::UpdateData", "Object Properties" ); |
|
|
|
//DBG("UpdateData\n"); |
|
//DumpKeyvalues(m_kv); |
|
|
|
__super::UpdateData( Mode, pData, bCanEdit ); |
|
|
|
if (!IsWindow(m_hWnd)) |
|
{ |
|
return; |
|
} |
|
|
|
if (GetFocus() == &m_cKey) |
|
{ |
|
OnKillfocusKey(); |
|
} |
|
|
|
if (Mode == LoadFinished) |
|
{ |
|
m_kvAdded.RemoveAll(); |
|
m_bAllowPresentProperties = true; |
|
PresentProperties(); |
|
return; |
|
} |
|
else if ( Mode == LoadData || Mode == LoadFirstData ) |
|
{ |
|
// Wait until the LoadFinished call to create all the controls, |
|
// otherwise it'll do a lot of unnecessary work. |
|
m_bAllowPresentProperties = false; |
|
} |
|
|
|
if (!pData) |
|
{ |
|
return; |
|
} |
|
|
|
CEditGameClass *pEdit = (CEditGameClass *)pData; |
|
char szBuf[KEYVALUE_MAX_VALUE_LENGTH]; |
|
|
|
if (Mode == LoadFirstData) |
|
{ |
|
LoadClassList(); |
|
|
|
m_nNewKeyCount = 1; |
|
|
|
QAngle vecAngles; |
|
pEdit->GetAngles(vecAngles); |
|
m_Angle.SetAngles(vecAngles, false); |
|
m_Angle.SetDifferent(false); |
|
|
|
#ifdef NAMES |
|
if(pEdit->pClass) |
|
{ |
|
sprintf(szBuf, "%s (%s)", pEdit->pClass->GetDescription(), pEdit->pClass->GetName()); |
|
} |
|
else |
|
#endif |
|
|
|
V_strcpy_safe( szBuf, pEdit->GetClassName() ); |
|
m_cClasses.AddSuggestion( szBuf ); // If we don't make sure it has this item in its list, it will do |
|
// Bad Things later on. This only happens when the FGD is missing an |
|
// entity that is in the map file. In that case, just let it be. |
|
// Check For Problems should ID the problem for them. |
|
m_cClasses.SelectItem(szBuf); |
|
m_bClassSelectionEmpty = false; |
|
|
|
// |
|
// Can't change the class of the worldspawn entity. |
|
// |
|
if ( pEdit->IsClass("worldspawn") || m_bCanEdit == false ) |
|
{ |
|
m_cClasses.EnableWindow(FALSE); |
|
} |
|
else |
|
{ |
|
m_cClasses.EnableWindow(TRUE); |
|
} |
|
|
|
// |
|
// Get the comments text from the entity. |
|
// |
|
m_Comments.SetWindowText(pEdit->GetComments()); |
|
|
|
// |
|
// Add entity's keys to our local storage |
|
// |
|
m_kv.RemoveAll(); |
|
for ( int i=pEdit->GetFirstKeyValue(); i != pEdit->GetInvalidKeyValue(); i=pEdit->GetNextKeyValue( i ) ) |
|
{ |
|
const char *pszKey = pEdit->GetKey(i); |
|
const char *pszValue = pEdit->GetKeyValue(i); |
|
if ((pszKey != NULL) && (pszValue != NULL)) |
|
{ |
|
m_kv.SetValue(pszKey, pszValue); |
|
} |
|
} |
|
|
|
UpdateEditClass(pEdit->GetClassName(), true); |
|
UpdateDisplayClass(pEdit->GetClassName()); |
|
|
|
SetCurKey(m_strLastKey); |
|
} |
|
else if (Mode == LoadData) |
|
{ |
|
// |
|
// Not first data - merge with current stuff. |
|
// |
|
|
|
// |
|
// Deal with class name. |
|
// |
|
CString str = m_cClasses.GetCurrentItem(); |
|
if ( m_bClassSelectionEmpty ) |
|
str = ""; |
|
|
|
if (strcmpi(str, pEdit->GetClassName())) |
|
{ |
|
// |
|
// Not the same - set class to be blank and |
|
// disable smartedit. |
|
// |
|
m_cClasses.ForceEditControlText( "" ); |
|
m_bClassSelectionEmpty = true; |
|
|
|
UpdateEditClass("", false); |
|
UpdateDisplayClass(""); |
|
} |
|
else |
|
{ |
|
LoadClassList(); |
|
m_cClasses.SelectItem( pEdit->GetClassName() ); |
|
} |
|
|
|
// |
|
// Mark the comments field as "(different)" if it isn't the same as this entity's |
|
// comments. |
|
// |
|
char szComments[1024]; |
|
m_Comments.GetWindowText(szComments, sizeof(szComments)); |
|
if (strcmp(szComments, pEdit->GetComments()) != 0) |
|
{ |
|
m_Comments.SetWindowText(VALUE_DIFFERENT_STRING); |
|
} |
|
|
|
MergeObjectKeyValues(pEdit); |
|
SetCurKey(m_strLastKey); |
|
} |
|
else |
|
{ |
|
Assert(FALSE); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stops entity, face, or angles picking. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::StopPicking(void) |
|
{ |
|
if (m_bPicking) |
|
{ |
|
m_bPicking = false; |
|
ToolManager()->SetTool(m_ToolPrePick); |
|
|
|
// |
|
// Stop angles picking if we are doing so. |
|
// |
|
CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_ANGLES); |
|
if (pButton) |
|
{ |
|
pButton->SetCheck(0); |
|
} |
|
|
|
// |
|
// Stop entity picking if we are doing so. |
|
// |
|
pButton = (CButton *)GetDlgItem(IDC_PICK_ENTITY); |
|
if (pButton) |
|
{ |
|
pButton->SetCheck(0); |
|
} |
|
|
|
// |
|
// Stop face picking if we are doing so. |
|
// |
|
pButton = (CButton *)GetDlgItem(IDC_PICK_FACES); |
|
if (pButton) |
|
{ |
|
pButton->SetCheck(0); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called manually from CObjectProperties::OnApply because the Apply |
|
// button is implemented in a nonstandard way. I'm not sure why. |
|
//----------------------------------------------------------------------------- |
|
BOOL COP_Entity::OnApply(void) |
|
{ |
|
m_pLastSmartControlVar = NULL; // Force it to recreate the target name combo if need be, |
|
// because we might have locked in a new targetname. |
|
StopPicking(); |
|
return(TRUE); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pEntity - |
|
// pszKey - |
|
// pszValue - |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::ApplyKeyValueToObject(CEditGameClass *pObject, const char *pszKey, const char *pszValue) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::ApplyKeyValueToObject", "Object Properties" ); |
|
|
|
GDclass *pClass = pObject->GetClass(); |
|
if (pClass != NULL) |
|
{ |
|
GDinputvariable *pVar = pClass->VarForName(pszKey); |
|
if (pVar != NULL) |
|
{ |
|
if ((pVar->GetType() == ivSideList) || (pVar->GetType() == ivSide)) |
|
{ |
|
CMapWorld *pWorld = GetActiveWorld(); |
|
|
|
// |
|
// Get the face list currently set in this keyvalue. |
|
// |
|
CMapFaceIDList CurFaceList; |
|
const char *pszCurVal = pObject->GetKeyValue(pszKey); |
|
if (pszCurVal != NULL) |
|
{ |
|
pWorld->FaceID_StringToFaceIDLists(&CurFaceList, NULL, pszCurVal); |
|
} |
|
|
|
// |
|
// Build the face list to apply. Only include the faces that are: |
|
// |
|
// 1. In the full selection list (outside the parentheses). |
|
// 2. In the partial selection set (inside the parentheses) AND are in the |
|
// original face list. |
|
// |
|
// FACEID TODO: Optimize so that we only call StringToFaceList once per keyvalue |
|
// instead of once per keyvalue per entity being applied to. |
|
CMapFaceIDList FullFaceList; |
|
CMapFaceIDList PartialFaceList; |
|
pWorld->FaceID_StringToFaceIDLists(&FullFaceList, &PartialFaceList, pszValue); |
|
|
|
CMapFaceIDList KeepFaceList; |
|
for (int i = 0; i < PartialFaceList.Count(); i++) |
|
{ |
|
int nFace = PartialFaceList.Element(i); |
|
|
|
if (CurFaceList.Find(nFace) != -1) |
|
{ |
|
KeepFaceList.AddToTail(nFace); |
|
} |
|
} |
|
|
|
FullFaceList.AddVectorToTail(KeepFaceList); |
|
|
|
char szSetValue[KEYVALUE_MAX_VALUE_LENGTH]; |
|
CMapWorld::FaceID_FaceIDListsToString(szSetValue, sizeof(szSetValue), &FullFaceList, NULL); |
|
|
|
pObject->SetKeyValue(pszKey, szSetValue); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
pObject->SetKeyValue(pszKey, pszValue); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by the sheet to let the page remember its state before a |
|
// refresh of the data. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::RememberState(void) |
|
{ |
|
GetCurKey(m_strLastKey); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by the object properties page to tell us that all our data |
|
// is dirty. We don't have to do anything on most of our data because it'll |
|
// get regenerated, but we must clear out our class pointers because we'll |
|
// do crazy things if they give us a new class and have us load the data |
|
// (we'll think it just changed from one class to another and we'll wipe |
|
// out spawnflags, for instance). So clear out the class pointers. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::MarkDataDirty() |
|
{ |
|
UpdateDisplayClass( (GDclass*)NULL ); |
|
m_pEditClass = NULL; |
|
m_VarList.DeleteAllItems(); |
|
m_InstanceParmData.RemoveAll(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Saves the dialog data into the objects being edited. |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool COP_Entity::SaveData(void) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::SaveData", "Object Properties" ); |
|
|
|
//DBG("SaveData\n"); |
|
//DumpKeyvalues(m_kv); |
|
|
|
RememberState(); |
|
|
|
CString strClassName = m_cClasses.GetCurrentItem(); |
|
|
|
// If we were multiselecting entities and they haven't chosen a new classname yet, |
|
// we need to know that here so we don't force them all to be the last selection in the classname combo. |
|
if ( m_bClassSelectionEmpty ) |
|
strClassName = ""; |
|
|
|
UpdateEditClass(strClassName, false); |
|
|
|
// |
|
// Apply the dialog data to all the objects being edited. |
|
// |
|
FOR_EACH_OBJ( *m_pObjectList, pos ) |
|
{ |
|
CMapClass *pObject = m_pObjectList->Element(pos); |
|
CEditGameClass *pEdit = dynamic_cast <CEditGameClass *>(pObject); |
|
Assert(pEdit != NULL); |
|
|
|
if (pEdit != NULL) |
|
{ |
|
RemoveBlankKeys(); |
|
|
|
// |
|
// Give keys back to object. For every key in our local storage that is |
|
// also found in the list of added keys, set the key value in the edit |
|
// object(s). |
|
// |
|
for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) ) |
|
{ |
|
MDkeyvalue &kvCur = m_kv.GetKeyValue(i); |
|
const char *pszAddedKeyValue = m_kvAdded.GetValue(kvCur.szKey); |
|
if (pszAddedKeyValue != NULL) |
|
{ |
|
Q_FixSlashes( kvCur.szValue, '/' ); |
|
// |
|
// Don't store keys with multiple/undefined values. |
|
// |
|
if (strcmp(kvCur.szValue, VALUE_DIFFERENT_STRING)) |
|
{ |
|
//DBG(" apply key %s\n", kvCur.szKey); |
|
ApplyKeyValueToObject(pEdit, kvCur.szKey, kvCur.szValue); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// All keys in the object should also be found in local storage, |
|
// unless the user deleted them. If there are any such extraneous |
|
// keys, get rid of them. Go from the top down because otherwise |
|
// deleting messes up our iteration. |
|
// |
|
int iNext; |
|
for ( int i=pEdit->GetFirstKeyValue(); i != pEdit->GetInvalidKeyValue(); i=iNext ) |
|
{ |
|
iNext = pEdit->GetNextKeyValue( i ); |
|
// |
|
// If this key is in not in our local storage, delete it from the object. |
|
// |
|
if (!m_kv.GetValue(pEdit->GetKey(i))) |
|
{ |
|
//DBG(" delete key %s\n", pEdit->GetKey(i)); |
|
pEdit->DeleteKeyValue(pEdit->GetKey(i)); |
|
} |
|
} |
|
|
|
// |
|
// Store class. |
|
// |
|
if (strClassName[0]) |
|
{ |
|
pEdit->SetClass(strClassName); |
|
} |
|
|
|
// |
|
// Store the entity comments. |
|
// |
|
char szComments[1024]; |
|
szComments[0] = '\0'; |
|
m_Comments.GetWindowText(szComments, sizeof(szComments)); |
|
if (strcmp(szComments, VALUE_DIFFERENT_STRING) != 0) |
|
{ |
|
pEdit->SetComments(szComments); |
|
} |
|
} |
|
|
|
pObject->PostUpdate(Notify_Changed); |
|
} |
|
|
|
UpdateDisplayClass(strClassName); |
|
|
|
return(true); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Given the short name of a key, find which row it's at in the list control. |
|
//----------------------------------------------------------------------------- |
|
int COP_Entity::GetKeyValueRowByShortName( const char *pShortName ) |
|
{ |
|
const char *pSearchString = pShortName; |
|
if ( m_bSmartedit ) |
|
{ |
|
if ( m_pDisplayClass ) |
|
{ |
|
GDinputvariable *pVar = m_pDisplayClass->VarForName( pShortName ); |
|
if (pVar) |
|
pSearchString = pVar->GetLongName(); |
|
} |
|
} |
|
|
|
LVFINDINFO fi; |
|
memset( &fi, 0, sizeof( fi ) ); |
|
fi.flags = LVFI_STRING; |
|
fi.psz = pSearchString; |
|
return m_VarList.FindItem( &fi ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Fills the values in the second column for all properties. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::RefreshKVListValues( const char *pOnlyThisVar ) |
|
{ |
|
// We match listctrl elements to their values in 2 different ways: |
|
// 1. In smartedit mode, the raw name of the property in the first column is matched to m_kv. |
|
// 2. In non-smartedit mode, we look at the lParam entry in the listctrl. |
|
for ( int i=0; i < m_VarList.GetItemCount(); i++ ) |
|
{ |
|
const char *pVarName = (const char*)m_VarList.GetItemData( i ); |
|
const char *pValue = NULL; |
|
char tmpValueBuf[512]; |
|
|
|
// If they only wanted to update one var... |
|
if ( pOnlyThisVar && Q_stricmp( pVarName, pOnlyThisVar ) != 0 ) |
|
continue; |
|
|
|
if ( m_bSmartedit ) |
|
{ |
|
GDinputvariable *pVar = (m_pDisplayClass ? m_pDisplayClass->VarForName( pVarName ) : NULL); |
|
if ( pVar ) |
|
{ |
|
const char *pUnformattedValue = m_kv.GetValue( pVar->GetName() ); |
|
pValue = pUnformattedValue; // Default is unformatted. |
|
|
|
if ( pUnformattedValue ) |
|
{ |
|
// Do special formatting for various value types. |
|
GDIV_TYPE eType = pVar->GetType(); |
|
if ( eType == ivChoices ) |
|
{ |
|
if ( pUnformattedValue ) |
|
{ |
|
const char *pTestValue = pVar->ItemStringForValue( pUnformattedValue ); |
|
if ( pTestValue ) |
|
pValue = pTestValue; |
|
} |
|
} |
|
else if ( |
|
(eType == ivStudioModel) || (eType == ivSprite) || (eType == ivSound) || (eType == ivDecal) || |
|
(eType == ivMaterial) || (eType == ivScene) ) |
|
{ |
|
// It's a filename.. just show the filename and not the directory. They can look at the |
|
// full filename in the smart control if they want. |
|
const char *pLastSlash = max( strrchr( pUnformattedValue, '\\' ), strrchr( pUnformattedValue, '/' ) ); |
|
if ( pLastSlash ) |
|
{ |
|
Q_strncpy( tmpValueBuf, pLastSlash+1, sizeof( tmpValueBuf ) ); |
|
pValue = tmpValueBuf; |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
pValue = m_kv.GetValue( pVarName ); // This was probably added in dumbedit mode. |
|
} |
|
} |
|
else |
|
{ |
|
pValue = m_kv.GetValue( pVarName ); |
|
} |
|
|
|
if ( pValue ) |
|
m_VarList.SetItemText( i, 1, pValue ); |
|
else |
|
m_VarList.SetItemText( i, 1, "" ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets up the current mode (smartedit/non) after loading an object |
|
// or toggling the SmartEdit button. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::PresentProperties() |
|
{ |
|
if ( !m_bAllowPresentProperties ) |
|
return; |
|
|
|
++m_nPresentPropertiesCalls; |
|
|
|
m_VarList.SetRedraw( FALSE ); |
|
ClearVarList(); |
|
|
|
if (!m_bSmartedit || !m_pDisplayClass) |
|
{ |
|
RemoveBlankKeys(); |
|
|
|
for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) ) |
|
{ |
|
MDkeyvalue &KeyValue = m_kv.GetKeyValue(i); |
|
|
|
int iItem = m_VarList.InsertItem( i, KeyValue.szKey ); |
|
m_VarList.SetItemData( iItem, (DWORD)KeyValue.szKey ); |
|
} |
|
|
|
m_Angle.Enable( m_bCanEdit ); |
|
} |
|
else |
|
{ |
|
// Too many entity variables! Increase GD_MAX_VARIABLES if you get this assertion. |
|
Assert(m_pDisplayClass->GetVariableCount() <= GD_MAX_VARIABLES); |
|
|
|
for ( int i=0; i < ARRAYSIZE(m_VarMap); i++ ) |
|
{ |
|
m_VarMap[i] = -1; |
|
} |
|
|
|
// |
|
// Add all the keys from the entity's class to the listbox. If the key is not already |
|
// in the entity, add it to the m_kvAdded list as well. |
|
// |
|
for (int i = 0; i < m_pDisplayClass->GetVariableCount(); i++) |
|
{ |
|
GDinputvariable *pVar = m_pDisplayClass->GetVariableAt(i); |
|
|
|
// |
|
// Spawnflags are handled separately - don't add that key. |
|
// |
|
if (strcmpi(pVar->GetName(), SPAWNFLAGS_KEYNAME) != 0) |
|
{ |
|
int iItem = m_VarList.InsertItem( i, pVar->GetLongName() ); |
|
m_VarList.SetItemData( iItem, (DWORD)pVar->GetName() ); |
|
} |
|
} |
|
|
|
if ( m_pObjectList->Count() == 1 ) |
|
{ |
|
CMapEntity *pEntity = static_cast< CMapEntity * >( m_pObjectList->Element( 0 ) ); |
|
|
|
CMapInstance *pMapInstance = pEntity->GetChildOfType( ( CMapInstance * )NULL ); |
|
if ( pMapInstance && pMapInstance->GetInstancedMap() ) |
|
{ |
|
CMapEntityList entityList; |
|
|
|
pMapInstance->GetInstancedMap()->FindEntitiesByClassName( entityList, "func_instance_parms", false ); |
|
if ( entityList.Count() == 1 ) |
|
{ |
|
CMapEntity *pInstanceParmsEntity = entityList.Element( 0 ); |
|
|
|
for ( int i = pInstanceParmsEntity->GetFirstKeyValue(); i != pInstanceParmsEntity->GetInvalidKeyValue(); i = pInstanceParmsEntity->GetNextKeyValue( i ) ) |
|
{ |
|
LPCTSTR pInstanceKey = pInstanceParmsEntity->GetKey( i ); |
|
LPCTSTR pInstanceValue = pInstanceParmsEntity->GetKeyValue( i ); |
|
|
|
if ( strnicmp( pInstanceKey, "parm", strlen( "parm" ) ) == 0 ) |
|
{ |
|
char ValueData[ KEYVALUE_MAX_KEY_LENGTH ]; |
|
const char *pVariable, *pReplace; |
|
|
|
pVariable = pReplace = ""; |
|
if ( pInstanceValue ) |
|
{ |
|
strcpy( ValueData, pInstanceValue ); |
|
pVariable = ValueData; |
|
char *pos = strchr( ValueData, ' ' ); |
|
if ( pos ) |
|
{ |
|
*pos = 0; |
|
pos++; |
|
pReplace = pos; |
|
} |
|
} |
|
else |
|
{ |
|
continue; |
|
} |
|
|
|
for ( int j = pEntity->GetFirstKeyValue(); j != pEntity->GetInvalidKeyValue(); j = pEntity->GetNextKeyValue( j ) ) |
|
{ |
|
LPCTSTR pKey = pEntity->GetKey( j ); |
|
LPCTSTR pValue = pEntity->GetKeyValue( j ); |
|
|
|
if ( strnicmp( pValue, pVariable, strlen( pVariable ) ) == 0 ) |
|
{ |
|
CInstanceParmData InstanceParmData; |
|
|
|
InstanceParmData.m_ParmVariable = new GDinputvariable( pReplace, pVariable ); |
|
InstanceParmData.m_ParmKey = pKey; |
|
InstanceParmData.m_VariableName = pVariable; |
|
int InsertIndex = m_InstanceParmData.Insert( InstanceParmData.m_VariableName, InstanceParmData ); |
|
|
|
const char *ptr = m_InstanceParmData[ InsertIndex ].m_VariableName; |
|
int iItem = m_VarList.InsertItem( 0, ptr ); |
|
m_VarList.SetItemData( iItem, (DWORD)ptr ); |
|
m_kv.SetValue( pVariable, pValue + strlen( pVariable ) + 1 ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// rj: this must be after the instancing check above, as adding keyvalues to m_kv can cause the list to get reallocated, causing the SetItemData to have an invalid pointer |
|
// Also add any keyvalues they added in dumbedit mode. These will show up in red. |
|
for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) ) |
|
{ |
|
MDkeyvalue &KeyValue = m_kv.GetKeyValue(i); |
|
|
|
if ( !m_pDisplayClass->VarForName( KeyValue.szKey ) && m_InstanceParmData.Find( KeyValue.szKey ) == m_InstanceParmData.InvalidIndex() ) |
|
{ |
|
int iItem = m_VarList.InsertItem( i, KeyValue.szKey ); |
|
m_VarList.SetItemData( iItem, (DWORD)KeyValue.szKey ); |
|
} |
|
} |
|
|
|
// |
|
// If this class defines angles, enable the angles control. |
|
// |
|
if (m_pDisplayClass->VarForName("angles") != NULL) |
|
{ |
|
m_Angle.Enable( m_bCanEdit ); |
|
} |
|
else |
|
{ |
|
m_Angle.Enable(false); |
|
} |
|
} |
|
|
|
RefreshKVListValues(); |
|
ResortItems(); |
|
|
|
SetCurKey(m_strLastKey); |
|
m_VarList.SetRedraw( TRUE ); |
|
m_VarList.Invalidate( FALSE ); |
|
} |
|
|
|
|
|
void COP_Entity::ClearVarList() |
|
{ |
|
m_VarList.DeleteAllItems(); |
|
m_InstanceParmData.RemoveAll(); |
|
|
|
for ( int i=0; i < ARRAYSIZE( m_VarMap ); i++ ) |
|
m_VarMap[i] = -1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Removes any keys with blank values from our local keyvalue storage. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::RemoveBlankKeys(void) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::RemoveBlankKeys", "Object Properties" ); |
|
|
|
int iNext; |
|
for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=iNext) |
|
{ |
|
iNext = m_kv.GetNext( i ); |
|
|
|
MDkeyvalue &KeyValue = m_kv.GetKeyValue(i); |
|
if (KeyValue.szValue[0] == '\0') |
|
{ |
|
bool bRemove = true; |
|
|
|
#if 0 |
|
// Only remove keys that are blank and whose default value is not blank, |
|
// because Hammer assigns any missing key with the FGD's default value. |
|
// |
|
// dvs: disabled for now because deleting the value text is the currently |
|
// accepted way of reverting a key to its default value. |
|
GDinputvariable *pVar = m_pDisplayClass->VarForName( KeyValue.szKey ); |
|
if ( pVar ) |
|
{ |
|
char szDefault[MAX_KEYVALUE_LEN]; |
|
pVar->GetDefault( szDefault ); |
|
if ( szDefault[0] != '\0' ) |
|
{ |
|
bRemove = false; |
|
} |
|
} |
|
#endif |
|
|
|
if ( bRemove ) |
|
{ |
|
m_kv.RemoveKeyAt(i); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::LoadClassList(void) |
|
{ |
|
CEditGameClass *pEdit = (CEditGameClass*) GetEditObject(); |
|
const char *pWorldSpawnString = "worldspawn"; |
|
int iSolidClasses = -1; |
|
|
|
if (pEdit->IsClass()) |
|
{ |
|
if ( pEdit->IsClass( pWorldSpawnString ) ) |
|
{ |
|
iSolidClasses = 2; |
|
} |
|
else if (pEdit->IsSolidClass()) |
|
{ |
|
iSolidClasses = 1; |
|
} |
|
else |
|
{ |
|
iSolidClasses = 0; |
|
} |
|
} |
|
|
|
// Ok, we've already initialized the list with the same list. Don't do it over again. |
|
if ( m_iLastClassListSolidClasses == iSolidClasses ) |
|
return; |
|
|
|
CUtlVector<CString> suggestions; |
|
if ( iSolidClasses == 2 ) |
|
{ |
|
suggestions.AddToTail( pWorldSpawnString ); |
|
} |
|
else |
|
{ |
|
int nCount = pGD->GetClassCount(); |
|
CString str; |
|
for (int i =0; i < nCount; i++) |
|
{ |
|
GDclass *pc = pGD->GetClass(i); |
|
if (!pc->IsBaseClass()) |
|
{ |
|
if (iSolidClasses == -1 || (iSolidClasses == (int)pc->IsSolidClass())) |
|
{ |
|
#ifdef NAMES |
|
str.Format("%s (%s)", pc->GetDescription(), pc->GetName()); |
|
#else |
|
str = pc->GetName(); |
|
#endif |
|
|
|
if (!pc->IsClass( pWorldSpawnString )) |
|
{ |
|
suggestions.AddToTail( str ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
m_iLastClassListSolidClasses = iSolidClasses; |
|
m_cClasses.SetSuggestions( suggestions, 0 ); |
|
|
|
// Add this class' class name in case it's not in the list yet. |
|
m_cClasses.AddSuggestion( pEdit->GetClassNameA() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
BOOL COP_Entity::OnInitDialog(void) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::OnInitDialog", "Object Properties" ); |
|
|
|
CObjectPage::OnInitDialog(); |
|
|
|
// Sometimes it has deleted our window (and its children) from underneath us, |
|
// and we don't want to hang onto old window pointers. |
|
m_SmartControls.Purge(); |
|
m_pDisplayClass = NULL; |
|
m_pEditClass = NULL; |
|
m_pLastSmartControlVar = NULL; |
|
|
|
// Clear m_kv. |
|
m_kv.RemoveAll(); |
|
ClearVarList(); |
|
|
|
// Hook up the main angles controls. |
|
m_Angle.SubclassDlgItem(IDC_ANGLEBOX, this); |
|
m_AngleEdit.SubclassDlgItem(IDC_ANGLEEDIT, this); |
|
m_Angle.SetEditControl(&m_AngleEdit); |
|
m_AngleEdit.SetAngleBox(&m_Angle); |
|
|
|
// Hook up the smart angles controls. |
|
m_SmartAngle.SubclassDlgItem(IDC_SMART_ANGLEBOX, this); |
|
m_SmartAngleEdit.SubclassDlgItem(IDC_SMART_ANGLEEDIT, this); |
|
m_SmartAngle.SetEditControl(&m_SmartAngleEdit); |
|
m_SmartAngleEdit.SetAngleBox(&m_SmartAngle); |
|
|
|
// Hook up the classes autoselect combo. |
|
m_cClasses.SubclassDlgItem(IDC_CLASS, this); |
|
|
|
// Hook up the pick color button. |
|
m_cPickColor.SubclassDlgItem(IDC_PICKCOLOR, this); |
|
|
|
LoadClassList(); |
|
|
|
//m_VarList.SetTabStops(65); |
|
|
|
// Put our varlist in the right mode. |
|
DWORD dwStyle = GetWindowLong( m_VarList.GetSafeHwnd(), GWL_STYLE ); |
|
dwStyle &= ~(LVS_ICON | LVS_LIST | LVS_SMALLICON); |
|
dwStyle |= LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_OWNERDRAWFIXED; |
|
SetWindowLong( m_VarList.GetSafeHwnd(), GWL_STYLE, dwStyle ); |
|
|
|
m_VarList.SetExtendedStyle( m_VarList.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES ); |
|
m_VarList.InsertColumn(0, "Property Name", LVCFMT_LEFT, 200); |
|
m_VarList.InsertColumn(1, "Value", LVCFMT_LEFT, 150); |
|
|
|
m_bWantSmartedit = true; |
|
SetSmartedit(false); |
|
|
|
UpdateAnchors(); |
|
return TRUE; |
|
} |
|
|
|
|
|
void COP_Entity::UpdateAnchors() |
|
{ |
|
CAnchorDef anchorDefs[] = |
|
{ |
|
CAnchorDef( IDC_KEYVALUES, k_eSimpleAnchorAllSides ), |
|
CAnchorDef( IDC_SMARTEDIT, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_ENTITY_HELP, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_ANGLES_LABEL, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_ANGLEEDIT, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_ANGLEBOX, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_KEY_LABEL, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_KEY, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_VALUE_LABEL, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_PICKCOLOR, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_SMART_ANGLEEDIT, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_SMART_ANGLEBOX, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_VALUE, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_KEYVALUE_HELP_GROUP, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_KEYVALUE_HELP, k_eAnchorRight, k_eAnchorTop, k_eAnchorRight, k_eAnchorBottom ), |
|
CAnchorDef( IDC_COMMENTS_LABEL, k_eSimpleAnchorBottomRight ), |
|
CAnchorDef( IDC_ENTITY_COMMENTS, k_eAnchorRight, k_eAnchorBottom, k_eAnchorRight, k_eAnchorBottom ), |
|
CAnchorDef( IDC_ADDKEYVALUE, k_eSimpleAnchorRightSide ), |
|
CAnchorDef( IDC_REMOVEKEYVALUE, k_eSimpleAnchorRightSide ) |
|
}; |
|
CUtlVector<CAnchorDef> defs; |
|
defs.CopyArray( anchorDefs, ARRAYSIZE( anchorDefs ) ); |
|
|
|
for ( int i=0; i < m_SmartControls.Count(); i++ ) |
|
{ |
|
defs.AddToTail( CAnchorDef( m_SmartControls[i]->GetSafeHwnd(), k_eSimpleAnchorRightSide ) ); |
|
} |
|
|
|
m_AnchorMgr.Init( GetSafeHwnd(), defs.Base(), defs.Count() ); |
|
} |
|
|
|
|
|
int COP_Entity::GetCurVarListSelection() |
|
{ |
|
POSITION pos = m_VarList.GetFirstSelectedItemPosition(); |
|
if ( pos ) |
|
{ |
|
return m_VarList.GetNextSelectedItem( pos ); |
|
} |
|
else |
|
{ |
|
return LB_ERR; |
|
} |
|
} |
|
|
|
|
|
void COP_Entity::OnItemChangedKeyValues(NMHDR* pNMHDR, LRESULT* pResult) |
|
{ |
|
OnSelchangeKeyvalues(); |
|
} |
|
|
|
|
|
void COP_Entity::OnDblClickKeyValues(NMHDR* pNMHDR, LRESULT* pResult) |
|
{ |
|
// Do smart stuff if we're in SmartEdit mode. |
|
if ( !m_bSmartedit ) |
|
return; |
|
|
|
int iSel = GetCurVarListSelection(); |
|
if ( iSel == LB_ERR ) |
|
return; |
|
|
|
GDinputvariable *pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( pVar->GetType() == ivColor255 || pVar->GetType() == ivColor1 ) |
|
{ |
|
OnPickColor(); |
|
} |
|
else if ( m_pSmartControl ) |
|
{ |
|
if ( m_pSmartBrowseButton ) |
|
{ |
|
OnBrowse(); |
|
} |
|
else if ( dynamic_cast< CMyEdit* >( m_pSmartControl ) ) |
|
{ |
|
m_pSmartControl->SetFocus(); |
|
m_pSmartControl->SendMessage( EM_SETSEL, 0, -1 ); |
|
} |
|
else if ( dynamic_cast< CMyComboBox* >( m_pSmartControl ) || dynamic_cast< CTargetNameComboBox* >( m_pSmartControl ) ) |
|
{ |
|
m_pSmartControl->SetFocus(); |
|
} |
|
} |
|
} |
|
|
|
|
|
void COP_Entity::SetCurVarListSelection( int iSel ) |
|
{ |
|
// Deselect everything. |
|
POSITION pos = m_VarList.GetFirstSelectedItemPosition(); |
|
while ( pos ) |
|
{ |
|
int iItem = m_VarList.GetNextSelectedItem( pos ); |
|
m_VarList.SetItemState( iItem, 0, LVIS_SELECTED ); |
|
} |
|
|
|
// Now set selection on the item we want. |
|
m_VarList.SetItemState( iSel, LVIS_SELECTED, LVIS_SELECTED ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets the name of the currently selected key in the properties list. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::GetCurKey(CString &strKey) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::GetCurKey", "Object Properties" ); |
|
|
|
int iSel = GetCurVarListSelection(); |
|
if (iSel == -1) |
|
{ |
|
strKey = ""; |
|
return; |
|
} |
|
|
|
strKey = (CString)(const char*)m_VarList.GetItemData( iSel ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Selects the given key in the list of keys. If the key is not in the |
|
// list, the first key is selected. |
|
// Input : pszKey - Name of key to select. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::SetCurKey(LPCTSTR pszKey) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::SetCurKey", "Object Properties" ); |
|
|
|
int nSel = m_VarList.GetItemCount(); |
|
|
|
for (int i = 0; i < nSel; i++) |
|
{ |
|
CString str = (CString)(const char*)m_VarList.GetItemData( i ); |
|
if ( !Q_stricmp( str, pszKey ) ) |
|
{ |
|
// found it here - |
|
SetCurVarListSelection( i ); |
|
|
|
// FIXME: Ideally we'd only call OnSelChangeKeyvalues if the selection index |
|
// actually changed, but that makes the keyvalue text not refresh properly |
|
// when multiselecting entities with a sidelist key selected. |
|
if ( m_bSmartedit ) |
|
{ |
|
OnSelchangeKeyvalues(); |
|
} |
|
return; |
|
} |
|
} |
|
|
|
// |
|
// Not found, select the first key in the list. |
|
// |
|
SetCurVarListSelection( 0 ); |
|
OnSelchangeKeyvalues(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::DestroySmartControls(void) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::DestroySmartControls", "Object Properties" ); |
|
|
|
for (int i = 0; i < m_SmartControls.Count(); i++) |
|
{ |
|
CWnd *pControl = m_SmartControls.Element(i); |
|
if (pControl != NULL) |
|
{ |
|
pControl->DestroyWindow(); |
|
delete pControl; |
|
} |
|
} |
|
|
|
m_SmartControls.RemoveAll(); |
|
m_pSmartBrowseButton = NULL; |
|
m_pSmartControl = NULL; |
|
m_pLastSmartControlVar = NULL; |
|
|
|
m_pEditInstanceVariable = NULL; |
|
m_pEditInstanceValue = NULL; |
|
m_pComboInstanceParmType = NULL; |
|
|
|
UpdateAnchors(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Creates the smart controls based on the given type. Deletes any |
|
// smart controls that are not appropriate to the given type. |
|
// Input : eType - Type of keyvalue for which to create controls. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::CreateSmartControls(GDinputvariable *pVar, CUtlVector<const char *>*pHelperType) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::CreateSmartControls", "Object Properties" ); |
|
|
|
// dvs: TODO: break this monster up into smaller functions |
|
if (pVar == NULL) |
|
{ |
|
// UNDONE: delete smart controls? |
|
return; |
|
} |
|
|
|
CString strValue = m_kv.GetValue( pVar->GetName() ); |
|
|
|
// If this is the same var that our smart controls are already setup for, then don't do anything. |
|
if ( m_SmartControls.Count() > 0 && |
|
pVar == m_pLastSmartControlVar && |
|
Q_stricmp( strValue, m_LastSmartControlVarValue ) == 0 ) |
|
{ |
|
return; |
|
} |
|
|
|
// |
|
// Set this so that we don't process notifications when we set the edit text, |
|
// which makes us do things like assume the user changed a key when they didn't. |
|
// |
|
m_bIgnoreKVChange = true; |
|
|
|
DestroySmartControls(); |
|
m_pLastSmartControlVar = pVar; |
|
m_LastSmartControlVarValue = strValue; |
|
|
|
// |
|
// Build a rectangle in which to create a new control. |
|
// |
|
CRect ctrlrect = CalculateSmartControlRect(); |
|
|
|
GDIV_TYPE eType = pVar->GetType(); |
|
|
|
// |
|
// Hide or show color button. |
|
// |
|
m_cPickColor.ShowWindow((eType == ivColor255 || eType == ivColor1) ? SW_SHOW : SW_HIDE); |
|
|
|
HFONT hControlFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); |
|
if (hControlFont == NULL) |
|
{ |
|
hControlFont = (HFONT)GetStockObject(ANSI_VAR_FONT); |
|
} |
|
|
|
// |
|
// Hide or show the Smart angles controls. |
|
// |
|
bool bShowSmartAngles = false; |
|
if (eType == ivAngle) |
|
{ |
|
CreateSmartControls_Angle( pVar, ctrlrect, hControlFont, &bShowSmartAngles ); |
|
} |
|
|
|
m_SmartAngle.ShowWindow(bShowSmartAngles ? SW_SHOW : SW_HIDE); |
|
m_SmartAngleEdit.ShowWindow(bShowSmartAngles ? SW_SHOW : SW_HIDE); |
|
|
|
// |
|
// Choices, NPC classes and filter classes get a combo box. |
|
// |
|
if ((eType == ivChoices) || (eType == ivNPCClass) || (eType == ivFilterClass) || (eType == ivPointEntityClass) ) |
|
{ |
|
CreateSmartControls_Choices( pVar, ctrlrect, hControlFont ); |
|
} |
|
else if ( eType == ivInstanceVariable ) |
|
{ |
|
CreateSmartControls_InstanceVariable( pVar, ctrlrect, hControlFont ); |
|
} |
|
else if ( eType == ivInstanceParm ) |
|
{ |
|
CreateSmartControls_InstanceParm( pVar, ctrlrect, hControlFont ); |
|
} |
|
else |
|
{ |
|
if ((eType == ivTargetSrc) || (eType == ivTargetDest)) |
|
{ |
|
CreateSmartControls_TargetName( pVar, ctrlrect, hControlFont ); |
|
} |
|
else |
|
{ |
|
CreateSmartControls_BasicEditControl( pVar, ctrlrect, hControlFont, pHelperType ); |
|
} |
|
|
|
// |
|
// Create a "Browse..." button for browsing for files. |
|
// |
|
if ((eType == ivStudioModel) || (eType == ivSprite) || (eType == ivSound) || (eType == ivDecal) || |
|
(eType == ivMaterial) || (eType == ivScene) || ( eType == ivInstanceFile ) ) |
|
{ |
|
CreateSmartControls_BrowseAndPlayButtons( pVar, ctrlrect, hControlFont ); |
|
} |
|
else if ((eType == ivTargetDest) || (eType == ivTargetNameOrClass) || (eType == ivTargetSrc) || (eType == ivNodeDest)) |
|
{ |
|
CreateSmartControls_MarkAndEyedropperButtons( pVar, ctrlrect, hControlFont ); |
|
} |
|
else if ((eType == ivSide) || (eType == ivSideList)) |
|
{ |
|
CreateSmartControls_PickButton( pVar, ctrlrect, hControlFont ); |
|
} |
|
} |
|
|
|
m_pSmartControl->ShowWindow(SW_SHOW); |
|
m_pSmartControl->SetWindowPos(&m_VarList, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); |
|
|
|
m_bIgnoreKVChange = false; |
|
UpdateAnchors(); |
|
} |
|
|
|
|
|
CRect COP_Entity::CalculateSmartControlRect() |
|
{ |
|
CRect ctrlrect; |
|
|
|
GetDlgItem(IDC_KEY)->GetWindowRect(ctrlrect); |
|
ScreenToClient(ctrlrect); |
|
int nHeight = ctrlrect.Height(); |
|
int nRight = ctrlrect.right - 10; |
|
|
|
// Put it just to the right of the keyvalue list. |
|
m_VarList.GetWindowRect(ctrlrect); |
|
ScreenToClient(ctrlrect); |
|
|
|
ctrlrect.left = ctrlrect.right + 10; |
|
ctrlrect.right = nRight; |
|
ctrlrect.bottom = ctrlrect.top + nHeight; |
|
|
|
return ctrlrect; |
|
} |
|
|
|
|
|
void COP_Entity::CreateSmartControls_Angle( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont, bool *bShowSmartAngles ) |
|
{ |
|
if (stricmp(pVar->GetName(), "angles")) |
|
{ |
|
*bShowSmartAngles = true; |
|
|
|
CRect rectAngleBox; |
|
m_SmartAngle.GetWindowRect(&rectAngleBox); |
|
|
|
CRect rectAngleEdit; |
|
m_SmartAngleEdit.GetWindowRect(&rectAngleEdit); |
|
|
|
m_SmartAngle.SetWindowPos(NULL, ctrlrect.left + rectAngleEdit.Width() + 4, ctrlrect.bottom + 10, 0, 0, SWP_NOSIZE | SWP_NOZORDER); |
|
m_SmartAngleEdit.SetWindowPos(NULL, ctrlrect.left, (ctrlrect.bottom + rectAngleBox.Height() + 10) - rectAngleEdit.Height(), 0, 0, SWP_NOSIZE | SWP_NOZORDER); |
|
|
|
// Update the smart control with the current value |
|
LPCTSTR pszValue = m_kv.GetValue(pVar->GetName()); |
|
if (pszValue != NULL) |
|
{ |
|
if (!stricmp(pszValue, VALUE_DIFFERENT_STRING)) |
|
{ |
|
m_SmartAngle.SetDifferent(true); |
|
} |
|
else |
|
{ |
|
m_SmartAngle.SetDifferent(false); |
|
m_SmartAngle.SetAngles(pszValue); |
|
} |
|
} |
|
} |
|
|
|
// |
|
// Create an eyedropper button for picking target angles. |
|
// |
|
CRect ButtonRect; |
|
if (bShowSmartAngles) |
|
{ |
|
CRect rectAngleBox; |
|
m_SmartAngle.GetWindowRect(&rectAngleBox); |
|
ScreenToClient(&rectAngleBox); |
|
|
|
CRect rectAngleEdit; |
|
m_SmartAngleEdit.GetWindowRect(&rectAngleEdit); |
|
ScreenToClient(&rectAngleEdit); |
|
|
|
ButtonRect.left = rectAngleBox.right + 8; |
|
ButtonRect.top = rectAngleEdit.top; |
|
ButtonRect.bottom = rectAngleEdit.bottom; |
|
} |
|
else |
|
{ |
|
ButtonRect.left = ctrlrect.left; |
|
ButtonRect.top = ctrlrect.bottom + 4; |
|
ButtonRect.bottom = ButtonRect.top + ctrlrect.Height(); |
|
} |
|
|
|
CButton *pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "Point At...", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_PUSHLIKE, |
|
ButtonRect.left, ButtonRect.top, 58, ButtonRect.Height() + 2, GetSafeHwnd(), (HMENU)IDC_PICK_ANGLES); |
|
pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
|
|
CWinApp *pApp = AfxGetApp(); |
|
HICON hIcon = pApp->LoadIcon(IDI_CROSSHAIR); |
|
pButton->SetIcon(hIcon); |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
} |
|
|
|
|
|
void COP_Entity::CreateSmartControls_Choices( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont ) |
|
{ |
|
CMyComboBox *pCombo = new CMyComboBox; |
|
pCombo->SetParentPage(this); |
|
ctrlrect.bottom += 150; |
|
pCombo->Create(CBS_DROPDOWN | CBS_HASSTRINGS | WS_TABSTOP | WS_CHILD | WS_BORDER | WS_VSCROLL | CBS_AUTOHSCROLL | ((pVar->GetType() != ivChoices) ? CBS_SORT : 0), ctrlrect, this, IDC_SMARTCONTROL); |
|
pCombo->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
pCombo->SetDroppedWidth(150); |
|
|
|
// |
|
// If we are editing multiple entities, start with the "(different)" string. |
|
// |
|
if (IsMultiEdit()) |
|
{ |
|
pCombo->AddString(VALUE_DIFFERENT_STRING); |
|
} |
|
|
|
// |
|
// If this is a choices field, give combo box text choices from GameData variable |
|
// |
|
if (pVar->GetType() == ivChoices) |
|
{ |
|
for (int i = 0; i < pVar->GetChoiceCount(); i++) |
|
{ |
|
pCombo->AddString(pVar->GetChoiceCaption(i)); |
|
} |
|
} |
|
// |
|
// For filterclass display a list of all the names of filters that are in the map |
|
// |
|
else if (pVar->GetType() == ivFilterClass) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
CMapWorld *pWorld = pDoc->GetMapWorld(); |
|
const CMapEntityList *pEntityList = pWorld->EntityList_GetList(); |
|
|
|
|
|
FOR_EACH_OBJ( *pEntityList, pos ) |
|
{ |
|
CMapEntity *pEntity = pEntityList->Element(pos); |
|
GDclass *pClass = pEntity->GetClass(); |
|
if (pClass && pClass->IsFilterClass()) |
|
{ |
|
const char *pString = pEntity->GetKeyValue("targetname"); |
|
if (pString) |
|
{ |
|
pCombo->AddString(pString); |
|
} |
|
} |
|
} |
|
} |
|
// |
|
// For npcclass fields, fill with all the NPC classes from the FGD. |
|
// |
|
else if (pVar->GetType() == ivNPCClass) |
|
{ |
|
if (pGD != NULL) |
|
{ |
|
int nCount = pGD->GetClassCount(); |
|
for (int i = 0; i < nCount; i++) |
|
{ |
|
GDclass *pClass = pGD->GetClass(i); |
|
if (pClass->IsNPCClass()) |
|
{ |
|
pCombo->AddString(pClass->GetName()); |
|
} |
|
} |
|
} |
|
} |
|
// |
|
// For pointentity fields, fill with all the point entity classes from the FGD. |
|
// |
|
else |
|
{ |
|
if (pGD != NULL) |
|
{ |
|
int nCount = pGD->GetClassCount(); |
|
for (int i = 0; i < nCount; i++) |
|
{ |
|
GDclass *pClass = pGD->GetClass(i); |
|
if (pClass->IsPointClass()) |
|
{ |
|
pCombo->AddString(pClass->GetName()); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// |
|
// If the current value is the "different" string, display that in the combo box. |
|
// |
|
LPCTSTR pszValue = m_kv.GetValue(pVar->GetName()); |
|
if (pszValue != NULL) |
|
{ |
|
if (strcmp(pszValue, VALUE_DIFFERENT_STRING) == 0) |
|
{ |
|
pCombo->SelectString(-1, VALUE_DIFFERENT_STRING); |
|
} |
|
else |
|
{ |
|
const char *p = NULL; |
|
|
|
// |
|
// If this is a choices field and the current value corresponds to one of our |
|
// choices, display the friendly name of the choice in the edit control. |
|
// |
|
if (pVar->GetType() == ivChoices) |
|
{ |
|
p = pVar->ItemStringForValue(pszValue); |
|
} |
|
|
|
if (p != NULL) |
|
{ |
|
pCombo->SelectString(-1, p); |
|
} |
|
// |
|
// Otherwise, just display the value as-is. |
|
// |
|
else |
|
{ |
|
pCombo->SetWindowText(pszValue); |
|
} |
|
} |
|
} |
|
|
|
m_pSmartControl = pCombo; |
|
m_SmartControls.AddToTail(pCombo); |
|
} |
|
|
|
|
|
void COP_Entity::CreateSmartControls_TargetName( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont ) |
|
{ |
|
// |
|
// Create a combo box for picking target names. |
|
// |
|
CRect ComboRect = ctrlrect; |
|
ComboRect.bottom += 150; |
|
CTargetNameComboBox *pCombo = CTargetNameComboBox::Create( &m_SmartControlTargetNameRouter, CBS_DROPDOWN | WS_TABSTOP | WS_CHILD | WS_BORDER | CBS_AUTOHSCROLL | WS_VSCROLL | CBS_SORT, ComboRect, this, IDC_SMARTCONTROL_TARGETNAME); |
|
pCombo->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
pCombo->SetDroppedWidth(150); |
|
|
|
m_pSmartControl = pCombo; |
|
m_SmartControls.AddToTail(m_pSmartControl); |
|
|
|
// |
|
// Attach the world's entity list to the add dialog. |
|
// |
|
// HACK: This is ugly. It should be passed in from outside. |
|
// |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
CMapWorld *pWorld = pDoc->GetMapWorld(); |
|
pCombo->SetEntityList(pWorld->EntityList_GetList()); |
|
pCombo->SelectItem( m_kv.GetValue(pVar->GetName()) ); |
|
|
|
if (pVar->IsReadOnly()) |
|
{ |
|
pCombo->EnableWindow(FALSE); |
|
} |
|
} |
|
|
|
|
|
void COP_Entity::CreateSmartControls_BasicEditControl( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont, CUtlVector<const char *> *pHelperType ) |
|
{ |
|
// |
|
// Create an edit control for inputting the keyvalue as text. |
|
// |
|
CMyEdit *pEdit = new CMyEdit; |
|
pEdit->SetParentPage(this); |
|
ctrlrect.bottom += 2; |
|
pEdit->CreateEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL, |
|
ctrlrect.left, ctrlrect.top, ctrlrect.Width(), ctrlrect.Height(), GetSafeHwnd(), HMENU(IDC_SMARTCONTROL)); |
|
pEdit->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
|
|
const char *pValue = m_kv.GetValue( pVar->GetName() ); |
|
if ( pValue ) |
|
pEdit->SetWindowText( pValue ); |
|
|
|
if (pVar->IsReadOnly()) |
|
{ |
|
pEdit->EnableWindow(FALSE); |
|
} |
|
|
|
for ( int i = 0; i < pHelperType->Count(); i++ ) |
|
{ |
|
if ( !Q_strcmp( pHelperType->Element(i), "sphere" ) ) |
|
{ |
|
CRect ButtonRect = ctrlrect; |
|
ButtonRect.top = ctrlrect.bottom + 4; |
|
ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4; |
|
ButtonRect.right = ButtonRect.left + 32; |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
CMapView3D *pView = pDoc->GetFirst3DView(); |
|
int disabled = 0; |
|
if ( !pView ) |
|
{ |
|
disabled = WS_DISABLED; |
|
} |
|
|
|
CButton *pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_ICON | BS_PUSHLIKE | disabled, |
|
ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(), |
|
GetSafeHwnd(), (HMENU)IDC_CAMERA_DISTANCE); |
|
ButtonRect.left += ButtonRect.Width() + 4; |
|
CWinApp *pApp = AfxGetApp(); |
|
HICON hIcon = pApp->LoadIcon(IDI_CAMERA); |
|
pButton->SetIcon(hIcon); |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
} |
|
} |
|
|
|
m_pSmartControl = pEdit; |
|
m_SmartControls.AddToTail(pEdit); |
|
} |
|
|
|
|
|
void COP_Entity::CreateSmartControls_BrowseAndPlayButtons( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont ) |
|
{ |
|
CRect ButtonRect = ctrlrect; |
|
|
|
ButtonRect.top = ctrlrect.bottom + 4; |
|
ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4; |
|
ButtonRect.right = ButtonRect.left + 54; |
|
|
|
HMENU message = (HMENU)IDC_BROWSE; |
|
if ( pVar->GetType() == ivInstanceFile ) |
|
{ |
|
message = (HMENU)IDC_BROWSE_INSTANCE; |
|
} |
|
|
|
CButton *pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "Browse...", WS_TABSTOP | WS_CHILD | WS_VISIBLE, |
|
ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(), |
|
GetSafeHwnd(), message); |
|
pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
m_pSmartBrowseButton = pButton; |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
|
|
if ( pVar->GetType() == ivSound || pVar->GetType() == ivScene ) |
|
{ |
|
ButtonRect.left = ButtonRect.right + 8; |
|
ButtonRect.right = ButtonRect.left + 54; |
|
|
|
pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "Play", WS_TABSTOP | WS_CHILD | WS_VISIBLE, |
|
ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(), |
|
GetSafeHwnd(), (HMENU)IDC_PLAY_SOUND); |
|
pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
} |
|
} |
|
|
|
|
|
void COP_Entity::CreateSmartControls_MarkAndEyedropperButtons( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont ) |
|
{ |
|
CRect ButtonRect = ctrlrect; |
|
ButtonRect.top = ctrlrect.bottom + 4; |
|
ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4; |
|
CButton *pButton = NULL; |
|
|
|
GDIV_TYPE eType = pVar->GetType(); |
|
|
|
if ((eType == ivTargetDest) || (eType == ivTargetNameOrClass) || (eType == ivTargetSrc)) |
|
{ |
|
// |
|
// Create a "Mark" button for finding target entities. |
|
// |
|
ButtonRect.right = ButtonRect.left + 48; |
|
|
|
pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "Mark", WS_TABSTOP | WS_CHILD | WS_VISIBLE, |
|
ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(), |
|
GetSafeHwnd(), (HMENU)IDC_MARK); |
|
pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
|
|
ButtonRect.left += ButtonRect.Width() + 4; |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
|
|
// |
|
// Create a "Mark+Add" button for finding target entities. |
|
// |
|
ButtonRect.right = ButtonRect.left + 64; |
|
|
|
pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "Mark+Add", WS_TABSTOP | WS_CHILD | WS_VISIBLE, |
|
ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(), |
|
GetSafeHwnd(), (HMENU)IDC_MARK_AND_ADD); |
|
pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
|
|
ButtonRect.left += ButtonRect.Width() + 4; |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
} |
|
|
|
// |
|
// Create an eyedropper button for picking entities. |
|
// |
|
ButtonRect.right = ButtonRect.left + 32; |
|
|
|
pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_ICON | BS_AUTOCHECKBOX | BS_PUSHLIKE, |
|
ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(), |
|
GetSafeHwnd(), (HMENU)IDC_PICK_ENTITY); |
|
|
|
ButtonRect.left += ButtonRect.Width() + 4; |
|
|
|
CWinApp *pApp = AfxGetApp(); |
|
HICON hIcon = pApp->LoadIcon(IDI_EYEDROPPER); |
|
pButton->SetIcon(hIcon); |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
} |
|
|
|
|
|
void COP_Entity::CreateSmartControls_PickButton( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont ) |
|
{ |
|
// |
|
// Create a "Pick" button for clicking on brush sides. |
|
// |
|
CRect ButtonRect = ctrlrect; |
|
|
|
ButtonRect.top = ctrlrect.bottom + 4; |
|
ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4; |
|
ButtonRect.right = ButtonRect.left + 54; |
|
|
|
CButton *pButton = new CButton; |
|
pButton->CreateEx(0, "Button", "Pick...", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_PUSHLIKE, |
|
ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(), |
|
GetSafeHwnd(), (HMENU)IDC_PICK_FACES); |
|
pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont); |
|
|
|
m_SmartControls.AddToTail(pButton); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will set up the smart controls for an instance variable |
|
// Input : pVar - the kv pair |
|
// ctrlrect - the rect space of this area |
|
// hControlFont - the standard font |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::CreateSmartControls_InstanceVariable( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont ) |
|
{ |
|
const char *pValue = m_kv.GetValue( pVar->GetName() ); |
|
char ValueData[ KEYVALUE_MAX_KEY_LENGTH ]; |
|
const char *pVariable, *pReplace; |
|
const int VariableLimit = 50; |
|
|
|
pVariable = pReplace = ""; |
|
if ( pValue ) |
|
{ |
|
strcpy( ValueData, pValue ); |
|
pVariable = ValueData; |
|
char *pos = strchr( ValueData, ' ' ); |
|
if ( pos ) |
|
{ |
|
*pos = 0; |
|
pos++; |
|
pReplace = pos; |
|
} |
|
} |
|
|
|
if ( m_pObjectList->Count() == 1 ) |
|
{ |
|
CMapEntity *pEntity = static_cast< CMapEntity * >( m_pObjectList->Element( 0 ) ); |
|
|
|
CMapInstance *pMapInstance = pEntity->GetChildOfType( ( CMapInstance * )NULL ); |
|
if ( pMapInstance != NULL && pMapInstance->GetInstancedMap() != NULL ) |
|
{ |
|
CMapEntityList entityList; |
|
|
|
pMapInstance->GetInstancedMap()->FindEntitiesByClassName( entityList, "func_instance_parms", false ); |
|
if ( entityList.Count() == 1 ) |
|
{ |
|
CMapEntity *pInstanceParmsEntity = entityList.Element( 0 ); |
|
|
|
for ( int i = pInstanceParmsEntity->GetFirstKeyValue(); i != pInstanceParmsEntity->GetInvalidKeyValue(); i = pInstanceParmsEntity->GetNextKeyValue( i ) ) |
|
{ |
|
LPCTSTR pKey = pInstanceParmsEntity->GetKey( i ); |
|
LPCTSTR psValue = pInstanceParmsEntity->GetKeyValue( i ); |
|
|
|
if ( strnicmp( pKey, "parm", strlen( "parm" ) ) == 0 ) |
|
{ |
|
if ( strnicmp( psValue, pVariable, strlen( pVariable ) ) == 0 ) |
|
{ |
|
m_kv.SetValue( "temp_parm_value", pReplace ); |
|
m_kv.SetValue( "temp_parm_name", pVar->GetName() ); |
|
m_kv.SetValue( "temp_parm_field", pVariable ); |
|
|
|
m_pInstanceVar = new GDinputvariable( "color255", "temp_parm_value" ); |
|
CUtlVector<const char *>helperNames; |
|
|
|
m_pDisplayClass->GetHelperForGDVar( m_pInstanceVar, &helperNames ); |
|
|
|
// |
|
// Update the keyvalue help text control with this variable's help info. |
|
// |
|
m_KeyValueHelpText.SetWindowText(m_pInstanceVar->GetDescription()); |
|
|
|
CreateSmartControls(m_pInstanceVar, &helperNames); |
|
m_eEditType = m_pInstanceVar->GetType(); |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
CStatic *pStaticInstanceVariable; |
|
pStaticInstanceVariable = new CStatic; |
|
pStaticInstanceVariable->CreateEx( WS_EX_LEFT, "STATIC", "Variable:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) ); |
|
pStaticInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
|
|
m_pEditInstanceVariable = new CEdit; |
|
ctrlrect.bottom += 2; |
|
m_pEditInstanceVariable->CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, |
|
ctrlrect.left + 50, ctrlrect.top, ctrlrect.Width() - 50, 24, GetSafeHwnd(), HMENU( IDC_SMARTCONTROL_INSTANCE_VARIABLE ) ); |
|
m_pEditInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
m_pEditInstanceVariable->SetWindowText( pVariable ); |
|
m_pEditInstanceVariable->SetLimitText( VariableLimit ); |
|
|
|
if ( pVar->IsReadOnly() ) |
|
{ |
|
m_pEditInstanceVariable->EnableWindow( FALSE ); |
|
} |
|
|
|
ctrlrect.top += 26; |
|
ctrlrect.bottom += 26; |
|
|
|
CStatic *pStaticInstanceValue; |
|
pStaticInstanceValue = new CStatic; |
|
pStaticInstanceValue->CreateEx( WS_EX_LEFT, "STATIC", "Value:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) ); |
|
pStaticInstanceValue->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
|
|
m_pEditInstanceValue = new CEdit; |
|
m_pEditInstanceValue->CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, |
|
ctrlrect.left + 50, ctrlrect.top, ctrlrect.Width() - 50, 24, GetSafeHwnd(), HMENU( IDC_SMARTCONTROL_INSTANCE_VALUE ) ); |
|
m_pEditInstanceValue->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
m_pEditInstanceValue->SetWindowText( pReplace ); |
|
m_pEditInstanceVariable->SetLimitText( KEYVALUE_MAX_KEY_LENGTH - VariableLimit - 2 ); // to account for null and space in between |
|
|
|
if ( pVar->IsReadOnly() ) |
|
{ |
|
m_pEditInstanceValue->EnableWindow( FALSE ); |
|
} |
|
|
|
m_pSmartControl = m_pEditInstanceVariable; |
|
m_SmartControls.AddToTail( m_pEditInstanceVariable ); |
|
m_SmartControls.AddToTail( m_pEditInstanceValue ); |
|
m_SmartControls.AddToTail( pStaticInstanceVariable ); |
|
m_SmartControls.AddToTail( pStaticInstanceValue ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will set up the smart controls for an instance parameter |
|
// Input : pVar - the kv pair |
|
// ctrlrect - the rect space of this area |
|
// hControlFont - the standard font |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::CreateSmartControls_InstanceParm( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont ) |
|
{ |
|
const char *pValue = m_kv.GetValue( pVar->GetName() ); |
|
char ValueData[ KEYVALUE_MAX_KEY_LENGTH ]; |
|
const char *pVariable, *pType; |
|
const int VariableLimit = 50; |
|
|
|
pVariable = pType = ""; |
|
if ( pValue ) |
|
{ |
|
strcpy( ValueData, pValue ); |
|
pVariable = ValueData; |
|
char *pos = strchr( ValueData, ' ' ); |
|
if ( pos ) |
|
{ |
|
*pos = 0; |
|
pos++; |
|
pType = pos; |
|
} |
|
} |
|
|
|
CStatic *pStaticInstanceVariable; |
|
pStaticInstanceVariable = new CStatic; |
|
pStaticInstanceVariable->CreateEx( WS_EX_LEFT, "STATIC", "Variable:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) ); |
|
pStaticInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
|
|
m_pEditInstanceVariable = new CEdit; |
|
ctrlrect.bottom += 2; |
|
m_pEditInstanceVariable->CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, |
|
ctrlrect.left + 50, ctrlrect.top, ctrlrect.Width() - 50, 24, GetSafeHwnd(), HMENU( IDC_SMARTCONTROL_INSTANCE_PARM ) ); |
|
m_pEditInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
m_pEditInstanceVariable->SetWindowText( pVariable ); |
|
m_pEditInstanceVariable->SetLimitText( VariableLimit ); |
|
|
|
if ( pVar->IsReadOnly() ) |
|
{ |
|
m_pEditInstanceVariable->EnableWindow( FALSE ); |
|
} |
|
|
|
ctrlrect.top += 26; |
|
ctrlrect.bottom += 26; |
|
|
|
CStatic *pStaticInstanceValue; |
|
pStaticInstanceValue = new CStatic; |
|
pStaticInstanceValue->CreateEx( WS_EX_LEFT, "STATIC", "Value:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) ); |
|
pStaticInstanceValue->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
|
|
m_pComboInstanceParmType = new CMyComboBox; |
|
m_pComboInstanceParmType->SetParentPage( this ); |
|
ctrlrect.bottom += 150; |
|
ctrlrect.left += 50; |
|
m_pComboInstanceParmType->Create( CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_SORT, ctrlrect, this, IDC_SMARTCONTROL_INSTANCE_PARM ); |
|
m_pComboInstanceParmType->SendMessage( WM_SETFONT, ( WPARAM )hControlFont ); |
|
m_pComboInstanceParmType->SetDroppedWidth( 150 ); |
|
|
|
if ( pVar->IsReadOnly() ) |
|
{ |
|
m_pComboInstanceParmType->EnableWindow( FALSE ); |
|
} |
|
|
|
for( int i = 0; i < ivMax; i++ ) |
|
{ |
|
m_pComboInstanceParmType->AddString( GDinputvariable::GetVarTypeName( ( GDIV_TYPE )i ) ); |
|
} |
|
|
|
m_pComboInstanceParmType->SelectString( -1, pType ); |
|
|
|
m_pSmartControl = m_pEditInstanceVariable; |
|
m_SmartControls.AddToTail( m_pEditInstanceVariable ); |
|
m_SmartControls.AddToTail( m_pComboInstanceParmType ); |
|
m_SmartControls.AddToTail( pStaticInstanceVariable ); |
|
m_SmartControls.AddToTail( pStaticInstanceValue ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::SetSmartControlText(const char *pszText) |
|
{ |
|
// dvs: HACK: the smart control should be completely self-contained, the dialog |
|
// should only set the type of the edited variable, then just set & get text |
|
CTargetNameComboBox *pCombo = dynamic_cast <CTargetNameComboBox *>(m_pSmartControl); |
|
if (pCombo) |
|
{ |
|
pCombo->SelectItem(pszText); |
|
} |
|
else |
|
{ |
|
m_pSmartControl->SetWindowText(pszText); |
|
} |
|
|
|
// FIXME: this should be called automatically, but it isn't |
|
OnChangeSmartcontrol(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnSelchangeKeyvalues(void) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::OnSelchangeKeyvalues", "Object Properties" ); |
|
|
|
// |
|
// Load new selection's key/values into the key/value |
|
// edit controls. |
|
// |
|
if (m_VarList.m_hWnd == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
if (m_bSmartedit) |
|
{ |
|
// |
|
// Stop face or entity picking if we are doing so. |
|
// |
|
StopPicking(); |
|
} |
|
|
|
int iSel = GetCurVarListSelection(); |
|
if (iSel == LB_ERR) |
|
{ |
|
if (!m_bSmartedit) |
|
{ |
|
// No selection; clear our the key and value controls. |
|
m_cKey.SetWindowText(""); |
|
m_cValue.SetWindowText(""); |
|
} |
|
|
|
return; |
|
} |
|
|
|
if (!m_bSmartedit) |
|
{ |
|
CString str, val; |
|
str = m_VarList.GetItemText( iSel, 0 ); |
|
val = m_VarList.GetItemText( iSel, 1 ); |
|
|
|
// |
|
// Set the edit control text, but ignore the notifications caused by that. |
|
// |
|
m_bIgnoreKVChange = true; |
|
m_cKey.SetWindowText( str ); |
|
m_cValue.SetWindowText( val ); |
|
m_bChangingKeyName = false; |
|
m_bIgnoreKVChange = false; |
|
} |
|
else |
|
{ |
|
GDinputvariable *pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL || !m_pDisplayClass ) |
|
{ |
|
// This is a var added in dumbedit mode. |
|
DestroySmartControls(); |
|
m_KeyValueHelpText.SetWindowText( "" ); |
|
} |
|
else |
|
{ |
|
CUtlVector<const char *>helperNames; |
|
|
|
m_pDisplayClass->GetHelperForGDVar( pVar, &helperNames ); |
|
|
|
// |
|
// Update the keyvalue help text control with this variable's help info. |
|
// |
|
m_KeyValueHelpText.SetWindowText(pVar->GetDescription()); |
|
|
|
CreateSmartControls(pVar, &helperNames); |
|
m_eEditType = pVar->GetType(); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Used only in standard (non-SmartEdit) mode. Called when the contents |
|
// of the value edit control change. This is where the edit control |
|
// contents are converted to keyvalue data in standard mode. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnChangeKeyorValue(void) |
|
{ |
|
if (m_bIgnoreKVChange) |
|
{ |
|
return; |
|
} |
|
|
|
int iSel = GetCurVarListSelection(); |
|
if (iSel == LB_ERR) |
|
{ |
|
return; |
|
} |
|
|
|
char szKey[KEYVALUE_MAX_KEY_LENGTH]; |
|
char szValue[KEYVALUE_MAX_VALUE_LENGTH]; |
|
|
|
m_cKey.GetWindowText(szKey, sizeof(szKey)); |
|
m_cValue.GetWindowText(szValue, sizeof(szValue)); |
|
|
|
UpdateKeyValue(szKey, szValue); |
|
|
|
// |
|
// Save it in our local kv storage. |
|
// |
|
m_kv.SetValue(szKey, szValue); |
|
|
|
// If they changed spawnflags, notify the flags page so its changes don't overwrite ours later. |
|
if ( V_stricmp( szKey, SPAWNFLAGS_KEYNAME ) == 0 ) |
|
{ |
|
unsigned long value; |
|
sscanf( szValue, "%lu", &value ); |
|
m_pFlagsPage->OnUpdateSpawnFlags( value ); |
|
} |
|
|
|
if (m_bEnableControlUpdate) |
|
{ |
|
// Update any controls that are displaying the same data as the edit control. |
|
|
|
// This code should only be hit as a result of user input in the edit control! |
|
// If they changed the "angles" key, update the main angles control. |
|
if (!stricmp(szKey, "angles")) |
|
{ |
|
m_Angle.SetDifferent(false); |
|
m_Angle.SetAngles(szValue, true); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnAddkeyvalue(void) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::OnAddkeyvalue", "Object Properties" ); |
|
|
|
// create a new keyvalue at the end of the list |
|
CNewKeyValue newkv; |
|
newkv.m_Key.Format("newkey%d", m_nNewKeyCount++); |
|
newkv.m_Value = "value"; |
|
if(newkv.DoModal() == IDCANCEL) |
|
return; |
|
|
|
// if the key we're adding is already in the list, do some |
|
// stuff to make it unique |
|
if(m_kv.GetValue(newkv.m_Key)) |
|
{ |
|
CString strTemp; |
|
for(int i = 1; ; i++) |
|
{ |
|
strTemp.Format("%s#%d", newkv.m_Key.GetBuffer(), i); |
|
if(!m_kv.GetValue(strTemp)) |
|
break; |
|
} |
|
newkv.m_Key = strTemp; |
|
} |
|
|
|
m_kvAdded.SetValue( newkv.m_Key, "1" ); |
|
m_kv.SetValue( newkv.m_Key, newkv.m_Value ); |
|
|
|
PresentProperties(); |
|
SetCurKey( newkv.m_Key ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Deletes the selected keyvalue. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnRemovekeyvalue(void) |
|
{ |
|
int iSel = GetCurVarListSelection(); |
|
if (iSel == LB_ERR) |
|
{ |
|
return; |
|
} |
|
|
|
CString strBuf; |
|
strBuf = (CString)(const char*)m_VarList.GetItemData(iSel); |
|
|
|
// |
|
// Remove the keyvalue from local storage. |
|
// |
|
m_kv.RemoveKey( strBuf ); |
|
m_VarList.DeleteItem(iSel); |
|
if (iSel == m_VarList.GetItemCount()) |
|
{ |
|
SetCurVarListSelection( iSel - 1 ); |
|
} |
|
|
|
ResortItems(); |
|
OnSelchangeKeyvalues(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns a mask indicating which flags have the same caption and bit value |
|
// between the two classes. |
|
//----------------------------------------------------------------------------- |
|
static unsigned long GetMatchingFlagsMask( GDinputvariable *pVar1, GDinputvariable *pVar2 ) |
|
{ |
|
unsigned long nMatchingMask = 0; |
|
|
|
for ( int i=0; i < pVar1->GetFlagCount(); i++ ) |
|
{ |
|
for ( int j=0; j < pVar2->GetFlagCount(); j++ ) |
|
{ |
|
if ( pVar1->GetFlagMask( i ) == pVar2->GetFlagMask( j ) ) |
|
{ |
|
if ( V_stricmp( pVar1->GetFlagCaption( i ), pVar2->GetFlagCaption( j ) ) == 0 ) |
|
{ |
|
unsigned long iMask = (unsigned long)pVar1->GetFlagMask( i ); |
|
nMatchingMask |= iMask; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return nMatchingMask; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Assign default values to keys that are in the FGD but missing from the entity. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::AssignClassDefaults(GDclass *pClass, GDclass *pOldClass) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::AssignClassDefaults", "Object Properties" ); |
|
|
|
if (!pClass) |
|
return; |
|
|
|
for (int i = 0; i < pClass->GetVariableCount(); i++) |
|
{ |
|
GDinputvariable *pVar = pClass->GetVariableAt(i); |
|
|
|
int iIndex; |
|
LPCTSTR p = m_kv.GetValue(pVar->GetName(), &iIndex); |
|
|
|
// Always reset spawnflags. |
|
if (!strcmpi(pVar->GetName(), SPAWNFLAGS_KEYNAME)) |
|
{ |
|
unsigned long nOriginalFlagsValue = 0; |
|
if (p) |
|
{ |
|
sscanf( p, "%lu", &nOriginalFlagsValue ); |
|
} |
|
|
|
unsigned long nCurrent = nOriginalFlagsValue; |
|
if ( pOldClass && (pOldClass != pClass) ) |
|
{ |
|
// First, just use the defaults for the new class. |
|
int defaultValue; |
|
pVar->GetDefault( &defaultValue ); |
|
nCurrent = (unsigned long)defaultValue; |
|
|
|
// But.. if the old class and the new class have any flags with the same name and value, |
|
// then keep the current value for that flag. |
|
GDinputvariable *pOldVar = pOldClass->VarForName( SPAWNFLAGS_KEYNAME ); |
|
if ( p && pOldVar ) |
|
{ |
|
unsigned long mask = GetMatchingFlagsMask( pOldVar, pVar ); |
|
nCurrent &= ~mask; // Get rid of the default value. |
|
nCurrent |= (nOriginalFlagsValue & mask); // Add back in the current values. |
|
} |
|
|
|
// Notify the flags page so it'll have the right data if they tab to it. |
|
// It'll also SAVE the right data when they save. |
|
m_pFlagsPage->OnUpdateSpawnFlags( nCurrent ); |
|
} |
|
else |
|
{ |
|
unsigned long nMask = 0; |
|
int nCount = pVar->GetFlagCount(); |
|
for (int j = 0; j < nCount; j++) |
|
{ |
|
nMask |= (unsigned int)pVar->GetFlagMask(j); |
|
} |
|
|
|
// Mask off any bits that aren't defined in the FGD. |
|
nCurrent &= nMask; |
|
} |
|
|
|
char szValue[128]; |
|
Q_snprintf( szValue, sizeof( szValue ), "%lu", nCurrent ); |
|
|
|
// Remember that we added or changed this key. |
|
if (!p || Q_stricmp(p, szValue) != 0) |
|
{ |
|
m_kvAdded.SetValue(SPAWNFLAGS_KEYNAME, "1"); |
|
} |
|
|
|
m_kv.SetValue(SPAWNFLAGS_KEYNAME, szValue); |
|
} |
|
else if (!p) |
|
{ |
|
MDkeyvalue newkv; |
|
pVar->ResetDefaults(); |
|
pVar->ToKeyValue(&newkv); |
|
m_kv.SetValue(newkv.szKey, newkv.szValue); |
|
|
|
// Remember that we added this key. |
|
m_kvAdded.SetValue(newkv.szKey, "1"); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Updates the dialog based on a change to the entity class name. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::UpdateEditClass(const char *pszClass, bool bForce) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::UpdateClass", "Object Properties" ); |
|
|
|
GDclass *pOldEditClass = m_pEditClass; |
|
m_pEditClass = pGD->ClassForName(pszClass); |
|
|
|
if (!bForce && (m_pEditClass == pOldEditClass)) |
|
return; |
|
|
|
//DBG("UpdateEditClass - BEFORE PRUNE\n"); |
|
//DumpKeyvalues(m_kv); |
|
|
|
// |
|
// Remove unused keyvalues. |
|
// |
|
if (m_pEditClass != pOldEditClass && m_pEditClass && pOldEditClass && strcmpi(pszClass, "multi_manager")) |
|
{ |
|
int iNext; |
|
for ( int i=m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=iNext ) |
|
{ |
|
iNext = m_kv.GetNext( i ); |
|
|
|
MDkeyvalue &KeyValue = m_kv.GetKeyValue(i); |
|
if (m_pEditClass->VarForName(KeyValue.szKey) == NULL) |
|
{ |
|
m_kv.RemoveKey(KeyValue.szKey); |
|
} |
|
} |
|
} |
|
|
|
//DBG("UpdateEditClass - AFTER PRUNE\n"); |
|
//DumpKeyvalues(m_kv); |
|
|
|
AssignClassDefaults(m_pEditClass, pOldEditClass); |
|
PresentProperties(); |
|
|
|
SetReadOnly( ( m_pDisplayClass != m_pEditClass ) || ( m_bCanEdit == false ) ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called when a keyvalue is modified by the user. |
|
// Input : szKey - The key name. |
|
// szValue - The new value. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::UpdateKeyValue(const char *szKey, const char *szValue) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::UpdateKeyValue", "Object Properties" ); |
|
|
|
m_kvAdded.SetValue(szKey, "1"); |
|
m_kv.SetValue(szKey, szValue); |
|
|
|
int index = m_InstanceParmData.Find( szKey ); |
|
|
|
if ( index != m_InstanceParmData.InvalidIndex() ) |
|
{ |
|
CString NewValue = m_InstanceParmData[ index ].m_VariableName + " " + szValue; |
|
|
|
m_kvAdded.SetValue( m_InstanceParmData[ index ].m_ParmKey, "1" ); |
|
m_kv.SetValue( m_InstanceParmData[ index ].m_ParmKey, NewValue ); |
|
RefreshKVListValues( m_InstanceParmData[ index ].m_ParmKey ); |
|
} |
|
|
|
RefreshKVListValues( szKey ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Enables or disables SmartEdit mode, hiding showing the appropriate |
|
// dialog controls. |
|
// Input : b - TRUE to enable, FALSE to disable SmartEdit. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::SetSmartedit(bool bSet) |
|
{ |
|
// Nothing to do? |
|
if ( m_bSmartedit == bSet ) |
|
return; |
|
|
|
m_bSmartedit = bSet; |
|
|
|
// |
|
// If disabling smartedit, remove any smartedit-specific controls that may |
|
// or may not be currently visible. |
|
// |
|
if (!m_bSmartedit) |
|
{ |
|
m_cPickColor.ShowWindow(SW_HIDE); |
|
m_SmartAngle.ShowWindow(SW_HIDE); |
|
m_SmartAngleEdit.ShowWindow(SW_HIDE); |
|
|
|
DestroySmartControls(); |
|
} |
|
|
|
m_KeyValueHelpText.ShowWindow(m_bSmartedit ? SW_SHOW : SW_HIDE); |
|
GetDlgItem(IDC_KEYVALUE_HELP_GROUP)->ShowWindow(m_bSmartedit ? SW_SHOW : SW_HIDE); |
|
|
|
// |
|
// Hide or show all controls after and including "delete kv" button. |
|
// |
|
for (int i = 0; i < ARRAYSIZE(g_DumbEditControls); i++) |
|
{ |
|
CWnd *pWnd = GetDlgItem(g_DumbEditControls[i]); |
|
if ( pWnd ) |
|
pWnd->ShowWindow(m_bSmartedit ? SW_HIDE : SW_SHOW); |
|
} |
|
|
|
((CButton*)GetDlgItem(IDC_SMARTEDIT))->SetCheck(m_bSmartedit); |
|
|
|
PresentProperties(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::SetReadOnly(bool bReadOnly) |
|
{ |
|
m_VarList.EnableWindow(bReadOnly ? FALSE : TRUE); |
|
m_cPickColor.EnableWindow(bReadOnly ? FALSE : TRUE); |
|
m_SmartAngle.EnableWindow(bReadOnly ? FALSE : TRUE); |
|
m_SmartAngleEdit.EnableWindow(bReadOnly ? FALSE : TRUE); |
|
m_PasteControl.EnableWindow(bReadOnly ? FALSE : TRUE); |
|
|
|
m_KeyValueHelpText.EnableWindow(bReadOnly ? FALSE : TRUE); |
|
GetDlgItem(IDC_KEYVALUE_HELP_GROUP)->EnableWindow(bReadOnly ? FALSE : TRUE); |
|
|
|
// |
|
// Hide or show all controls after and including "delete kv" button. |
|
// |
|
for (int i = 0; i < ARRAYSIZE(g_DumbEditControls); i++) |
|
{ |
|
CWnd *pWnd = GetDlgItem(g_DumbEditControls[i]); |
|
if ( pWnd ) |
|
pWnd->EnableWindow( !bReadOnly ); |
|
} |
|
|
|
for (int i = 0; i < m_SmartControls.Count(); i++) |
|
{ |
|
if (m_SmartControls.Element(i) != NULL) |
|
{ |
|
m_SmartControls.Element(i)->EnableWindow(bReadOnly ? FALSE : TRUE); |
|
} |
|
} |
|
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnSmartedit(void) |
|
{ |
|
m_iSortColumn = -1; // Go back to FGD sorting. |
|
SetSmartedit(!m_bSmartedit); |
|
m_bWantSmartedit = m_bSmartedit; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Updates the UI to show properties for the given FGD class. If the class |
|
// differs from the actual class of the edited objects, the UI is set to be |
|
// read only until the Apply button is pressed. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::UpdateDisplayClass(const char *szClass) |
|
{ |
|
UpdateDisplayClass( pGD->ClassForName( szClass ) ); |
|
} |
|
|
|
void COP_Entity::UpdateDisplayClass(GDclass *pClass) |
|
{ |
|
// The outer check here is lame, but somewhere along the line, all the controls get enabled |
|
// behind our backs. So verify that our state information is sane. If it's not, then we'll redo some state in here. |
|
bool bForceRefresh = false; |
|
if ( GetDlgItem(IDC_SMARTEDIT)->IsWindowEnabled() != (m_pDisplayClass != 0) ) |
|
bForceRefresh = true; |
|
|
|
if ( pClass == m_pDisplayClass && !bForceRefresh ) |
|
{ |
|
return; |
|
} |
|
|
|
int lastNumPresentPropertiesCalls = m_nPresentPropertiesCalls; |
|
|
|
bool bNeedsSetupForMode = true; |
|
m_pDisplayClass = pClass; |
|
|
|
// In case we're not allowed to present the properties yet, make sure nobody uses m_VarMap. |
|
if ( !m_bAllowPresentProperties ) |
|
{ |
|
ClearVarList(); |
|
} |
|
|
|
if (!m_pDisplayClass) |
|
{ |
|
// |
|
// Object has no known class - get rid of smartedit. |
|
// |
|
if (m_bSmartedit || bForceRefresh) |
|
{ |
|
m_bSmartedit = true; // In case bForceRefresh was on, force it to refresh the controls. |
|
SetSmartedit(false); |
|
bNeedsSetupForMode = false; |
|
} |
|
|
|
GetDlgItem(IDC_SMARTEDIT)->EnableWindow(FALSE); |
|
} |
|
else |
|
{ |
|
CEntityHelpDlg::SetEditGameClass(m_pDisplayClass); |
|
|
|
// |
|
// Known class - enable smartedit. |
|
// |
|
GetDlgItem(IDC_SMARTEDIT)->EnableWindow(TRUE); |
|
|
|
if (bForceRefresh) |
|
m_bSmartedit = !m_bWantSmartedit; |
|
|
|
SetSmartedit(m_bWantSmartedit); |
|
} |
|
|
|
// No need to call PresentProperties an extra time if it was called because of SetSmartedit.. |
|
if ( bNeedsSetupForMode && (m_nPresentPropertiesCalls == lastNumPresentPropertiesCalls) ) |
|
{ |
|
PresentProperties(); |
|
} |
|
|
|
SetReadOnly( ( m_pDisplayClass != m_pEditClass ) || ( m_bCanEdit == false ) ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool COP_Entity::BrowseModels( char *szModelName, int length, int &nSkin ) |
|
{ |
|
bool bChanged = false; |
|
|
|
if (pModelBrowser == NULL) |
|
{ |
|
pModelBrowser = new CModelBrowser( GetMainWnd() ); |
|
} |
|
else |
|
{ |
|
pModelBrowser->Show(); |
|
} |
|
|
|
pModelBrowser->SetModelName( szModelName ); |
|
pModelBrowser->SetSkin( nSkin ); |
|
|
|
if (pModelBrowser->DoModal() == IDOK) |
|
{ |
|
pModelBrowser->GetModelName( szModelName, length ); |
|
pModelBrowser->GetSkin( nSkin ); |
|
bChanged = true; |
|
} |
|
|
|
pModelBrowser->Hide(); |
|
|
|
return bChanged; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::BrowseTextures( const char *szFilter, bool bSprite ) |
|
{ |
|
// browse for textures |
|
int iSel = GetCurVarListSelection(); |
|
|
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
// get current texture |
|
char szInitialTexture[128]; |
|
m_pSmartControl->GetWindowText(szInitialTexture, 128); |
|
|
|
// create a texture browser and set it to browse decals |
|
CTextureBrowser *pBrowser = new CTextureBrowser(GetMainWnd()); |
|
|
|
// setup filter - if any |
|
if( szFilter[0] != '\0' ) |
|
{ |
|
pBrowser->SetFilter( szFilter ); |
|
} |
|
|
|
pBrowser->SetInitialTexture(szInitialTexture); |
|
|
|
if (pBrowser->DoModal() == IDOK) |
|
{ |
|
IEditorTexture *pTex = g_Textures.FindActiveTexture(pBrowser->m_cTextureWindow.szCurTexture); |
|
|
|
char szName[MAX_PATH]; |
|
if (pTex) |
|
{ |
|
pTex->GetShortName(szName); |
|
} |
|
else |
|
{ |
|
szName[0] = '\0'; |
|
} |
|
|
|
if (bSprite && g_pGameConfig->GetTextureFormat() == tfVMT) |
|
{ |
|
char sprExt[4]; |
|
Q_snprintf(sprExt, 4, ".vmt"); |
|
Q_snprintf(szName, MAX_PATH, "%s.vmt", szName); |
|
//Strcat is being zee stupido. I prolly have to strip the other string or something. |
|
//Q_strcat( szName, sprExt, MAX_PATH ); |
|
} |
|
|
|
|
|
m_pSmartControl->SetWindowText(szName); |
|
|
|
// also set variable |
|
m_kv.SetValue(pVar->GetName(), szName); |
|
m_kvAdded.SetValue(pVar->GetName(), "1"); |
|
} |
|
|
|
delete pBrowser; |
|
} |
|
|
|
|
|
void COP_Entity::OnChangeSmartcontrol(void) |
|
{ |
|
if ( m_pSmartControl ) |
|
{ |
|
char szValue[KEYVALUE_MAX_VALUE_LENGTH]; |
|
m_pSmartControl->GetWindowText(szValue, sizeof(szValue)); |
|
InternalOnChangeSmartcontrol( szValue ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Used only in SmartEdit mode. Called when the contents of the value |
|
// edit control change. This is where the edit control contents are |
|
// converted to keyvalue data in SmartEdit mode. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::InternalOnChangeSmartcontrol( const char *szValue ) |
|
{ |
|
//VPROF_BUDGET( "COP_Entity::OnChangeSmartcontrol", "Object Properties" ); |
|
|
|
// |
|
// We only respond to this message when it is due to user input. |
|
// Don't do anything if we're creating the smart control. |
|
// |
|
if (m_bIgnoreKVChange) |
|
{ |
|
return; |
|
} |
|
|
|
m_LastSmartControlVarValue = szValue; |
|
|
|
int iSel = GetCurVarListSelection(); |
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
char szKey[KEYVALUE_MAX_KEY_LENGTH]; |
|
V_strncpy(szKey, pVar->GetName(), sizeof( szKey )); |
|
|
|
CString strValue = szValue; |
|
|
|
if (pVar->GetType() == ivChoices) |
|
{ |
|
// |
|
// If a choicelist, change buffer to the string value of what we chose. |
|
// |
|
const char *pszValueString = pVar->ItemValueForString(szValue); |
|
if (pszValueString != NULL) |
|
{ |
|
strValue = pszValueString; |
|
} |
|
} |
|
|
|
UpdateKeyValue(szKey, strValue); |
|
|
|
if (m_bEnableControlUpdate) |
|
{ |
|
// Update any controls that are displaying the same data as the edit control. |
|
// This code should only be hit as a result of user input in the edit control! |
|
if (pVar->GetType() == ivAngle) |
|
{ |
|
// If they changed the "angles" key, update the main angles control. |
|
if (!stricmp(pVar->GetName(), "angles")) |
|
{ |
|
m_Angle.SetDifferent(false); |
|
m_Angle.SetAngles(strValue, true); |
|
} |
|
// Otherwise update the Smart angles control. |
|
else |
|
{ |
|
m_SmartAngle.SetDifferent(false); |
|
m_SmartAngle.SetAngles(strValue, true); |
|
} |
|
} |
|
} |
|
|
|
// We have to do this because it's an owner draw control and we're redoing the background colors. |
|
// Normally, Windows will only invalidate the second column. |
|
RECT rc; |
|
if ( m_VarList.GetItemRect( iSel, &rc, LVIR_BOUNDS ) ) |
|
m_VarList.InvalidateRect( &rc, FALSE ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function is called whenever the instance variable or value is changed |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnChangeInstanceVariableControl( void ) |
|
{ |
|
if ( m_pEditInstanceVariable && m_pEditInstanceValue ) |
|
{ |
|
char szVariable[ KEYVALUE_MAX_VALUE_LENGTH ], szValue[ KEYVALUE_MAX_VALUE_LENGTH ]; |
|
m_pEditInstanceVariable->GetWindowText( szVariable, sizeof( szVariable ) ); |
|
m_pEditInstanceValue->GetWindowText( szValue, sizeof( szValue ) ); |
|
|
|
if ( szValue[ 0 ] ) |
|
{ |
|
strcat( szVariable, " " ); |
|
strcat( szVariable, szValue ); |
|
} |
|
|
|
int iSel = GetCurVarListSelection(); |
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
char szKey[ KEYVALUE_MAX_KEY_LENGTH ]; |
|
V_strncpy( szKey, pVar->GetName(), sizeof( szKey ) ); |
|
|
|
UpdateKeyValue( szKey, szVariable ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function is called whenever the instance parameter is changed |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnChangeInstanceParmControl( void ) |
|
{ |
|
if ( m_pEditInstanceVariable && m_pComboInstanceParmType ) |
|
{ |
|
char szVariable[ KEYVALUE_MAX_VALUE_LENGTH ], szValue[ KEYVALUE_MAX_VALUE_LENGTH ]; |
|
m_pEditInstanceVariable->GetWindowText( szVariable, sizeof( szVariable ) ); |
|
|
|
int iSmartsel = m_pComboInstanceParmType->GetCurSel(); |
|
if ( iSmartsel != LB_ERR ) |
|
{ |
|
// found a selection - now get the text |
|
m_pComboInstanceParmType->GetLBText( iSmartsel, szValue ); |
|
} |
|
else |
|
{ |
|
m_pComboInstanceParmType->GetWindowText( szValue, sizeof( szValue ) ); |
|
} |
|
|
|
if ( szValue[ 0 ] ) |
|
{ |
|
strcat( szVariable, " " ); |
|
strcat( szVariable, szValue ); |
|
} |
|
|
|
int iSel = GetCurVarListSelection(); |
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
char szKey[ KEYVALUE_MAX_KEY_LENGTH ]; |
|
V_strncpy( szKey, pVar->GetName(), sizeof( szKey ) ); |
|
|
|
UpdateKeyValue( szKey, szVariable ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnChangeSmartcontrolSel(void) |
|
{ |
|
// update current value with this |
|
int iSel = GetCurVarListSelection(); |
|
if ( !m_pDisplayClass ) |
|
return; |
|
|
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
if ((pVar->GetType() != ivTargetSrc) && |
|
(pVar->GetType() != ivTargetDest) && |
|
(pVar->GetType() != ivTargetNameOrClass) && |
|
(pVar->GetType() != ivChoices) && |
|
(pVar->GetType() != ivNPCClass) && |
|
(pVar->GetType() != ivFilterClass) && |
|
(pVar->GetType() != ivPointEntityClass)) |
|
{ |
|
return; |
|
} |
|
|
|
CComboBox *pCombo = (CComboBox *)m_pSmartControl; |
|
|
|
char szBuf[128]; |
|
|
|
// get current selection |
|
int iSmartsel = pCombo->GetCurSel(); |
|
if (iSmartsel != LB_ERR) |
|
{ |
|
// found a selection - now get the text |
|
pCombo->GetLBText(iSmartsel, szBuf); |
|
} |
|
else |
|
{ |
|
// just get the text from the combo box (no selection) |
|
pCombo->GetWindowText(szBuf, 128); |
|
} |
|
|
|
if (pVar->GetType() == ivChoices) |
|
{ |
|
const char *pszValue = pVar->ItemValueForString(szBuf); |
|
if (pszValue != NULL) |
|
{ |
|
strcpy(szBuf, pszValue); |
|
} |
|
} |
|
|
|
m_LastSmartControlVarValue = szBuf; |
|
|
|
m_kvAdded.SetValue(pVar->GetName(), "1"); |
|
m_kv.SetValue(pVar->GetName(), szBuf); |
|
RefreshKVListValues( pVar->GetName() ); |
|
|
|
// We have to do this because it's an owner draw control and we're redoing the background colors. |
|
// Normally, Windows will only invalidate the second column. |
|
RECT rc; |
|
if ( m_VarList.GetItemRect( iSel, &rc, LVIR_BOUNDS ) ) |
|
m_VarList.InvalidateRect( &rc, FALSE ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : cmd - |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::SetNextVar(int cmd) |
|
{ |
|
int iSel = GetCurVarListSelection(); |
|
int nCount = m_VarList.GetItemCount(); |
|
if(iSel == LB_ERR) |
|
return; // no! |
|
iSel += cmd; |
|
if(iSel == nCount) |
|
--iSel; |
|
if(iSel == -1) |
|
++iSel; |
|
SetCurVarListSelection( iSel ); |
|
OnSelchangeKeyvalues(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set flags page |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::SetFlagsPage( COP_Flags *pFlagsPage ) |
|
{ |
|
m_pFlagsPage = pFlagsPage; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Plays the sound |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnPlaySound(void) |
|
{ |
|
if ( m_eEditType != ivSound && m_eEditType != ivScene ) |
|
return; |
|
|
|
// Get the name of the sound or VCD. |
|
char szCurrentSound[256]; |
|
m_pSmartControl->GetWindowText(szCurrentSound, 256); |
|
if (!szCurrentSound[0]) |
|
return; |
|
|
|
// Get rid of "scenes/" for scenes. |
|
CString filename = szCurrentSound; |
|
if ( m_eEditType == ivScene ) |
|
filename = StripDirPrefix( szCurrentSound, "scenes" ); |
|
|
|
// Now play the sound.. |
|
SoundType_t type; |
|
int nIndex; |
|
if ( g_Sounds.FindSoundByName( filename, &type, &nIndex ) ) |
|
g_Sounds.Play( type, nIndex ); |
|
} |
|
|
|
|
|
// Filesystem dialog module wrappers. |
|
CSysModule *g_pFSDialogModule = 0; |
|
CreateInterfaceFn g_FSDialogFactory = 0; |
|
|
|
void LoadFileSystemDialogModule() |
|
{ |
|
Assert( !g_pFSDialogModule ); |
|
|
|
// Load the module with the file system open dialog. |
|
const char *pDLLName = "FileSystemOpenDialog.dll"; |
|
g_pFSDialogModule = Sys_LoadModule( pDLLName ); |
|
if ( !g_pFSDialogModule || |
|
(g_FSDialogFactory = Sys_GetFactory( g_pFSDialogModule )) == NULL |
|
) |
|
{ |
|
if ( g_pFSDialogModule ) |
|
{ |
|
Sys_UnloadModule( g_pFSDialogModule ); |
|
g_pFSDialogModule = 0; |
|
} |
|
|
|
char str[512]; |
|
Q_snprintf( str, sizeof( str ), "Can't load %s.\n", pDLLName ); |
|
AfxMessageBox( str, MB_OK ); |
|
} |
|
} |
|
|
|
void UnloadFileSystemDialogModule() |
|
{ |
|
if ( g_pFSDialogModule ) |
|
{ |
|
Sys_UnloadModule( g_pFSDialogModule ); |
|
g_pFSDialogModule = 0; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Brings up the file browser in the appropriate default directory |
|
// based on the type of file being browsed for. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnBrowse(void) |
|
{ |
|
// handle browsing for .fgd "material" type |
|
if( m_eEditType == ivMaterial ) |
|
{ |
|
BrowseTextures( "\0" ); |
|
return; |
|
} |
|
|
|
if ( m_eEditType == ivStudioModel && Options.IsVGUIModelBrowserEnabled() ) |
|
{ |
|
char szCurrentModel[512]; |
|
char szCurrentSkin[512]; |
|
|
|
const char *result = m_kv.GetValue( "skin" ); |
|
int nSkin = ( result ) ? Q_atoi( result ) : 0; |
|
|
|
m_pSmartControl->GetWindowText( szCurrentModel, sizeof(szCurrentModel) ); |
|
|
|
if ( BrowseModels( szCurrentModel, sizeof(szCurrentModel), nSkin ) ) |
|
{ |
|
// model was changed |
|
m_pSmartControl->SetWindowText( szCurrentModel ); |
|
UpdateKeyValue("skin", itoa( nSkin, szCurrentSkin, 10 )); |
|
} |
|
return; |
|
} |
|
|
|
// handle browsing for .fgd "decal" type |
|
if(m_eEditType == ivDecal) |
|
{ |
|
if (g_pGameConfig->GetTextureFormat() == tfVMT) |
|
{ |
|
BrowseTextures("decals/"); |
|
} |
|
else |
|
{ |
|
BrowseTextures("{"); |
|
} |
|
return; |
|
} |
|
|
|
if ( m_eEditType == ivSprite ) |
|
{ |
|
BrowseTextures( "sprites/", true); |
|
return; |
|
} |
|
|
|
if ( m_eEditType == ivInstanceFile ) |
|
{ |
|
OnBrowseInstance(); |
|
return; |
|
} |
|
|
|
char *pszInitialDir = 0; |
|
|
|
|
|
// Instantiate a dialog. |
|
if ( !g_FSDialogFactory ) |
|
return; |
|
|
|
IFileSystemOpenDialog *pDlg; |
|
pDlg = (IFileSystemOpenDialog*)g_FSDialogFactory( FILESYSTEMOPENDIALOG_VERSION, NULL ); |
|
if ( !pDlg ) |
|
{ |
|
char str[512]; |
|
Q_snprintf( str, sizeof( str ), "Can't create %s interface.", FILESYSTEMOPENDIALOG_VERSION ); |
|
AfxMessageBox( str, MB_OK ); |
|
return; |
|
} |
|
pDlg->Init( g_Factory, NULL ); |
|
|
|
|
|
const char *pPathID = "GAME"; |
|
|
|
// |
|
// Based on the type of file that we are picking, set up the default extension, |
|
// default directory, and filters. Each type of file remembers its last directory. |
|
// |
|
switch (m_eEditType) |
|
{ |
|
case ivStudioModel: |
|
{ |
|
static char szInitialDir[MAX_PATH] = "models"; |
|
pszInitialDir = szInitialDir; |
|
|
|
pDlg->AddFileMask( "*.jpg" ); |
|
pDlg->AddFileMask( "*.mdl" ); |
|
pDlg->SetInitialDir( pszInitialDir, pPathID ); |
|
pDlg->SetFilterMdlAndJpgFiles( true ); |
|
break; |
|
} |
|
|
|
case ivSound: |
|
{ |
|
CString currentValue; |
|
m_pSmartControl->GetWindowText( currentValue ); |
|
|
|
CSoundBrowser soundDlg( currentValue ); |
|
if ( soundDlg.m_SoundType != SOUND_TYPE_RAW && soundDlg.m_SoundType != SOUND_TYPE_GAMESOUND ) |
|
soundDlg.m_SoundType = SOUND_TYPE_GAMESOUND; |
|
|
|
int nRet = soundDlg.DoModal(); |
|
if ( nRet == IDOK ) |
|
{ |
|
m_pSmartControl->SetWindowText(soundDlg.GetSelectedSound()); |
|
} |
|
goto Cleanup; |
|
} |
|
|
|
case ivScene: |
|
{ |
|
CString currentValue; |
|
m_pSmartControl->GetWindowText( currentValue ); |
|
CString stripped = StripDirPrefix( currentValue, "scenes" ); |
|
|
|
CSoundBrowser soundDlg( stripped ); |
|
soundDlg.m_SoundType = SOUND_TYPE_SCENE; |
|
int nRet = soundDlg.DoModal(); |
|
if ( nRet == IDOK ) |
|
{ |
|
m_pSmartControl->SetWindowText(CString("scenes\\") + soundDlg.GetSelectedSound()); |
|
} |
|
goto Cleanup; |
|
} |
|
|
|
default: |
|
{ |
|
pDlg->AddFileMask( "*.*" ); |
|
pDlg->SetInitialDir( ".", pPathID ); |
|
break; |
|
} |
|
} |
|
|
|
// |
|
// If they picked a file and hit OK, put everything after the last backslash |
|
// into the SmartEdit control. If there is no backslash, put the whole filename. |
|
// |
|
int ret; |
|
if ( 1/*g_pFullFileSystem->IsSteam()*/ || CommandLine()->FindParm( "-NewDialogs" ) ) |
|
ret = pDlg->DoModal(); |
|
else |
|
ret = pDlg->DoModal_WindowsDialog(); |
|
|
|
if ( ret == IDOK ) |
|
{ |
|
// |
|
// Save the default folder for next time. |
|
// |
|
pDlg->GetFilename( pszInitialDir, MAX_PATH ); |
|
char *pchSlash = strrchr(pszInitialDir, '\\'); |
|
if (pchSlash != NULL) |
|
{ |
|
*pchSlash = '\0'; |
|
} |
|
|
|
if (m_pSmartControl != NULL) |
|
{ |
|
// |
|
// Reverse the slashes, because the engine expects them that way. |
|
// |
|
char szTemp[MAX_PATH]; |
|
pDlg->GetFilename( szTemp, sizeof( szTemp ) ); |
|
for (unsigned int i = 0; i < strlen(szTemp); i++) |
|
{ |
|
if (szTemp[i] == '\\') |
|
{ |
|
szTemp[i] = '/'; |
|
} |
|
} |
|
|
|
m_pSmartControl->SetWindowText(szTemp); |
|
} |
|
} |
|
|
|
Cleanup:; |
|
pDlg->Release(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will display a file dialog to locate an instance vmf. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnBrowseInstance(void) |
|
{ |
|
const char *pszMapPath = "\\maps\\"; |
|
CString MapFileName; |
|
char FileName[ MAX_PATH ]; |
|
CString currentValue; |
|
CMapDoc *activeDoc = CMapDoc::GetActiveMapDoc(); |
|
|
|
m_pSmartControl->GetWindowText( currentValue ); |
|
|
|
MapFileName = activeDoc->GetPathName(); |
|
|
|
CMapInstance::DeterminePath( MapFileName, currentValue, FileName ); |
|
|
|
CFileDialog dlg( |
|
true, // open dialog? |
|
".vmf", // default file extension |
|
FileName, // initial filename |
|
OFN_ENABLESIZING, // flags |
|
"Valve Map Files (*.vmf)|*.vmf||", |
|
this ); |
|
|
|
if ( dlg.DoModal() == IDOK ) |
|
{ |
|
strcpy( FileName, dlg.GetPathName() ); |
|
V_RemoveDotSlashes( FileName ); |
|
V_FixDoubleSlashes( FileName ); |
|
V_strlower( FileName ); |
|
|
|
char *pos = strstr( FileName, pszMapPath ); |
|
if ( pos ) |
|
{ |
|
pos += strlen( pszMapPath ); |
|
*( pos - 1 ) = 0; |
|
} |
|
else if ( pos == NULL ) |
|
{ |
|
const char *pszInstancePath = CMapInstance::GetInstancePath(); |
|
|
|
if ( pszInstancePath[ 0 ] != 0 ) |
|
{ |
|
pos = strstr( FileName, pszInstancePath ); |
|
if ( pos ) |
|
{ |
|
pos += strlen( pszInstancePath ); |
|
*( pos - 1 ) = 0; |
|
} |
|
} |
|
} |
|
|
|
if ( pos ) |
|
{ |
|
m_pSmartControl->SetWindowText( pos ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnCopy(void) |
|
{ |
|
// copy entity keyvalues |
|
kvClipboard.RemoveAll(); |
|
bKvClipEmpty = FALSE; |
|
for ( int i=m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) ) |
|
{ |
|
if (stricmp(m_kv.GetKey(i), "origin")) |
|
{ |
|
kvClipboard.SetValue(m_kv.GetKey(i), m_kv.GetValue(i)); |
|
} |
|
} |
|
|
|
CString strClass = m_cClasses.GetCurrentItem(); |
|
kvClipboard.SetValue("xxxClassxxx", strClass); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnEntityHelp(void) |
|
{ |
|
CEntityHelpDlg::ShowEntityHelpDialog(); |
|
CEntityHelpDlg::SetEditGameClass(m_pDisplayClass); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnKillfocusKey(void) |
|
{ |
|
if (!m_bChangingKeyName) |
|
return; |
|
|
|
m_bChangingKeyName = false; |
|
CString strOutput; |
|
|
|
m_cKey.GetWindowText(strOutput); |
|
|
|
if (strOutput.IsEmpty()) |
|
{ |
|
AfxMessageBox("Use the delete button to remove key/value pairs."); |
|
return; |
|
} |
|
|
|
strOutput.MakeLower(); |
|
|
|
// No change |
|
if (strOutput == m_szOldKeyName) |
|
return; |
|
|
|
char szSaveValue[KEYVALUE_MAX_VALUE_LENGTH]; |
|
memset(szSaveValue, 0, sizeof(szSaveValue)); |
|
V_strcpy_safe(szSaveValue, m_kv.GetValue(m_szOldKeyName, NULL)); |
|
|
|
int iSel = GetCurVarListSelection(); |
|
if (iSel == LB_ERR) |
|
return; |
|
|
|
// Get rid of the old key. |
|
CString strBuf; |
|
strBuf = (CString)(const char*)m_VarList.GetItemData(iSel); |
|
m_kv.RemoveKey( strBuf ); // remove from local kv storage |
|
m_VarList.DeleteItem(iSel); |
|
|
|
// Add a new key with the new keyname + old value. |
|
CNewKeyValue newkv; |
|
newkv.m_Key = strOutput; |
|
newkv.m_Value = szSaveValue; |
|
|
|
m_kvAdded.SetValue(newkv.m_Key, "1"); |
|
m_kv.SetValue(newkv.m_Key, newkv.m_Value); |
|
|
|
PresentProperties(); |
|
|
|
// Select this property. |
|
SetCurVarListSelection( GetKeyValueRowByShortName( newkv.m_Key ) ); |
|
OnSelchangeKeyvalues(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Does the dirty marking deed |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::PerformMark( const char *szTargetName, bool bClear, bool bNameOrClass ) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
|
|
if (pDoc != NULL) |
|
{ |
|
if (szTargetName[0] != '\0') |
|
{ |
|
CMapEntityList Found; |
|
|
|
pDoc->FindEntitiesByName(Found, szTargetName, false); |
|
if ((Found.Count() == 0) && bNameOrClass) |
|
{ |
|
pDoc->FindEntitiesByClassName(Found, szTargetName, false); |
|
} |
|
|
|
if (Found.Count() != 0) |
|
{ |
|
CMapObjectList Select; |
|
FOR_EACH_OBJ( Found, pos ) |
|
{ |
|
CMapEntity *pEntity = Found.Element(pos); |
|
Select.AddToTail(pEntity); |
|
} |
|
|
|
if ( bClear ) |
|
{ |
|
// clear & safe previous selection |
|
pDoc->SelectObjectList(&Select); |
|
} |
|
else |
|
{ |
|
// don't save changes and add object to selection |
|
pDoc->SelectObjectList(&Select, scSelect ); |
|
} |
|
|
|
pDoc->Center2DViewsOnSelection(); |
|
} |
|
else |
|
{ |
|
MessageBox("No entities were found with that targetname.", "No entities found", MB_ICONINFORMATION | MB_OK); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Marks all entities whose targetnames match the currently selected |
|
// key value. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnMark(void) |
|
{ |
|
int iSel = GetCurVarListSelection(); |
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
char szTargetName[MAX_IO_NAME_LEN]; |
|
m_pSmartControl->GetWindowText(szTargetName, sizeof(szTargetName)); |
|
|
|
bool bNameOrClass = false; |
|
if (pVar && (pVar->GetType() == ivTargetNameOrClass)) |
|
{ |
|
bNameOrClass = true; |
|
} |
|
|
|
PerformMark( szTargetName, true, bNameOrClass ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Add the mark to the selection |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnMarkAndAdd(void) |
|
{ |
|
int iSel = GetCurVarListSelection(); |
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
// Build a temporary list of all the currently selected objects because this |
|
// process will change the selected objects. |
|
CMapEntity **pTemp = (CMapEntity**)stackalloc( m_pObjectList->Count() * sizeof(CMapEntity*) ); |
|
CUtlVector<CMapEntity*> temp( pTemp, m_pObjectList->Count() ); |
|
|
|
FOR_EACH_OBJ( *m_pObjectList, pos ) |
|
{ |
|
CMapEntity *pEntity = static_cast<CMapEntity *>(m_pObjectList->Element(pos)); |
|
temp.AddToTail( pEntity ); |
|
} |
|
|
|
// Mark all the entities referred to by the current keyvalue in the selected entities. |
|
bool bNameOrClass = false; |
|
if (pVar && (pVar->GetType() == ivTargetNameOrClass)) |
|
{ |
|
bNameOrClass = true; |
|
} |
|
|
|
for ( int i = 0; i < temp.Count(); ++i ) |
|
{ |
|
const char *pTargetName = temp[i]->GetKeyValue( pVar->GetName() ); |
|
if ( pTargetName ) |
|
{ |
|
PerformMark( pTargetName, false, bNameOrClass ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnPaste(void) |
|
{ |
|
if(bKvClipEmpty) |
|
return; |
|
|
|
CString str; |
|
GetCurKey(str); |
|
|
|
// copy entity keyvalues |
|
for (int i = kvClipboard.GetFirst(); i != kvClipboard.GetInvalidIndex(); i=kvClipboard.GetNext( i ) ) |
|
{ |
|
if (!strcmp(kvClipboard.GetKey(i), "xxxClassxxx")) |
|
{ |
|
m_cClasses.SelectItem( kvClipboard.GetValue(i) ); |
|
UpdateEditClass(kvClipboard.GetValue(i), false); |
|
UpdateDisplayClass(kvClipboard.GetValue(i)); |
|
continue; |
|
} |
|
|
|
m_kv.SetValue(kvClipboard.GetKey(i), kvClipboard.GetValue(i)); |
|
m_kvAdded.SetValue(kvClipboard.GetKey(i), "1"); |
|
} |
|
|
|
PresentProperties(); |
|
SetCurKey(str); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: For the given key name, builds a list of faces that are common |
|
// to all entitie being edited and a list of faces that are found in at |
|
// least one entity being edited. |
|
// Input : FullFaces - |
|
// PartialFaces - |
|
// pszKey - the name of the key. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::GetFaceIDListsForKey(CMapFaceIDList &FullFaces, CMapFaceIDList &PartialFaces, const char *pszKey) |
|
{ |
|
CMapWorld *pWorld = GetActiveWorld(); |
|
|
|
if ((m_pObjectList != NULL) && (pWorld != NULL)) |
|
{ |
|
bool bFirst = true; |
|
|
|
FOR_EACH_OBJ( *m_pObjectList, pos ) |
|
{ |
|
CMapClass *pObject = m_pObjectList->Element(pos); |
|
CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject); |
|
if (pEntity != NULL) |
|
{ |
|
const char *pszValue = pEntity->GetKeyValue(pszKey); |
|
|
|
if (bFirst) |
|
{ |
|
pWorld->FaceID_StringToFaceIDLists(&FullFaces, NULL, pszValue); |
|
bFirst = false; |
|
} |
|
else |
|
{ |
|
CMapFaceIDList TempFaces; |
|
pWorld->FaceID_StringToFaceIDLists(&TempFaces, NULL, pszValue); |
|
|
|
CMapFaceIDList TempFullFaces = FullFaces; |
|
FullFaces.RemoveAll(); |
|
|
|
TempFaces.Intersect(TempFullFaces, FullFaces, PartialFaces); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: For the given key name, builds a list of faces that are common |
|
// to all entitie being edited and a list of faces that are found in at |
|
// least one entity being edited. |
|
// Input : FullFaces - |
|
// PartialFaces - |
|
// pszKey - the name of the key. |
|
//----------------------------------------------------------------------------- |
|
// dvs: FIXME: try to eliminate this function |
|
void COP_Entity::GetFaceListsForKey(CMapFaceList &FullFaces, CMapFaceList &PartialFaces, const char *pszKey) |
|
{ |
|
CMapWorld *pWorld = GetActiveWorld(); |
|
|
|
if ((m_pObjectList != NULL) && (pWorld != NULL)) |
|
{ |
|
bool bFirst = true; |
|
|
|
FOR_EACH_OBJ( *m_pObjectList, pos ) |
|
{ |
|
CMapClass *pObject = m_pObjectList->Element(pos); |
|
CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject); |
|
if (pEntity != NULL) |
|
{ |
|
const char *pszValue = pEntity->GetKeyValue(pszKey); |
|
|
|
if (bFirst) |
|
{ |
|
pWorld->FaceID_StringToFaceLists(&FullFaces, NULL, pszValue); |
|
bFirst = false; |
|
} |
|
else |
|
{ |
|
CMapFaceList TempFaces; |
|
pWorld->FaceID_StringToFaceLists(&TempFaces, NULL, pszValue); |
|
|
|
CMapFaceList TempFullFaces = FullFaces; |
|
FullFaces.RemoveAll(); |
|
|
|
TempFaces.Intersect(TempFullFaces, FullFaces, PartialFaces); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnPickFaces(void) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
Assert(pDoc != NULL); |
|
|
|
if (pDoc == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_FACES); |
|
Assert(pButton != NULL); |
|
|
|
if (pButton != NULL) |
|
{ |
|
if (pButton->GetCheck()) |
|
{ |
|
int nSel = GetCurVarListSelection(); |
|
Assert(nSel != LB_ERR); |
|
|
|
if (nSel != LB_ERR ) |
|
{ |
|
GDinputvariable * pVar = GetVariableAt( nSel ); |
|
if ( pVar != NULL ) |
|
{ |
|
Assert((pVar->GetType() == ivSideList) || (pVar->GetType() == ivSide)); |
|
|
|
// FACEID TODO: make the faces highlight even when the tool is not active |
|
|
|
// |
|
// Build the list of faces that are in all selected entities, and a list |
|
// of faces that are in at least one selected entity, so that we can do |
|
// multiselect properly. |
|
// |
|
CMapFaceList FullFaces; |
|
CMapFaceList PartialFaces; |
|
GetFaceListsForKey(FullFaces, PartialFaces, pVar->GetName()); |
|
|
|
// Save the old tool so we can reset to the correct tool when we stop picking. |
|
m_ToolPrePick = ToolManager()->GetActiveToolID(); |
|
m_bPicking = true; |
|
|
|
// |
|
// Activate the face picker tool. It will handle the picking of faces. |
|
// |
|
ToolManager()->SetTool(TOOL_PICK_FACE); |
|
|
|
CToolPickFace *pTool = (CToolPickFace *)ToolManager()->GetToolForID(TOOL_PICK_FACE); |
|
pTool->SetSelectedFaces(FullFaces, PartialFaces); |
|
m_PickFaceTarget.AttachEntityDlg(this); |
|
pTool->Attach(&m_PickFaceTarget); |
|
pTool->AllowMultiSelect(pVar->GetType() == ivSideList); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// |
|
// Get the face IDs from the face picker tool. |
|
// |
|
m_bPicking = false; |
|
CToolPickFace *pTool = (CToolPickFace *)ToolManager()->GetToolForID(TOOL_PICK_FACE); |
|
UpdatePickFaceText(pTool); |
|
ToolManager()->SetTool(TOOL_POINTER); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnPickAngles(void) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
Assert(pDoc != NULL); |
|
if (pDoc == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_ANGLES); |
|
Assert(pButton != NULL); |
|
|
|
if (pButton != NULL) |
|
{ |
|
if (pButton->GetCheck()) |
|
{ |
|
int nSel = GetCurVarListSelection(); |
|
Assert(nSel != LB_ERR); |
|
|
|
if (nSel != LB_ERR) |
|
{ |
|
GDinputvariable * pVar = GetVariableAt( nSel ); |
|
if ( pVar != NULL ) |
|
{ |
|
Assert(pVar->GetType() == ivAngle); |
|
|
|
// Save the old tool so we can reset to the correct tool when we stop picking. |
|
m_ToolPrePick = ToolManager()->GetActiveToolID(); |
|
m_bPicking = true; |
|
|
|
// |
|
// Activate the angles picker tool. |
|
// |
|
CToolPickAngles *pTool = (CToolPickAngles *)ToolManager()->GetToolForID(TOOL_PICK_ANGLES); |
|
m_PickAnglesTarget.AttachEntityDlg(this); |
|
pTool->Attach(&m_PickAnglesTarget); |
|
|
|
ToolManager()->SetTool(TOOL_PICK_ANGLES); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
StopPicking(); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnPickEntity(void) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
Assert(pDoc != NULL); |
|
if (pDoc == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_ENTITY); |
|
Assert(pButton != NULL); |
|
|
|
if (pButton != NULL) |
|
{ |
|
if (pButton->GetCheck()) |
|
{ |
|
int nSel = GetCurVarListSelection(); |
|
Assert(nSel != LB_ERR); |
|
|
|
if (nSel != LB_ERR) |
|
{ |
|
GDinputvariable * pVar = GetVariableAt( nSel ); |
|
if ( pVar != NULL ) |
|
{ |
|
// Save the old tool so we can reset to the correct tool when we stop picking. |
|
m_ToolPrePick = ToolManager()->GetActiveToolID(); |
|
m_bPicking = true; |
|
|
|
// |
|
// Activate the entity picker tool. |
|
// |
|
CToolPickEntity *pTool = (CToolPickEntity *)ToolManager()->GetToolForID(TOOL_PICK_ENTITY); |
|
m_PickEntityTarget.AttachEntityDlg(this); |
|
|
|
switch (pVar->GetType()) |
|
{ |
|
case ivTargetDest: |
|
case ivTargetNameOrClass: |
|
case ivTargetSrc: |
|
{ |
|
m_PickEntityTarget.SetKeyToRetrieve("targetname"); |
|
break; |
|
} |
|
|
|
case ivNodeDest: |
|
{ |
|
m_PickEntityTarget.SetKeyToRetrieve("nodeid"); |
|
break; |
|
} |
|
|
|
default: |
|
{ |
|
Assert(false); |
|
} |
|
} |
|
|
|
pTool->Attach(&m_PickEntityTarget); |
|
|
|
ToolManager()->SetTool(TOOL_PICK_ENTITY); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
StopPicking(); |
|
} |
|
} |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Load custom colors |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::LoadCustomColors() |
|
{ |
|
if (m_bCustomColorsLoaded) |
|
return; |
|
|
|
char szRootDir[MAX_PATH]; |
|
char szFullPath[MAX_PATH]; |
|
APP()->GetDirectory(DIR_PROGRAM, szRootDir); |
|
Q_MakeAbsolutePath( szFullPath, MAX_PATH, "customcolors.dat", szRootDir ); |
|
std::ifstream file(szFullPath, std::ios::in | std::ios::binary); |
|
|
|
if(!file.is_open()) |
|
{ |
|
//Nothing to load, but don't keep trying every time the dialog pops up. |
|
m_bCustomColorsLoaded = true; |
|
return; |
|
} |
|
file.read((char*)CustomColors, sizeof(CustomColors)); |
|
file.close(); |
|
m_bCustomColorsLoaded = true; |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Save custom colors out to a file |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::SaveCustomColors() |
|
{ |
|
char szRootDir[MAX_PATH]; |
|
char szFullPath[MAX_PATH]; |
|
APP()->GetDirectory(DIR_PROGRAM, szRootDir); |
|
Q_MakeAbsolutePath( szFullPath, MAX_PATH, "customcolors.dat", szRootDir ); |
|
std::ofstream file( szFullPath, std::ios::out | std::ios::binary ); |
|
|
|
file.write((char*)CustomColors, sizeof(CustomColors)); |
|
file.close(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: this function will attempt to look up a variable from the variable map |
|
// Input : index - non-negative, it is the index into the variable map. |
|
// -1 = invalid |
|
// negative starting at the INSTANCE_VAR_MAP_START indicates it is a |
|
// custom instance parameter. |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
GDinputvariable *COP_Entity::GetVariableAt( int index ) |
|
{ |
|
if ( m_VarMap[ index ] == -1 ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
if ( m_VarMap[ index ] <= INSTANCE_VAR_MAP_START ) |
|
{ |
|
return m_InstanceParmData[ INSTANCE_VAR_MAP_START - m_VarMap[ index ] ].m_ParmVariable; |
|
} |
|
|
|
return m_pDisplayClass->GetVariableAt( m_VarMap[ index ] ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnPickColor(void) |
|
{ |
|
int iSel = GetCurVarListSelection(); |
|
GDinputvariable * pVar = GetVariableAt( iSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
if (!m_bCustomColorsLoaded) |
|
LoadCustomColors(); |
|
|
|
// find current color |
|
COLORREF clr; |
|
BYTE r = 255, g = 255, b = 255; |
|
DWORD brightness = 0xffffffff; |
|
|
|
char szTmp[128], *pTmp; |
|
m_pSmartControl->GetWindowText(szTmp, sizeof szTmp); |
|
|
|
pTmp = strtok(szTmp, " "); |
|
int iCurToken = 0; |
|
while(pTmp) |
|
{ |
|
if(pTmp[0]) |
|
{ |
|
if(iCurToken == 3) |
|
{ |
|
brightness = atol(pTmp); |
|
} |
|
else if(pVar->GetType() == ivColor255) |
|
{ |
|
if(iCurToken == 0) |
|
r = BYTE(atol(pTmp)); |
|
if(iCurToken == 1) |
|
g = BYTE(atol(pTmp)); |
|
if(iCurToken == 2) |
|
b = BYTE(atol(pTmp)); |
|
} |
|
else |
|
{ |
|
if(iCurToken == 0) |
|
r = BYTE(atof(pTmp) * 255.f); |
|
if(iCurToken == 1) |
|
g = BYTE(atof(pTmp) * 255.f); |
|
if(iCurToken == 2) |
|
b = BYTE(atof(pTmp) * 255.f); |
|
} |
|
++iCurToken; |
|
} |
|
pTmp = strtok(NULL, " "); |
|
} |
|
|
|
clr = RGB(r, g, b); |
|
|
|
CColorDialog dlg(clr, CC_FULLOPEN); |
|
dlg.m_cc.lpCustColors = CustomColors; |
|
if(dlg.DoModal() != IDOK) |
|
return; |
|
|
|
SaveCustomColors(); |
|
|
|
r = GetRValue(dlg.m_cc.rgbResult); |
|
g = GetGValue(dlg.m_cc.rgbResult); |
|
b = GetBValue(dlg.m_cc.rgbResult); |
|
|
|
// set back in field |
|
if(pVar->GetType() == ivColor255) |
|
{ |
|
sprintf(szTmp, "%d %d %d", r, g, b); |
|
} |
|
else |
|
{ |
|
sprintf(szTmp, "%.3f %.3f %.3f", float(r) / 255.f, |
|
float(g) / 255.f, float(b) / 255.f); |
|
} |
|
|
|
if(brightness != 0xffffffff) |
|
sprintf(szTmp + strlen(szTmp), " %d", brightness); |
|
|
|
m_pSmartControl->SetWindowText(szTmp); |
|
RefreshKVListValues(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnSetfocusKey(void) |
|
{ |
|
m_cKey.GetWindowText(m_szOldKeyName); |
|
|
|
if (m_szOldKeyName.IsEmpty()) |
|
return; |
|
|
|
m_szOldKeyName.MakeLower(); |
|
|
|
m_bChangingKeyName = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called whenever we are hidden or shown. |
|
// Input : bShow - TRUE if we are being shown, FALSE if we are being hidden. |
|
// nStatus - |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnShowPropertySheet(BOOL bShow, UINT nStatus) |
|
{ |
|
if (bShow) |
|
{ |
|
// |
|
// Being shown. Make sure the data in the smartedit control is correct. |
|
// |
|
OnSelchangeKeyvalues(); |
|
} |
|
else |
|
{ |
|
// |
|
// Being hidden. Abort face picking if we are doing so. |
|
// |
|
StopPicking(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : nChar - |
|
// nRepCnt - |
|
// nFlags - |
|
//----------------------------------------------------------------------------- |
|
void CMyEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) |
|
{ |
|
CEdit::OnChar(nChar, nRepCnt, nFlags); |
|
return; |
|
|
|
if(nChar == 1) // ctrl+a |
|
{ |
|
m_pParent->SetNextVar(1); |
|
} |
|
else if(nChar == 11) // ctrl+q |
|
{ |
|
m_pParent->SetNextVar(-1); |
|
} |
|
else |
|
{ |
|
CEdit::OnChar(nChar, nRepCnt, nFlags); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : nChar - |
|
// nRepCnt - |
|
// nFlags - |
|
//----------------------------------------------------------------------------- |
|
void CMyComboBox::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) |
|
{ |
|
CComboBox::OnChar(nChar, nRepCnt, nFlags); |
|
return; |
|
|
|
if(nChar == 1) // ctrl+a |
|
{ |
|
m_pParent->SetNextVar(1); |
|
} |
|
else if(nChar == 11) // ctrl+q |
|
{ |
|
m_pParent->SetNextVar(-1); |
|
} |
|
else |
|
{ |
|
CComboBox::OnChar(nChar, nRepCnt, nFlags); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets the new face ID list from the pick face tool and updates the |
|
// contents of the edit control with space-delimited face IDs. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::UpdatePickFaceText(CToolPickFace *pTool) |
|
{ |
|
char szList[KEYVALUE_MAX_VALUE_LENGTH]; |
|
szList[0] = '\0'; |
|
|
|
CMapFaceList FaceListFull; |
|
CMapFaceList FaceListPartial; |
|
|
|
pTool->GetSelectedFaces(FaceListFull, FaceListPartial); |
|
if (!CMapWorld::FaceID_FaceListsToString(szList, sizeof(szList), &FaceListFull, &FaceListPartial)) |
|
{ |
|
MessageBox("Too many faces selected for this keyvalue to hold. Deselect some faces.", "Error", MB_OK); |
|
} |
|
|
|
// |
|
// Update the edit control text with the new face IDs. This text will be |
|
// stuffed into the local keyvalue storage in OnChangeSmartControl. |
|
// |
|
m_pSmartControl->SetWindowText(szList); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles the message sent by the angles custom control when the user |
|
// changes the angle via the angle box or edit combo. |
|
// Input : WPARAM - The ID of the control that sent the message. |
|
// LPARAM - Unused. |
|
//----------------------------------------------------------------------------- |
|
LRESULT COP_Entity::OnChangeAngleBox(WPARAM nID, LPARAM) |
|
{ |
|
CString strKey; |
|
GetCurKey(strKey); |
|
|
|
char szValue[KEYVALUE_MAX_VALUE_LENGTH]; |
|
bool bUpdateControl = false; |
|
if ((nID == IDC_ANGLEBOX) || (nID == IDC_ANGLEEDIT)) |
|
{ |
|
// From the main "angles" box. |
|
m_Angle.GetAngles(szValue); |
|
|
|
// Only update the edit control text if the "angles" key is selected. |
|
if (!strKey.CompareNoCase("angles")) |
|
{ |
|
bUpdateControl = true; |
|
} |
|
|
|
// Slam "angles" into the key name since that's the key we're modifying. |
|
strKey = "angles"; |
|
} |
|
else |
|
{ |
|
// From the secondary angles box that edits the selected keyvalue. |
|
m_SmartAngle.GetAngles(szValue); |
|
bUpdateControl = true; |
|
} |
|
|
|
// Commit the change to our local storage. |
|
UpdateKeyValue(strKey, szValue); |
|
|
|
if (bUpdateControl) |
|
{ |
|
if (m_bSmartedit) |
|
{ |
|
// Reflect the change in the SmartEdit control. |
|
Assert(m_pSmartControl); |
|
if (m_pSmartControl) |
|
{ |
|
m_bEnableControlUpdate = false; |
|
m_pSmartControl->SetWindowText(szValue); |
|
m_bEnableControlUpdate = true; |
|
} |
|
} |
|
else |
|
{ |
|
// Reflect the change in the keyvalue control. |
|
m_bEnableControlUpdate = false; |
|
m_cValue.SetWindowText(szValue); |
|
m_bEnableControlUpdate = true; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void COP_Entity::OnCameraDistance(void) |
|
{ |
|
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); |
|
CMapView3D *pView = pDoc->GetFirst3DView(); |
|
if ( !pView ) |
|
return; |
|
const CCamera *camera = pView->GetCamera(); |
|
Vector cameraPos; |
|
camera->GetViewPoint( cameraPos ); |
|
Assert(pDoc != NULL); |
|
if (pDoc == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
int nSel = GetCurVarListSelection(); |
|
Assert(nSel != LB_ERR); |
|
|
|
if (nSel != LB_ERR) |
|
{ |
|
GDinputvariable * pVar = GetVariableAt( nSel ); |
|
if ( pVar == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
Vector objectPos; |
|
const CMapObjectList *pSelection = pDoc->GetSelection()->GetList(); |
|
int iSelectionCount = pSelection->Count(); |
|
if ( iSelectionCount == 1 ) |
|
{ |
|
// Only 1 entity selected.. we can just set our SmartControl text and the change will get applied |
|
// when they close the properties dialog or click Apply. |
|
CMapClass *selectedObject = pSelection->Element(iSelectionCount - 1); |
|
selectedObject->GetOrigin( objectPos ); |
|
int distance = VectorLength( cameraPos - objectPos ); |
|
char buf[255]; |
|
itoa( distance, buf, 10 ); |
|
m_pSmartControl->SetWindowText(buf); |
|
} |
|
else |
|
{ |
|
// Multiple entities selected. We have to apply the current set of changes, |
|
// Set the value in each entity and set the kv text to VALUE_DIFFERENT_STRING so it doesn't overwrite anything when we Apply(). |
|
int index = m_kv.FindByKeyName( pVar->GetName() ); |
|
if ( index == m_kv.GetInvalidIndex() ) |
|
return; |
|
|
|
// First set VALUE_DIFFERENT_STRING in our smart control and in m_kv. |
|
m_pSmartControl->SetWindowText( VALUE_DIFFERENT_STRING ); |
|
MDkeyvalue &kvCur = m_kv.GetKeyValue( index ); |
|
V_strncpy( kvCur.szValue, VALUE_DIFFERENT_STRING, sizeof( kvCur.szValue ) ); |
|
|
|
// Get the list of objects we'll apply this to. |
|
CMapObjectList objectList; |
|
FOR_EACH_OBJ( *m_pObjectList, pos ) |
|
{ |
|
CMapClass *pObject = m_pObjectList->Element(pos); |
|
if ( pObject && !IsWorldObject( pObject ) && dynamic_cast <CEditGameClass *>(pObject) ) |
|
objectList.AddToTail( pObject ); |
|
} |
|
|
|
// Now set the distance property directly on the selected entities. |
|
if ( objectList.Count() > 0 ) |
|
{ |
|
// Setup undo stuff. |
|
GetHistory()->MarkUndoPosition( pDoc->GetSelection()->GetList(), "Change Properties"); |
|
GetHistory()->Keep( &objectList ); |
|
|
|
FOR_EACH_OBJ( objectList, pos ) |
|
{ |
|
CMapClass *pObject = m_pObjectList->Element(pos); |
|
CEditGameClass *pEdit = dynamic_cast <CEditGameClass *>(pObject); |
|
Assert( pObject && pEdit ); |
|
|
|
pObject->GetOrigin( objectPos ); |
|
int distance = VectorLength( cameraPos - objectPos ); |
|
char buf[255]; |
|
itoa( distance, buf, 10 ); |
|
|
|
pEdit->SetKeyValue( pVar->GetName(), buf ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
void COP_Entity::OnTextChanged( const char *pText ) |
|
{ |
|
m_bClassSelectionEmpty = false; |
|
UpdateDisplayClass( pText ); |
|
} |
|
|
|
|
|
bool COP_Entity::OnUnknownEntry( const char *pText ) |
|
{ |
|
// They entered a classname we don't recognize. Get rid of all keys. |
|
// It's about to call OnTextChanged, and we'll null m_pDisplayClass, disable SmartEdit, etc. |
|
m_kv.RemoveAll(); |
|
return true; |
|
} |
|
|
|
void COP_Entity::OnSmartControlTargetNameChanged( const char *pText ) |
|
{ |
|
CFilteredComboBox *pCombo = dynamic_cast<CFilteredComboBox*>( m_pSmartControl ); |
|
Assert( pCombo ); |
|
if ( pCombo ) |
|
{ |
|
InternalOnChangeSmartcontrol( pCombo->GetCurrentItem() ); |
|
} |
|
} |
|
|
|
|
|
void COP_Entity::GetItemColor( int iItem, COLORREF *pBackgroundColor, COLORREF *pTextColor ) |
|
{ |
|
// Setup the background color. |
|
EKeyState eState; |
|
bool bMissingTarget; |
|
GetKeyState( (const char*)m_VarList.GetItemData( iItem ), &eState, &bMissingTarget ); |
|
|
|
if ( eState == k_EKeyState_Modified ) |
|
*pBackgroundColor = g_BgColor_Edited; |
|
else if ( eState == k_EKeyState_AddedManually ) |
|
*pBackgroundColor = g_BgColor_Added; |
|
else if ( eState == k_EKeyState_InstanceParm ) |
|
*pBackgroundColor = g_BgColor_InstanceParm; |
|
else |
|
*pBackgroundColor = g_BgColor_Default; |
|
|
|
// Setup the text color. |
|
if ( bMissingTarget ) |
|
*pTextColor = g_TextColor_MissingTarget; |
|
else |
|
*pTextColor = g_TextColor_Normal; |
|
} |
|
|
|
|
|
bool COP_Entity::CustomDrawItemValue( const LPDRAWITEMSTRUCT p, const RECT *pRect ) |
|
{ |
|
if ( !m_bSmartedit || p->itemID < 0 || p->itemID >= ARRAYSIZE(m_VarMap) || m_VarMap[p->itemID] < 0 ) |
|
return false; |
|
|
|
if ( !m_pDisplayClass ) |
|
return false; |
|
|
|
GDinputvariable * pVar = GetVariableAt( p->itemID ); |
|
if ( pVar == NULL ) |
|
{ |
|
return false; |
|
} |
|
if ( pVar && (pVar->GetType() == ivColor255 || pVar->GetType() == ivColor1) ) |
|
{ |
|
const char *pValue = m_kv.GetValue( pVar->GetName() ); |
|
if ( pValue ) |
|
{ |
|
int r, g, b; |
|
if ( pVar->GetType() == ivColor255 ) |
|
{ |
|
sscanf( pValue, "%d %d %d", &r, &g, &b ); |
|
} |
|
else |
|
{ |
|
float fr, fg, fb; |
|
sscanf( pValue, "%f %f %f", &fr, &fg, &fb ); |
|
r = (int)(fr * 255.0); |
|
g = (int)(fg * 255.0); |
|
b = (int)(fb * 255.0); |
|
} |
|
|
|
HBRUSH hBrush = CreateSolidBrush( RGB( r, g, b ) ); |
|
HPEN hPen = CreatePen( PS_SOLID, 0, RGB(0,0,0) ); |
|
SelectObject( p->hDC, hBrush ); |
|
SelectObject( p->hDC, hPen ); |
|
|
|
RECT rc = *pRect; |
|
Rectangle( p->hDC, rc.left+6, rc.top+2, rc.right-6, rc.bottom-2 ); |
|
|
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: The flags page calls this whenever a spawnflag changes in it. |
|
// Input : preserveMask tells the bits you should NOT change in the spawnflags value. |
|
// newValues is the values of the other bits. |
|
//----------------------------------------------------------------------------- |
|
void COP_Entity::OnUpdateSpawnFlags( unsigned long preserveMask, unsigned long newValues ) |
|
{ |
|
const char *p = m_kv.GetValue( SPAWNFLAGS_KEYNAME ); |
|
if ( !p ) |
|
return; |
|
|
|
unsigned long oldValue = 0; |
|
sscanf( p, "%lu", &oldValue ); |
|
|
|
unsigned long newValue = (oldValue & preserveMask) | (newValues & ~preserveMask); |
|
|
|
// We print the string ourselves here because the int version of SetValue will show a negative number |
|
// if we exceed 1<<31. |
|
char str[512]; |
|
V_snprintf( str, sizeof( str ), "%lu", newValue ); |
|
m_kv.SetValue( SPAWNFLAGS_KEYNAME, str ); |
|
|
|
RefreshKVListValues( SPAWNFLAGS_KEYNAME ); |
|
OnSelchangeKeyvalues(); // Refresh the control with its value in case it's selected currently. |
|
} |
|
|
|
|
|
void COP_Entity::OnSize( UINT nType, int cx, int cy ) |
|
{ |
|
m_AnchorMgr.OnSize(); |
|
} |
|
|
|
|
|
|