//========= 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();
}