Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4829 lines
131 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "hammer.h"
  8. #include "EntityHelpDlg.h"
  9. #include "History.h"
  10. #include "MainFrm.h"
  11. #include "MapWorld.h"
  12. #include "OP_Entity.h"
  13. #include "CustomMessages.h"
  14. #include "NewKeyValue.h"
  15. #include "GlobalFunctions.h"
  16. #include "MapDoc.h"
  17. #include "MapEntity.h"
  18. #include "ObjectProperties.h"
  19. #include "TargetNameCombo.h"
  20. #include "TextureBrowser.h"
  21. #include "TextureSystem.h"
  22. #include "ToolPickAngles.h"
  23. #include "ToolPickEntity.h"
  24. #include "ToolPickFace.h"
  25. #include "ToolManager.h"
  26. #include "SoundBrowser.h"
  27. #include "ifilesystemopendialog.h"
  28. #include "filesystem_tools.h"
  29. #include "tier0/icommandline.h"
  30. #include "HammerVGui.h"
  31. #include "mapview3d.h"
  32. #include "camera.h"
  33. #include "Selection.h"
  34. #include "options.h"
  35. #include "op_flags.h"
  36. #include "MapInstance.h"
  37. extern GameData *pGD; // current game data
  38. // memdbgon must be the last include file in a .cpp file!!!
  39. #include <tier0/memdbgon.h>
  40. #pragma warning( disable : 4355 )
  41. #define IDC_SMARTCONTROL 1
  42. #define IDC_SMARTCONTROL_TARGETNAME 2 // We have a different define for this because we rely 100%
  43. // on CTargetNameComboBox for the info about its updates.
  44. #define IDC_SMARTCONTROL_INSTANCE_VARIABLE 3
  45. #define IDC_SMARTCONTROL_INSTANCE_VALUE 4
  46. #define IDC_SMARTCONTROL_INSTANCE_PARM 5
  47. #define SPAWNFLAGS_KEYNAME "spawnflags"
  48. #define INSTANCE_VAR_MAP_START -10
  49. static WCKeyValues kvClipboard;
  50. static BOOL bKvClipEmpty = TRUE;
  51. // Colors used for the keyvalues list control.
  52. static COLORREF g_BgColor_Edited = RGB( 239, 239, 255 ); // blue
  53. static COLORREF g_BgColor_Default = RGB( 255, 255, 255 ); // white
  54. static COLORREF g_BgColor_Added = RGB( 255, 239, 239 ); // pink
  55. static COLORREF g_BgColor_InstanceParm = RGB( 239, 255, 239 ); // green
  56. static COLORREF g_TextColor_Normal = RGB( 0, 0, 0 );
  57. static COLORREF g_TextColor_MissingTarget = RGB( 255, 0, 0 ); // dark red
  58. static int g_DumbEditControls[] = {IDC_DELETEKEYVALUE, IDC_KEY, IDC_VALUE, IDC_ADDKEYVALUE, IDC_KEY_LABEL, IDC_VALUE_LABEL};
  59. //-----------------------------------------------------------------------------
  60. // Less function for use with CString
  61. //-----------------------------------------------------------------------------
  62. bool CStringLessFunc(const CString &lhs, const CString &rhs)
  63. {
  64. return (Q_strcmp(lhs, rhs) < 0);
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose: Returns true if the string specifies the name of an entity in the world.
  68. //-----------------------------------------------------------------------------
  69. static bool IsValidTargetName( const char *pTestName )
  70. {
  71. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  72. CMapWorld *pWorld = pDoc->GetMapWorld();
  73. const CMapEntityList *pList = pWorld->EntityList_GetList();
  74. for ( int i=0; i < pList->Count(); i++ )
  75. {
  76. CMapEntity *pEntity = pList->Element( i );
  77. const char *pszTargetName = pEntity->GetKeyValue("targetname");
  78. if ( pszTargetName && Q_stricmp( pszTargetName, pTestName ) == 0 )
  79. return true;
  80. }
  81. return false;
  82. }
  83. static CString StripDirPrefix( const char *pFilename, const char *pPrefix )
  84. {
  85. int prefixLen = V_strlen( pPrefix );
  86. if ( V_stristr( pFilename, pPrefix ) == pFilename )
  87. {
  88. if ( pFilename[prefixLen] == '/' || pFilename[prefixLen] == '\\' )
  89. return pFilename + prefixLen + 1;
  90. }
  91. return pFilename;
  92. }
  93. //-----------------------------------------------------------------------------
  94. // CColoredListCtrl implementation.
  95. //-----------------------------------------------------------------------------
  96. CColoredListCtrl::CColoredListCtrl( IItemColorCallback *pCallback )
  97. {
  98. m_pCallback = pCallback;
  99. }
  100. void CColoredListCtrl::DrawItem( LPDRAWITEMSTRUCT p )
  101. {
  102. CDC *pDC = CDC::FromHandle( p->hDC );
  103. COLORREF bgColor, txtColor;
  104. m_pCallback->GetItemColor( p->itemID, &bgColor, &txtColor );
  105. // Draw the background.
  106. CBrush br;
  107. CPen pen( PS_SOLID, 0, bgColor );
  108. // Selected? Draw a dotted border.
  109. LOGBRUSH logBrush;
  110. logBrush.lbColor = RGB(0,0,0);
  111. logBrush.lbHatch = HS_CROSS;
  112. logBrush.lbStyle = BS_SOLID;
  113. CPen dashedPen( PS_ALTERNATE, 1, &logBrush );
  114. if ( p->itemState & ODS_SELECTED )
  115. pDC->SelectObject( &dashedPen );
  116. else
  117. pDC->SelectObject( &pen );
  118. br.CreateSolidBrush( bgColor );
  119. pDC->SelectObject( &br );
  120. RECT rcFill = p->rcItem;
  121. rcFill.bottom -= 1;
  122. pDC->Rectangle( &rcFill );
  123. // Setup for drawing text.
  124. pDC->SetTextColor( txtColor );
  125. // Draw the first column.
  126. RECT rcItem = p->rcItem;
  127. rcItem.left += 3;
  128. pDC->DrawText( GetItemText( p->itemID, 0 ), &rcItem, DT_LEFT | DT_VCENTER );
  129. // Draw the second column.
  130. LVCOLUMN columnInfo;
  131. columnInfo.mask = LVCF_WIDTH;
  132. GetColumn( 0, &columnInfo );
  133. rcItem = p->rcItem;
  134. rcItem.left += columnInfo.cx;
  135. // Give our owner a chance to draw the second column.
  136. if ( !m_pCallback->CustomDrawItemValue( p, &rcItem ) )
  137. {
  138. rcItem.left += 3;
  139. pDC->DrawText( GetItemText( p->itemID, 1 ), &rcItem, DT_LEFT | DT_VCENTER );
  140. }
  141. }
  142. class CMyEdit : public CEdit
  143. {
  144. public:
  145. void SetParentPage(COP_Entity* pPage)
  146. {
  147. m_pParent = pPage;
  148. }
  149. afx_msg void OnChar(UINT, UINT, UINT);
  150. COP_Entity *m_pParent;
  151. DECLARE_MESSAGE_MAP()
  152. };
  153. BEGIN_MESSAGE_MAP(CMyEdit, CEdit)
  154. ON_WM_CHAR()
  155. END_MESSAGE_MAP()
  156. class CMyComboBox : public CComboBox
  157. {
  158. public:
  159. void SetParentPage(COP_Entity* pPage)
  160. {
  161. m_pParent = pPage;
  162. }
  163. afx_msg void OnChar(UINT, UINT, UINT);
  164. COP_Entity *m_pParent;
  165. DECLARE_MESSAGE_MAP()
  166. };
  167. BEGIN_MESSAGE_MAP(CMyComboBox, CComboBox)
  168. ON_WM_CHAR()
  169. END_MESSAGE_MAP()
  170. //-----------------------------------------------------------------------------
  171. // Purpose: Called by the angles picker tool when a target point is picked. This
  172. // stuffs the angles into the smartedit control so that the entity
  173. // points at the target position.
  174. //-----------------------------------------------------------------------------
  175. void CPickAnglesTarget::OnNotifyPickAngles(const Vector &vecPos)
  176. {
  177. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  178. if (!pDoc)
  179. {
  180. return;
  181. }
  182. GetHistory()->MarkUndoPosition(pDoc->GetSelection()->GetList(), "Point At");
  183. //
  184. // Update the edit control text with the entity name. This text will be
  185. // stuffed into the local keyvalue storage in OnChangeSmartControl.
  186. //
  187. FOR_EACH_OBJ( *m_pDlg->m_pObjectList, pos )
  188. {
  189. CMapClass *pObject = m_pDlg->m_pObjectList->Element(pos);
  190. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  191. Assert(pEntity != NULL);
  192. if (pEntity != NULL)
  193. {
  194. GetHistory()->Keep(pEntity);
  195. // Calculate the angles to point this entity at the chosen spot.
  196. Vector vecOrigin;
  197. pEntity->GetOrigin(vecOrigin);
  198. Vector vecForward = vecPos - vecOrigin;
  199. QAngle angFace;
  200. VectorAngles(vecForward, angFace);
  201. // HACK: lights negate pitch
  202. if (pEntity->GetClassName() && (!strnicmp(pEntity->GetClassName(), "light_", 6)))
  203. {
  204. angFace[PITCH] *= -1;
  205. }
  206. // Update the edit control with the calculated angles.
  207. char szAngles[80];
  208. sprintf(szAngles, "%.0f %.0f %.0f", angFace[PITCH], angFace[YAW], angFace[ROLL]);
  209. pEntity->SetKeyValue("angles", szAngles);
  210. // HACK: lights have a separate "pitch" key
  211. if (pEntity->GetClassName() && (!strnicmp(pEntity->GetClassName(), "light_", 6)))
  212. {
  213. char szPitch[20];
  214. sprintf(szPitch, "%.0f", angFace[PITCH]);
  215. pEntity->SetKeyValue("pitch", szPitch);
  216. }
  217. // FIXME: this should be called automatically, but it isn't
  218. m_pDlg->OnChangeSmartcontrol();
  219. }
  220. }
  221. m_pDlg->StopPicking();
  222. GetMainWnd()->pObjectProperties->MarkDataDirty();
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Called by the entity picker tool when an entity is picked. This
  226. // stuffs the entity name into the smartedit control.
  227. //-----------------------------------------------------------------------------
  228. void CPickEntityTarget::OnNotifyPickEntity(CToolPickEntity *pTool)
  229. {
  230. //
  231. // Update the edit control text with the entity name. This text will be
  232. // stuffed into the local keyvalue storage in OnChangeSmartControl.
  233. //
  234. CMapEntityList Full;
  235. CMapEntityList Partial;
  236. pTool->GetSelectedEntities(Full, Partial);
  237. CMapEntity *pEntity = Full.Element(0);
  238. if (pEntity)
  239. {
  240. const char *pszValue = pEntity->GetKeyValue(m_szKey);
  241. if (!pszValue)
  242. {
  243. pszValue = "";
  244. }
  245. m_pDlg->SetSmartControlText(pszValue);
  246. }
  247. m_pDlg->StopPicking();
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Purpose: Called by the face picker tool when the face selection changes.
  251. // Input : pTool - The face picker tool that is notifying us.
  252. //-----------------------------------------------------------------------------
  253. void CPickFaceTarget::OnNotifyPickFace(CToolPickFace *pTool)
  254. {
  255. m_pDlg->UpdatePickFaceText(pTool);
  256. }
  257. CSmartControlTargetNameRouter::CSmartControlTargetNameRouter( COP_Entity *pDlg )
  258. {
  259. m_pDlg = pDlg;
  260. }
  261. void CSmartControlTargetNameRouter::OnTextChanged( const char *pText )
  262. {
  263. m_pDlg->OnSmartControlTargetNameChanged( pText );
  264. }
  265. BEGIN_MESSAGE_MAP(COP_Entity, CObjectPage)
  266. //{{AFX_MSG_MAP(COP_Entity)
  267. ON_NOTIFY(LVN_ITEMCHANGED, IDC_KEYVALUES, OnItemChangedKeyValues)
  268. ON_NOTIFY(NM_DBLCLK, IDC_KEYVALUES, OnDblClickKeyValues)
  269. ON_BN_CLICKED(IDC_ADDKEYVALUE, OnAddkeyvalue)
  270. ON_BN_CLICKED(IDC_REMOVEKEYVALUE, OnRemovekeyvalue)
  271. ON_BN_CLICKED(IDC_SMARTEDIT, OnSmartedit)
  272. ON_EN_CHANGE(IDC_VALUE, OnChangeKeyorValue)
  273. ON_BN_CLICKED(IDC_COPY, OnCopy)
  274. ON_BN_CLICKED(IDC_PASTE, OnPaste)
  275. ON_BN_CLICKED(IDC_PICKCOLOR, OnPickColor)
  276. ON_WM_SIZE()
  277. ON_EN_SETFOCUS(IDC_KEY, OnSetfocusKey)
  278. ON_EN_KILLFOCUS(IDC_KEY, OnKillfocusKey)
  279. ON_MESSAGE(ABN_CHANGED, OnChangeAngleBox)
  280. ON_CBN_SELCHANGE(IDC_SMARTCONTROL, OnChangeSmartcontrolSel)
  281. ON_CBN_EDITUPDATE(IDC_SMARTCONTROL, OnChangeSmartcontrol)
  282. ON_EN_CHANGE(IDC_SMARTCONTROL, OnChangeSmartcontrol)
  283. ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
  284. ON_BN_CLICKED(IDC_BROWSE_INSTANCE, OnBrowseInstance)
  285. ON_BN_CLICKED(IDC_PLAY_SOUND, OnPlaySound)
  286. ON_BN_CLICKED(IDC_MARK, OnMark)
  287. ON_BN_CLICKED(IDC_MARK_AND_ADD, OnMarkAndAdd)
  288. ON_BN_CLICKED(IDC_PICK_FACES, OnPickFaces)
  289. ON_BN_CLICKED(IDC_ENTITY_HELP, OnEntityHelp)
  290. ON_BN_CLICKED(IDC_PICK_ANGLES, OnPickAngles)
  291. ON_BN_CLICKED(IDC_PICK_ENTITY, OnPickEntity)
  292. ON_BN_CLICKED(IDC_CAMERA_DISTANCE, OnCameraDistance)
  293. ON_EN_CHANGE(IDC_SMARTCONTROL_INSTANCE_VARIABLE, OnChangeInstanceVariableControl)
  294. ON_EN_CHANGE(IDC_SMARTCONTROL_INSTANCE_VALUE, OnChangeInstanceVariableControl)
  295. ON_EN_CHANGE(IDC_SMARTCONTROL_INSTANCE_PARM, OnChangeInstanceParmControl)
  296. ON_CBN_SELCHANGE(IDC_SMARTCONTROL_INSTANCE_PARM, OnChangeInstanceParmControl)
  297. ON_CBN_EDITUPDATE(IDC_SMARTCONTROL_INSTANCE_PARM, OnChangeInstanceParmControl)
  298. //}}AFX_MSG_MAP
  299. END_MESSAGE_MAP()
  300. IMPLEMENT_DYNCREATE(COP_Entity, CObjectPage)
  301. typedef int (CALLBACK *ColumnSortFn)( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam );
  302. int InternalSortByColumn( COP_Entity *pDlg, const char *pShortName1, const char *pShortName2, int iColumn )
  303. {
  304. int i1 = pDlg->GetKeyValueRowByShortName( pShortName1 );
  305. int i2 = pDlg->GetKeyValueRowByShortName( pShortName2 );
  306. if ( i1 == -1 || i2 == -1 )
  307. return 0;
  308. CString str1 = pDlg->m_VarList.GetItemText( i1, iColumn );
  309. CString str2 = pDlg->m_VarList.GetItemText( i2, iColumn );
  310. return Q_stricmp( str1, str2 );
  311. }
  312. int CALLBACK SortByItemEditedState( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
  313. {
  314. COP_Entity *pDlg = (COP_Entity*)lpParam;
  315. if ( !pDlg->m_pDisplayClass )
  316. return 0;
  317. const char *pShortName1 = (const char*)iItem1;
  318. const char *pShortName2 = (const char*)iItem2;
  319. EKeyState s1, s2;
  320. bool b1, b2;
  321. pDlg->GetKeyState( pShortName1, &s1, &b1 );
  322. pDlg->GetKeyState( pShortName2, &s2, &b2 );
  323. bool bNew1 = (s1 == k_EKeyState_AddedManually);
  324. bool bNew2 = (s2 == k_EKeyState_AddedManually);
  325. return bNew1 < bNew2;
  326. }
  327. static int CALLBACK SortByColumn0( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
  328. {
  329. return InternalSortByColumn( (COP_Entity*)lpParam, (const char*)iItem1, (const char*)iItem2, 0 );
  330. }
  331. static int CALLBACK SortByColumn1( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
  332. {
  333. return InternalSortByColumn( (COP_Entity*)lpParam, (const char*)iItem1, (const char*)iItem2, 1 );
  334. }
  335. ColumnSortFn g_ColumnSortFunctions[] =
  336. {
  337. SortByItemEditedState,
  338. SortByColumn0,
  339. SortByColumn1
  340. };
  341. //-----------------------------------------------------------------------------
  342. // Purpose:
  343. //-----------------------------------------------------------------------------
  344. COP_Entity::COP_Entity()
  345. : CObjectPage(COP_Entity::IDD),
  346. m_cClasses( this ),
  347. m_SmartControlTargetNameRouter( this ),
  348. m_VarList( this ),
  349. m_InstanceParmData( CStringLessFunc )
  350. {
  351. //{{AFX_DATA_INIT(COP_Entity)
  352. // NOTE: the ClassWizard will add member initialization here
  353. //}}AFX_DATA_INIT
  354. m_iLastClassListSolidClasses = -9999;
  355. m_bAllowPresentProperties = true;
  356. m_nPresentPropertiesCalls = 0;
  357. m_bClassSelectionEmpty = false;
  358. m_cClasses.SetOnlyProvideSuggestions( true );
  359. m_bPicking = false;
  360. m_bChangingKeyName = false;
  361. m_pSmartBrowseButton = NULL;
  362. m_pLastSmartControlVar = NULL;
  363. m_pEditInstanceVariable = NULL;
  364. m_pEditInstanceValue = NULL;
  365. m_pComboInstanceParmType = NULL;
  366. m_bIgnoreKVChange = false;
  367. m_bSmartedit = true;
  368. m_pSmartControl = NULL;
  369. m_pDisplayClass = NULL;
  370. m_pEditClass = NULL;
  371. m_eEditType = ivString;
  372. m_nNewKeyCount = 0;
  373. m_iSortColumn = -1;
  374. m_bEnableControlUpdate = true;
  375. m_pEditObjectRuntimeClass = RUNTIME_CLASS(editCEditGameClass);
  376. pModelBrowser = NULL;
  377. m_pInstanceVar = NULL;
  378. m_bCustomColorsLoaded = false; //Make sure they get loaded!
  379. memset(CustomColors, 0, sizeof(CustomColors));
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose: Destructor.
  383. //-----------------------------------------------------------------------------
  384. COP_Entity::~COP_Entity(void)
  385. {
  386. DestroySmartControls();
  387. delete pModelBrowser;
  388. pModelBrowser = NULL;
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose:
  392. // Input : pDX -
  393. //-----------------------------------------------------------------------------
  394. void COP_Entity::DoDataExchange(CDataExchange* pDX)
  395. {
  396. CObjectPage::DoDataExchange(pDX);
  397. //{{AFX_DATA_MAP(COP_Entity)
  398. DDX_Control(pDX, IDC_VALUE, m_cValue);
  399. DDX_Control(pDX, IDC_KEYVALUES, m_VarList);
  400. DDX_Control(pDX, IDC_KEY, m_cKey);
  401. DDX_Control(pDX, IDC_ENTITY_COMMENTS, m_Comments);
  402. DDX_Control(pDX, IDC_KEYVALUE_HELP, m_KeyValueHelpText);
  403. DDX_Control(pDX, IDC_PASTE, m_PasteControl);
  404. //}}AFX_DATA_MAP
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: Handles notifications..
  408. //-----------------------------------------------------------------------------
  409. BOOL COP_Entity::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
  410. {
  411. NMHDR *pHdr = (NMHDR*)lParam;
  412. if ( pHdr->idFrom == IDC_KEYVALUES )
  413. {
  414. if ( pHdr->code == LVN_COLUMNCLICK )
  415. {
  416. LPNMLISTVIEW pListView = (LPNMLISTVIEW)lParam;
  417. // Now sort by this column.
  418. m_iSortColumn = max( 0, min( pListView->iSubItem, ARRAYSIZE( g_ColumnSortFunctions ) - 1 ) );
  419. ResortItems();
  420. }
  421. }
  422. return CObjectPage::OnNotify(wParam, lParam, pResult);
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Determine how/if this key's value has been modified relative
  426. // to its default in the FGD file.
  427. //-----------------------------------------------------------------------------
  428. void COP_Entity::GetKeyState( const char *pShortName, EKeyState *pState, bool *pMissingTarget )
  429. {
  430. *pMissingTarget = false;
  431. // If we're in multiedit mode with various types of entities selected, then don't look at m_pDisplayClass.
  432. if ( !m_pDisplayClass )
  433. {
  434. *pState = k_EKeyState_DefaultFGDValue;
  435. return;
  436. }
  437. const char *pszCurValue = m_kv.GetValue( pShortName );
  438. if ( !pszCurValue )
  439. pszCurValue = "";
  440. // Now see if this var is even in the FGD.
  441. GDinputvariable *pVar = m_pDisplayClass->VarForName( pShortName );
  442. if ( !pVar )
  443. {
  444. if ( m_InstanceParmData.Find( pShortName ) != m_InstanceParmData.InvalidIndex() )
  445. {
  446. *pState = k_EKeyState_InstanceParm;
  447. }
  448. else
  449. {
  450. *pState = k_EKeyState_AddedManually;
  451. }
  452. return;
  453. }
  454. // Missing targetname?
  455. if ((pVar->GetType() == ivTargetSrc) || (pVar->GetType() == ivTargetDest))
  456. {
  457. if ( pszCurValue[0] && !IsValidTargetName( pszCurValue ) )
  458. *pMissingTarget = true;
  459. }
  460. // Now we know it's in the FGD, so see if the value has been modified from the default.
  461. GDinputvariable varCopy;
  462. varCopy = *pVar;
  463. MDkeyvalue tmpkv;
  464. varCopy.ResetDefaults();
  465. varCopy.ToKeyValue( &tmpkv );
  466. if ( Q_stricmp( pszCurValue, tmpkv.szValue ) == 0 )
  467. *pState = k_EKeyState_DefaultFGDValue;
  468. else
  469. *pState = k_EKeyState_Modified;
  470. }
  471. void COP_Entity::ResortItems()
  472. {
  473. m_VarList.SortItems( g_ColumnSortFunctions[m_iSortColumn+1], (LPARAM)this );
  474. // Update m_VarMap if in smart edit mode.
  475. if ( m_bSmartedit )
  476. {
  477. for ( int i=0; i < m_VarList.GetItemCount(); i++ )
  478. {
  479. const char *pShortName = (const char*)m_VarList.GetItemData( i );
  480. if ( !pShortName )
  481. continue;
  482. int index = -1;
  483. if ( m_pDisplayClass && m_pDisplayClass->VarForName( pShortName, &index ) )
  484. {
  485. m_VarMap[i] = index;
  486. }
  487. else
  488. {
  489. index = m_InstanceParmData.Find( pShortName );
  490. if ( index != m_InstanceParmData.InvalidIndex() )
  491. {
  492. m_VarMap[i] = INSTANCE_VAR_MAP_START - index;
  493. }
  494. else
  495. {
  496. m_VarMap[i] = -1;
  497. }
  498. }
  499. }
  500. }
  501. }
  502. //-----------------------------------------------------------------------------
  503. //-----------------------------------------------------------------------------
  504. void DumpKeyvalues(WCKeyValues &kv)
  505. {
  506. for (int i = kv.GetFirst(); i != kv.GetInvalidIndex(); i=kv.GetNext( i ) )
  507. {
  508. DBG(" %d: %s\n", i, kv.GetKey(i));
  509. }
  510. }
  511. //-----------------------------------------------------------------------------
  512. // Purpose: Adds an object's keys to our list of keys. If a given key is already
  513. // in the list, it is either ignored or set to a "different" if the value
  514. // is different from the value in our list.
  515. // Input : pEdit - Object whose keys are to be added to our list.
  516. //-----------------------------------------------------------------------------
  517. void COP_Entity::MergeObjectKeyValues(CEditGameClass *pEdit)
  518. {
  519. //VPROF_BUDGET( "COP_Entity::MergeObjectKeyValues", "Object Properties" );
  520. for ( int i=pEdit->GetFirstKeyValue(); i != pEdit->GetInvalidKeyValue(); i=pEdit->GetNextKeyValue( i ) )
  521. {
  522. LPCTSTR pszCurValue = m_kv.GetValue(pEdit->GetKey(i));
  523. if (pszCurValue == NULL)
  524. {
  525. //
  526. // Doesn't exist yet - set current value.
  527. //
  528. m_kv.SetValue(pEdit->GetKey(i), pEdit->GetKeyValue(i));
  529. }
  530. else if (strcmp(pszCurValue, pEdit->GetKeyValue(i)))
  531. {
  532. //
  533. // Already present - we need to merge the value with the existing data.
  534. //
  535. MergeKeyValue(pEdit->GetKey(i));
  536. }
  537. }
  538. }
  539. //-----------------------------------------------------------------------------
  540. // Purpose: Updates the dialog's keyvalue data with a given keyvalue. If the
  541. // data can be merged in with existing data in a meaningful manner,
  542. // that will be done. If not, VALUE_DIFFERENT_STRING will be set to
  543. // indicate that not all objects have the same value for the key.
  544. // Input : pszKey -
  545. //-----------------------------------------------------------------------------
  546. void COP_Entity::MergeKeyValue(char const *pszKey)
  547. {
  548. //VPROF_BUDGET( "COP_Entity::MergeKeyValue", "Object Properties" );
  549. Assert(pszKey);
  550. if (!pszKey)
  551. {
  552. return;
  553. }
  554. bool bHandled = false;
  555. if (m_pEditClass != NULL)
  556. {
  557. GDinputvariable *pVar = m_pEditClass->VarForName(pszKey);
  558. if (pVar != NULL)
  559. {
  560. switch (pVar->GetType())
  561. {
  562. case ivSideList:
  563. {
  564. //
  565. // Merging sidelist keys is a little complicated. We build a string
  566. // representing the merged sidelists.
  567. //
  568. CMapFaceIDList FaceIDListFull;
  569. CMapFaceIDList FaceIDListPartial;
  570. GetFaceIDListsForKey(FaceIDListFull, FaceIDListPartial, pszKey);
  571. char szValue[KEYVALUE_MAX_VALUE_LENGTH];
  572. CMapWorld::FaceID_FaceIDListsToString(szValue, sizeof(szValue), &FaceIDListFull, &FaceIDListPartial);
  573. m_kv.SetValue(pszKey, szValue);
  574. bHandled = true;
  575. break;
  576. }
  577. case ivAngle:
  578. {
  579. //
  580. // We can't merge angles, so set the appropriate angles control to "different".
  581. // We'll catch the main angles control below, since it's supported even
  582. // for objects of an unknown class.
  583. //
  584. if (stricmp(pVar->GetName(), "angles"))
  585. {
  586. m_SmartAngle.SetDifferent(true);
  587. }
  588. break;
  589. }
  590. }
  591. }
  592. }
  593. if (!bHandled)
  594. {
  595. //
  596. // Can't merge with current value - show a "different" string.
  597. //
  598. m_kv.SetValue(pszKey, VALUE_DIFFERENT_STRING);
  599. if (!stricmp(pszKey, "angles"))
  600. {
  601. // We can't merge angles, so set the main angles control to "different".
  602. m_Angle.SetDifferent(true);
  603. }
  604. }
  605. }
  606. //-----------------------------------------------------------------------------
  607. // Purpose:
  608. // Input : Mode -
  609. // pData -
  610. //-----------------------------------------------------------------------------
  611. void COP_Entity::UpdateData( int Mode, PVOID pData, bool bCanEdit )
  612. {
  613. //VPROF_BUDGET( "COP_Entity::UpdateData", "Object Properties" );
  614. //DBG("UpdateData\n");
  615. //DumpKeyvalues(m_kv);
  616. __super::UpdateData( Mode, pData, bCanEdit );
  617. if (!IsWindow(m_hWnd))
  618. {
  619. return;
  620. }
  621. if (GetFocus() == &m_cKey)
  622. {
  623. OnKillfocusKey();
  624. }
  625. if (Mode == LoadFinished)
  626. {
  627. m_kvAdded.RemoveAll();
  628. m_bAllowPresentProperties = true;
  629. PresentProperties();
  630. return;
  631. }
  632. else if ( Mode == LoadData || Mode == LoadFirstData )
  633. {
  634. // Wait until the LoadFinished call to create all the controls,
  635. // otherwise it'll do a lot of unnecessary work.
  636. m_bAllowPresentProperties = false;
  637. }
  638. if (!pData)
  639. {
  640. return;
  641. }
  642. CEditGameClass *pEdit = (CEditGameClass *)pData;
  643. char szBuf[KEYVALUE_MAX_VALUE_LENGTH];
  644. if (Mode == LoadFirstData)
  645. {
  646. LoadClassList();
  647. m_nNewKeyCount = 1;
  648. QAngle vecAngles;
  649. pEdit->GetAngles(vecAngles);
  650. m_Angle.SetAngles(vecAngles, false);
  651. m_Angle.SetDifferent(false);
  652. #ifdef NAMES
  653. if(pEdit->pClass)
  654. {
  655. sprintf(szBuf, "%s (%s)", pEdit->pClass->GetDescription(), pEdit->pClass->GetName());
  656. }
  657. else
  658. #endif
  659. V_strcpy_safe( szBuf, pEdit->GetClassName() );
  660. m_cClasses.AddSuggestion( szBuf ); // If we don't make sure it has this item in its list, it will do
  661. // Bad Things later on. This only happens when the FGD is missing an
  662. // entity that is in the map file. In that case, just let it be.
  663. // Check For Problems should ID the problem for them.
  664. m_cClasses.SelectItem(szBuf);
  665. m_bClassSelectionEmpty = false;
  666. //
  667. // Can't change the class of the worldspawn entity.
  668. //
  669. if ( pEdit->IsClass("worldspawn") || m_bCanEdit == false )
  670. {
  671. m_cClasses.EnableWindow(FALSE);
  672. }
  673. else
  674. {
  675. m_cClasses.EnableWindow(TRUE);
  676. }
  677. //
  678. // Get the comments text from the entity.
  679. //
  680. m_Comments.SetWindowText(pEdit->GetComments());
  681. //
  682. // Add entity's keys to our local storage
  683. //
  684. m_kv.RemoveAll();
  685. for ( int i=pEdit->GetFirstKeyValue(); i != pEdit->GetInvalidKeyValue(); i=pEdit->GetNextKeyValue( i ) )
  686. {
  687. const char *pszKey = pEdit->GetKey(i);
  688. const char *pszValue = pEdit->GetKeyValue(i);
  689. if ((pszKey != NULL) && (pszValue != NULL))
  690. {
  691. m_kv.SetValue(pszKey, pszValue);
  692. }
  693. }
  694. UpdateEditClass(pEdit->GetClassName(), true);
  695. UpdateDisplayClass(pEdit->GetClassName());
  696. SetCurKey(m_strLastKey);
  697. }
  698. else if (Mode == LoadData)
  699. {
  700. //
  701. // Not first data - merge with current stuff.
  702. //
  703. //
  704. // Deal with class name.
  705. //
  706. CString str = m_cClasses.GetCurrentItem();
  707. if ( m_bClassSelectionEmpty )
  708. str = "";
  709. if (strcmpi(str, pEdit->GetClassName()))
  710. {
  711. //
  712. // Not the same - set class to be blank and
  713. // disable smartedit.
  714. //
  715. m_cClasses.ForceEditControlText( "" );
  716. m_bClassSelectionEmpty = true;
  717. UpdateEditClass("", false);
  718. UpdateDisplayClass("");
  719. }
  720. else
  721. {
  722. LoadClassList();
  723. m_cClasses.SelectItem( pEdit->GetClassName() );
  724. }
  725. //
  726. // Mark the comments field as "(different)" if it isn't the same as this entity's
  727. // comments.
  728. //
  729. char szComments[1024];
  730. m_Comments.GetWindowText(szComments, sizeof(szComments));
  731. if (strcmp(szComments, pEdit->GetComments()) != 0)
  732. {
  733. m_Comments.SetWindowText(VALUE_DIFFERENT_STRING);
  734. }
  735. MergeObjectKeyValues(pEdit);
  736. SetCurKey(m_strLastKey);
  737. }
  738. else
  739. {
  740. Assert(FALSE);
  741. }
  742. }
  743. //-----------------------------------------------------------------------------
  744. // Purpose: Stops entity, face, or angles picking.
  745. //-----------------------------------------------------------------------------
  746. void COP_Entity::StopPicking(void)
  747. {
  748. if (m_bPicking)
  749. {
  750. m_bPicking = false;
  751. ToolManager()->SetTool(m_ToolPrePick);
  752. //
  753. // Stop angles picking if we are doing so.
  754. //
  755. CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_ANGLES);
  756. if (pButton)
  757. {
  758. pButton->SetCheck(0);
  759. }
  760. //
  761. // Stop entity picking if we are doing so.
  762. //
  763. pButton = (CButton *)GetDlgItem(IDC_PICK_ENTITY);
  764. if (pButton)
  765. {
  766. pButton->SetCheck(0);
  767. }
  768. //
  769. // Stop face picking if we are doing so.
  770. //
  771. pButton = (CButton *)GetDlgItem(IDC_PICK_FACES);
  772. if (pButton)
  773. {
  774. pButton->SetCheck(0);
  775. }
  776. }
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Purpose: Called manually from CObjectProperties::OnApply because the Apply
  780. // button is implemented in a nonstandard way. I'm not sure why.
  781. //-----------------------------------------------------------------------------
  782. BOOL COP_Entity::OnApply(void)
  783. {
  784. m_pLastSmartControlVar = NULL; // Force it to recreate the target name combo if need be,
  785. // because we might have locked in a new targetname.
  786. StopPicking();
  787. return(TRUE);
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Purpose:
  791. // Input : pEntity -
  792. // pszKey -
  793. // pszValue -
  794. //-----------------------------------------------------------------------------
  795. void COP_Entity::ApplyKeyValueToObject(CEditGameClass *pObject, const char *pszKey, const char *pszValue)
  796. {
  797. //VPROF_BUDGET( "COP_Entity::ApplyKeyValueToObject", "Object Properties" );
  798. GDclass *pClass = pObject->GetClass();
  799. if (pClass != NULL)
  800. {
  801. GDinputvariable *pVar = pClass->VarForName(pszKey);
  802. if (pVar != NULL)
  803. {
  804. if ((pVar->GetType() == ivSideList) || (pVar->GetType() == ivSide))
  805. {
  806. CMapWorld *pWorld = GetActiveWorld();
  807. //
  808. // Get the face list currently set in this keyvalue.
  809. //
  810. CMapFaceIDList CurFaceList;
  811. const char *pszCurVal = pObject->GetKeyValue(pszKey);
  812. if (pszCurVal != NULL)
  813. {
  814. pWorld->FaceID_StringToFaceIDLists(&CurFaceList, NULL, pszCurVal);
  815. }
  816. //
  817. // Build the face list to apply. Only include the faces that are:
  818. //
  819. // 1. In the full selection list (outside the parentheses).
  820. // 2. In the partial selection set (inside the parentheses) AND are in the
  821. // original face list.
  822. //
  823. // FACEID TODO: Optimize so that we only call StringToFaceList once per keyvalue
  824. // instead of once per keyvalue per entity being applied to.
  825. CMapFaceIDList FullFaceList;
  826. CMapFaceIDList PartialFaceList;
  827. pWorld->FaceID_StringToFaceIDLists(&FullFaceList, &PartialFaceList, pszValue);
  828. CMapFaceIDList KeepFaceList;
  829. for (int i = 0; i < PartialFaceList.Count(); i++)
  830. {
  831. int nFace = PartialFaceList.Element(i);
  832. if (CurFaceList.Find(nFace) != -1)
  833. {
  834. KeepFaceList.AddToTail(nFace);
  835. }
  836. }
  837. FullFaceList.AddVectorToTail(KeepFaceList);
  838. char szSetValue[KEYVALUE_MAX_VALUE_LENGTH];
  839. CMapWorld::FaceID_FaceIDListsToString(szSetValue, sizeof(szSetValue), &FullFaceList, NULL);
  840. pObject->SetKeyValue(pszKey, szSetValue);
  841. return;
  842. }
  843. }
  844. }
  845. pObject->SetKeyValue(pszKey, pszValue);
  846. }
  847. //-----------------------------------------------------------------------------
  848. // Purpose: Called by the sheet to let the page remember its state before a
  849. // refresh of the data.
  850. //-----------------------------------------------------------------------------
  851. void COP_Entity::RememberState(void)
  852. {
  853. GetCurKey(m_strLastKey);
  854. }
  855. //-----------------------------------------------------------------------------
  856. // Purpose: Called by the object properties page to tell us that all our data
  857. // is dirty. We don't have to do anything on most of our data because it'll
  858. // get regenerated, but we must clear out our class pointers because we'll
  859. // do crazy things if they give us a new class and have us load the data
  860. // (we'll think it just changed from one class to another and we'll wipe
  861. // out spawnflags, for instance). So clear out the class pointers.
  862. //-----------------------------------------------------------------------------
  863. void COP_Entity::MarkDataDirty()
  864. {
  865. UpdateDisplayClass( (GDclass*)NULL );
  866. m_pEditClass = NULL;
  867. m_VarList.DeleteAllItems();
  868. m_InstanceParmData.RemoveAll();
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Purpose: Saves the dialog data into the objects being edited.
  872. // Output : Returns true on success, false on failure.
  873. //-----------------------------------------------------------------------------
  874. bool COP_Entity::SaveData(void)
  875. {
  876. //VPROF_BUDGET( "COP_Entity::SaveData", "Object Properties" );
  877. //DBG("SaveData\n");
  878. //DumpKeyvalues(m_kv);
  879. RememberState();
  880. CString strClassName = m_cClasses.GetCurrentItem();
  881. // If we were multiselecting entities and they haven't chosen a new classname yet,
  882. // we need to know that here so we don't force them all to be the last selection in the classname combo.
  883. if ( m_bClassSelectionEmpty )
  884. strClassName = "";
  885. UpdateEditClass(strClassName, false);
  886. //
  887. // Apply the dialog data to all the objects being edited.
  888. //
  889. FOR_EACH_OBJ( *m_pObjectList, pos )
  890. {
  891. CMapClass *pObject = m_pObjectList->Element(pos);
  892. CEditGameClass *pEdit = dynamic_cast <CEditGameClass *>(pObject);
  893. Assert(pEdit != NULL);
  894. if (pEdit != NULL)
  895. {
  896. RemoveBlankKeys();
  897. //
  898. // Give keys back to object. For every key in our local storage that is
  899. // also found in the list of added keys, set the key value in the edit
  900. // object(s).
  901. //
  902. for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) )
  903. {
  904. MDkeyvalue &kvCur = m_kv.GetKeyValue(i);
  905. const char *pszAddedKeyValue = m_kvAdded.GetValue(kvCur.szKey);
  906. if (pszAddedKeyValue != NULL)
  907. {
  908. Q_FixSlashes( kvCur.szValue, '/' );
  909. //
  910. // Don't store keys with multiple/undefined values.
  911. //
  912. if (strcmp(kvCur.szValue, VALUE_DIFFERENT_STRING))
  913. {
  914. //DBG(" apply key %s\n", kvCur.szKey);
  915. ApplyKeyValueToObject(pEdit, kvCur.szKey, kvCur.szValue);
  916. }
  917. }
  918. }
  919. //
  920. // All keys in the object should also be found in local storage,
  921. // unless the user deleted them. If there are any such extraneous
  922. // keys, get rid of them. Go from the top down because otherwise
  923. // deleting messes up our iteration.
  924. //
  925. int iNext;
  926. for ( int i=pEdit->GetFirstKeyValue(); i != pEdit->GetInvalidKeyValue(); i=iNext )
  927. {
  928. iNext = pEdit->GetNextKeyValue( i );
  929. //
  930. // If this key is in not in our local storage, delete it from the object.
  931. //
  932. if (!m_kv.GetValue(pEdit->GetKey(i)))
  933. {
  934. //DBG(" delete key %s\n", pEdit->GetKey(i));
  935. pEdit->DeleteKeyValue(pEdit->GetKey(i));
  936. }
  937. }
  938. //
  939. // Store class.
  940. //
  941. if (strClassName[0])
  942. {
  943. pEdit->SetClass(strClassName);
  944. }
  945. //
  946. // Store the entity comments.
  947. //
  948. char szComments[1024];
  949. szComments[0] = '\0';
  950. m_Comments.GetWindowText(szComments, sizeof(szComments));
  951. if (strcmp(szComments, VALUE_DIFFERENT_STRING) != 0)
  952. {
  953. pEdit->SetComments(szComments);
  954. }
  955. }
  956. pObject->PostUpdate(Notify_Changed);
  957. }
  958. UpdateDisplayClass(strClassName);
  959. return(true);
  960. }
  961. //-----------------------------------------------------------------------------
  962. // Purpose: Given the short name of a key, find which row it's at in the list control.
  963. //-----------------------------------------------------------------------------
  964. int COP_Entity::GetKeyValueRowByShortName( const char *pShortName )
  965. {
  966. const char *pSearchString = pShortName;
  967. if ( m_bSmartedit )
  968. {
  969. if ( m_pDisplayClass )
  970. {
  971. GDinputvariable *pVar = m_pDisplayClass->VarForName( pShortName );
  972. if (pVar)
  973. pSearchString = pVar->GetLongName();
  974. }
  975. }
  976. LVFINDINFO fi;
  977. memset( &fi, 0, sizeof( fi ) );
  978. fi.flags = LVFI_STRING;
  979. fi.psz = pSearchString;
  980. return m_VarList.FindItem( &fi );
  981. }
  982. //-----------------------------------------------------------------------------
  983. // Purpose: Fills the values in the second column for all properties.
  984. //-----------------------------------------------------------------------------
  985. void COP_Entity::RefreshKVListValues( const char *pOnlyThisVar )
  986. {
  987. // We match listctrl elements to their values in 2 different ways:
  988. // 1. In smartedit mode, the raw name of the property in the first column is matched to m_kv.
  989. // 2. In non-smartedit mode, we look at the lParam entry in the listctrl.
  990. for ( int i=0; i < m_VarList.GetItemCount(); i++ )
  991. {
  992. const char *pVarName = (const char*)m_VarList.GetItemData( i );
  993. const char *pValue = NULL;
  994. char tmpValueBuf[512];
  995. // If they only wanted to update one var...
  996. if ( pOnlyThisVar && Q_stricmp( pVarName, pOnlyThisVar ) != 0 )
  997. continue;
  998. if ( m_bSmartedit )
  999. {
  1000. GDinputvariable *pVar = (m_pDisplayClass ? m_pDisplayClass->VarForName( pVarName ) : NULL);
  1001. if ( pVar )
  1002. {
  1003. const char *pUnformattedValue = m_kv.GetValue( pVar->GetName() );
  1004. pValue = pUnformattedValue; // Default is unformatted.
  1005. if ( pUnformattedValue )
  1006. {
  1007. // Do special formatting for various value types.
  1008. GDIV_TYPE eType = pVar->GetType();
  1009. if ( eType == ivChoices )
  1010. {
  1011. if ( pUnformattedValue )
  1012. {
  1013. const char *pTestValue = pVar->ItemStringForValue( pUnformattedValue );
  1014. if ( pTestValue )
  1015. pValue = pTestValue;
  1016. }
  1017. }
  1018. else if (
  1019. (eType == ivStudioModel) || (eType == ivSprite) || (eType == ivSound) || (eType == ivDecal) ||
  1020. (eType == ivMaterial) || (eType == ivScene) )
  1021. {
  1022. // It's a filename.. just show the filename and not the directory. They can look at the
  1023. // full filename in the smart control if they want.
  1024. const char *pLastSlash = max( strrchr( pUnformattedValue, '\\' ), strrchr( pUnformattedValue, '/' ) );
  1025. if ( pLastSlash )
  1026. {
  1027. Q_strncpy( tmpValueBuf, pLastSlash+1, sizeof( tmpValueBuf ) );
  1028. pValue = tmpValueBuf;
  1029. }
  1030. }
  1031. }
  1032. }
  1033. else
  1034. {
  1035. pValue = m_kv.GetValue( pVarName ); // This was probably added in dumbedit mode.
  1036. }
  1037. }
  1038. else
  1039. {
  1040. pValue = m_kv.GetValue( pVarName );
  1041. }
  1042. if ( pValue )
  1043. m_VarList.SetItemText( i, 1, pValue );
  1044. else
  1045. m_VarList.SetItemText( i, 1, "" );
  1046. }
  1047. }
  1048. //-----------------------------------------------------------------------------
  1049. // Purpose: Sets up the current mode (smartedit/non) after loading an object
  1050. // or toggling the SmartEdit button.
  1051. //-----------------------------------------------------------------------------
  1052. void COP_Entity::PresentProperties()
  1053. {
  1054. if ( !m_bAllowPresentProperties )
  1055. return;
  1056. ++m_nPresentPropertiesCalls;
  1057. m_VarList.SetRedraw( FALSE );
  1058. ClearVarList();
  1059. if (!m_bSmartedit || !m_pDisplayClass)
  1060. {
  1061. RemoveBlankKeys();
  1062. for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) )
  1063. {
  1064. MDkeyvalue &KeyValue = m_kv.GetKeyValue(i);
  1065. int iItem = m_VarList.InsertItem( i, KeyValue.szKey );
  1066. m_VarList.SetItemData( iItem, (DWORD)KeyValue.szKey );
  1067. }
  1068. m_Angle.Enable( m_bCanEdit );
  1069. }
  1070. else
  1071. {
  1072. // Too many entity variables! Increase GD_MAX_VARIABLES if you get this assertion.
  1073. Assert(m_pDisplayClass->GetVariableCount() <= GD_MAX_VARIABLES);
  1074. for ( int i=0; i < ARRAYSIZE(m_VarMap); i++ )
  1075. {
  1076. m_VarMap[i] = -1;
  1077. }
  1078. //
  1079. // Add all the keys from the entity's class to the listbox. If the key is not already
  1080. // in the entity, add it to the m_kvAdded list as well.
  1081. //
  1082. for (int i = 0; i < m_pDisplayClass->GetVariableCount(); i++)
  1083. {
  1084. GDinputvariable *pVar = m_pDisplayClass->GetVariableAt(i);
  1085. //
  1086. // Spawnflags are handled separately - don't add that key.
  1087. //
  1088. if (strcmpi(pVar->GetName(), SPAWNFLAGS_KEYNAME) != 0)
  1089. {
  1090. int iItem = m_VarList.InsertItem( i, pVar->GetLongName() );
  1091. m_VarList.SetItemData( iItem, (DWORD)pVar->GetName() );
  1092. }
  1093. }
  1094. if ( m_pObjectList->Count() == 1 )
  1095. {
  1096. CMapEntity *pEntity = static_cast< CMapEntity * >( m_pObjectList->Element( 0 ) );
  1097. CMapInstance *pMapInstance = pEntity->GetChildOfType( ( CMapInstance * )NULL );
  1098. if ( pMapInstance && pMapInstance->GetInstancedMap() )
  1099. {
  1100. CMapEntityList entityList;
  1101. pMapInstance->GetInstancedMap()->FindEntitiesByClassName( entityList, "func_instance_parms", false );
  1102. if ( entityList.Count() == 1 )
  1103. {
  1104. CMapEntity *pInstanceParmsEntity = entityList.Element( 0 );
  1105. for ( int i = pInstanceParmsEntity->GetFirstKeyValue(); i != pInstanceParmsEntity->GetInvalidKeyValue(); i = pInstanceParmsEntity->GetNextKeyValue( i ) )
  1106. {
  1107. LPCTSTR pInstanceKey = pInstanceParmsEntity->GetKey( i );
  1108. LPCTSTR pInstanceValue = pInstanceParmsEntity->GetKeyValue( i );
  1109. if ( strnicmp( pInstanceKey, "parm", strlen( "parm" ) ) == 0 )
  1110. {
  1111. char ValueData[ KEYVALUE_MAX_KEY_LENGTH ];
  1112. const char *pVariable, *pReplace;
  1113. pVariable = pReplace = "";
  1114. if ( pInstanceValue )
  1115. {
  1116. strcpy( ValueData, pInstanceValue );
  1117. pVariable = ValueData;
  1118. char *pos = strchr( ValueData, ' ' );
  1119. if ( pos )
  1120. {
  1121. *pos = 0;
  1122. pos++;
  1123. pReplace = pos;
  1124. }
  1125. }
  1126. else
  1127. {
  1128. continue;
  1129. }
  1130. for ( int j = pEntity->GetFirstKeyValue(); j != pEntity->GetInvalidKeyValue(); j = pEntity->GetNextKeyValue( j ) )
  1131. {
  1132. LPCTSTR pKey = pEntity->GetKey( j );
  1133. LPCTSTR pValue = pEntity->GetKeyValue( j );
  1134. if ( strnicmp( pValue, pVariable, strlen( pVariable ) ) == 0 )
  1135. {
  1136. CInstanceParmData InstanceParmData;
  1137. InstanceParmData.m_ParmVariable = new GDinputvariable( pReplace, pVariable );
  1138. InstanceParmData.m_ParmKey = pKey;
  1139. InstanceParmData.m_VariableName = pVariable;
  1140. int InsertIndex = m_InstanceParmData.Insert( InstanceParmData.m_VariableName, InstanceParmData );
  1141. const char *ptr = m_InstanceParmData[ InsertIndex ].m_VariableName;
  1142. int iItem = m_VarList.InsertItem( 0, ptr );
  1143. m_VarList.SetItemData( iItem, (DWORD)ptr );
  1144. m_kv.SetValue( pVariable, pValue + strlen( pVariable ) + 1 );
  1145. }
  1146. }
  1147. }
  1148. }
  1149. }
  1150. }
  1151. }
  1152. // 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
  1153. // Also add any keyvalues they added in dumbedit mode. These will show up in red.
  1154. for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) )
  1155. {
  1156. MDkeyvalue &KeyValue = m_kv.GetKeyValue(i);
  1157. if ( !m_pDisplayClass->VarForName( KeyValue.szKey ) && m_InstanceParmData.Find( KeyValue.szKey ) == m_InstanceParmData.InvalidIndex() )
  1158. {
  1159. int iItem = m_VarList.InsertItem( i, KeyValue.szKey );
  1160. m_VarList.SetItemData( iItem, (DWORD)KeyValue.szKey );
  1161. }
  1162. }
  1163. //
  1164. // If this class defines angles, enable the angles control.
  1165. //
  1166. if (m_pDisplayClass->VarForName("angles") != NULL)
  1167. {
  1168. m_Angle.Enable( m_bCanEdit );
  1169. }
  1170. else
  1171. {
  1172. m_Angle.Enable(false);
  1173. }
  1174. }
  1175. RefreshKVListValues();
  1176. ResortItems();
  1177. SetCurKey(m_strLastKey);
  1178. m_VarList.SetRedraw( TRUE );
  1179. m_VarList.Invalidate( FALSE );
  1180. }
  1181. void COP_Entity::ClearVarList()
  1182. {
  1183. m_VarList.DeleteAllItems();
  1184. m_InstanceParmData.RemoveAll();
  1185. for ( int i=0; i < ARRAYSIZE( m_VarMap ); i++ )
  1186. m_VarMap[i] = -1;
  1187. }
  1188. //-----------------------------------------------------------------------------
  1189. // Purpose: Removes any keys with blank values from our local keyvalue storage.
  1190. //-----------------------------------------------------------------------------
  1191. void COP_Entity::RemoveBlankKeys(void)
  1192. {
  1193. //VPROF_BUDGET( "COP_Entity::RemoveBlankKeys", "Object Properties" );
  1194. int iNext;
  1195. for (int i = m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=iNext)
  1196. {
  1197. iNext = m_kv.GetNext( i );
  1198. MDkeyvalue &KeyValue = m_kv.GetKeyValue(i);
  1199. if (KeyValue.szValue[0] == '\0')
  1200. {
  1201. bool bRemove = true;
  1202. #if 0
  1203. // Only remove keys that are blank and whose default value is not blank,
  1204. // because Hammer assigns any missing key with the FGD's default value.
  1205. //
  1206. // dvs: disabled for now because deleting the value text is the currently
  1207. // accepted way of reverting a key to its default value.
  1208. GDinputvariable *pVar = m_pDisplayClass->VarForName( KeyValue.szKey );
  1209. if ( pVar )
  1210. {
  1211. char szDefault[MAX_KEYVALUE_LEN];
  1212. pVar->GetDefault( szDefault );
  1213. if ( szDefault[0] != '\0' )
  1214. {
  1215. bRemove = false;
  1216. }
  1217. }
  1218. #endif
  1219. if ( bRemove )
  1220. {
  1221. m_kv.RemoveKeyAt(i);
  1222. }
  1223. }
  1224. }
  1225. }
  1226. //-----------------------------------------------------------------------------
  1227. // Purpose:
  1228. //-----------------------------------------------------------------------------
  1229. void COP_Entity::LoadClassList(void)
  1230. {
  1231. CEditGameClass *pEdit = (CEditGameClass*) GetEditObject();
  1232. const char *pWorldSpawnString = "worldspawn";
  1233. int iSolidClasses = -1;
  1234. if (pEdit->IsClass())
  1235. {
  1236. if ( pEdit->IsClass( pWorldSpawnString ) )
  1237. {
  1238. iSolidClasses = 2;
  1239. }
  1240. else if (pEdit->IsSolidClass())
  1241. {
  1242. iSolidClasses = 1;
  1243. }
  1244. else
  1245. {
  1246. iSolidClasses = 0;
  1247. }
  1248. }
  1249. // Ok, we've already initialized the list with the same list. Don't do it over again.
  1250. if ( m_iLastClassListSolidClasses == iSolidClasses )
  1251. return;
  1252. CUtlVector<CString> suggestions;
  1253. if ( iSolidClasses == 2 )
  1254. {
  1255. suggestions.AddToTail( pWorldSpawnString );
  1256. }
  1257. else
  1258. {
  1259. int nCount = pGD->GetClassCount();
  1260. CString str;
  1261. for (int i =0; i < nCount; i++)
  1262. {
  1263. GDclass *pc = pGD->GetClass(i);
  1264. if (!pc->IsBaseClass())
  1265. {
  1266. if (iSolidClasses == -1 || (iSolidClasses == (int)pc->IsSolidClass()))
  1267. {
  1268. #ifdef NAMES
  1269. str.Format("%s (%s)", pc->GetDescription(), pc->GetName());
  1270. #else
  1271. str = pc->GetName();
  1272. #endif
  1273. if (!pc->IsClass( pWorldSpawnString ))
  1274. {
  1275. suggestions.AddToTail( str );
  1276. }
  1277. }
  1278. }
  1279. }
  1280. }
  1281. m_iLastClassListSolidClasses = iSolidClasses;
  1282. m_cClasses.SetSuggestions( suggestions, 0 );
  1283. // Add this class' class name in case it's not in the list yet.
  1284. m_cClasses.AddSuggestion( pEdit->GetClassNameA() );
  1285. }
  1286. //-----------------------------------------------------------------------------
  1287. // Purpose:
  1288. //-----------------------------------------------------------------------------
  1289. BOOL COP_Entity::OnInitDialog(void)
  1290. {
  1291. //VPROF_BUDGET( "COP_Entity::OnInitDialog", "Object Properties" );
  1292. CObjectPage::OnInitDialog();
  1293. // Sometimes it has deleted our window (and its children) from underneath us,
  1294. // and we don't want to hang onto old window pointers.
  1295. m_SmartControls.Purge();
  1296. m_pDisplayClass = NULL;
  1297. m_pEditClass = NULL;
  1298. m_pLastSmartControlVar = NULL;
  1299. // Clear m_kv.
  1300. m_kv.RemoveAll();
  1301. ClearVarList();
  1302. // Hook up the main angles controls.
  1303. m_Angle.SubclassDlgItem(IDC_ANGLEBOX, this);
  1304. m_AngleEdit.SubclassDlgItem(IDC_ANGLEEDIT, this);
  1305. m_Angle.SetEditControl(&m_AngleEdit);
  1306. m_AngleEdit.SetAngleBox(&m_Angle);
  1307. // Hook up the smart angles controls.
  1308. m_SmartAngle.SubclassDlgItem(IDC_SMART_ANGLEBOX, this);
  1309. m_SmartAngleEdit.SubclassDlgItem(IDC_SMART_ANGLEEDIT, this);
  1310. m_SmartAngle.SetEditControl(&m_SmartAngleEdit);
  1311. m_SmartAngleEdit.SetAngleBox(&m_SmartAngle);
  1312. // Hook up the classes autoselect combo.
  1313. m_cClasses.SubclassDlgItem(IDC_CLASS, this);
  1314. // Hook up the pick color button.
  1315. m_cPickColor.SubclassDlgItem(IDC_PICKCOLOR, this);
  1316. LoadClassList();
  1317. //m_VarList.SetTabStops(65);
  1318. // Put our varlist in the right mode.
  1319. DWORD dwStyle = GetWindowLong( m_VarList.GetSafeHwnd(), GWL_STYLE );
  1320. dwStyle &= ~(LVS_ICON | LVS_LIST | LVS_SMALLICON);
  1321. dwStyle |= LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_OWNERDRAWFIXED;
  1322. SetWindowLong( m_VarList.GetSafeHwnd(), GWL_STYLE, dwStyle );
  1323. m_VarList.SetExtendedStyle( m_VarList.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES );
  1324. m_VarList.InsertColumn(0, "Property Name", LVCFMT_LEFT, 200);
  1325. m_VarList.InsertColumn(1, "Value", LVCFMT_LEFT, 150);
  1326. m_bWantSmartedit = true;
  1327. SetSmartedit(false);
  1328. UpdateAnchors();
  1329. return TRUE;
  1330. }
  1331. void COP_Entity::UpdateAnchors()
  1332. {
  1333. CAnchorDef anchorDefs[] =
  1334. {
  1335. CAnchorDef( IDC_KEYVALUES, k_eSimpleAnchorAllSides ),
  1336. CAnchorDef( IDC_SMARTEDIT, k_eSimpleAnchorRightSide ),
  1337. CAnchorDef( IDC_ENTITY_HELP, k_eSimpleAnchorRightSide ),
  1338. CAnchorDef( IDC_ANGLES_LABEL, k_eSimpleAnchorRightSide ),
  1339. CAnchorDef( IDC_ANGLEEDIT, k_eSimpleAnchorRightSide ),
  1340. CAnchorDef( IDC_ANGLEBOX, k_eSimpleAnchorRightSide ),
  1341. CAnchorDef( IDC_KEY_LABEL, k_eSimpleAnchorRightSide ),
  1342. CAnchorDef( IDC_KEY, k_eSimpleAnchorRightSide ),
  1343. CAnchorDef( IDC_VALUE_LABEL, k_eSimpleAnchorRightSide ),
  1344. CAnchorDef( IDC_PICKCOLOR, k_eSimpleAnchorRightSide ),
  1345. CAnchorDef( IDC_SMART_ANGLEEDIT, k_eSimpleAnchorRightSide ),
  1346. CAnchorDef( IDC_SMART_ANGLEBOX, k_eSimpleAnchorRightSide ),
  1347. CAnchorDef( IDC_VALUE, k_eSimpleAnchorRightSide ),
  1348. CAnchorDef( IDC_KEYVALUE_HELP_GROUP, k_eSimpleAnchorRightSide ),
  1349. CAnchorDef( IDC_KEYVALUE_HELP, k_eAnchorRight, k_eAnchorTop, k_eAnchorRight, k_eAnchorBottom ),
  1350. CAnchorDef( IDC_COMMENTS_LABEL, k_eSimpleAnchorBottomRight ),
  1351. CAnchorDef( IDC_ENTITY_COMMENTS, k_eAnchorRight, k_eAnchorBottom, k_eAnchorRight, k_eAnchorBottom ),
  1352. CAnchorDef( IDC_ADDKEYVALUE, k_eSimpleAnchorRightSide ),
  1353. CAnchorDef( IDC_REMOVEKEYVALUE, k_eSimpleAnchorRightSide )
  1354. };
  1355. CUtlVector<CAnchorDef> defs;
  1356. defs.CopyArray( anchorDefs, ARRAYSIZE( anchorDefs ) );
  1357. for ( int i=0; i < m_SmartControls.Count(); i++ )
  1358. {
  1359. defs.AddToTail( CAnchorDef( m_SmartControls[i]->GetSafeHwnd(), k_eSimpleAnchorRightSide ) );
  1360. }
  1361. m_AnchorMgr.Init( GetSafeHwnd(), defs.Base(), defs.Count() );
  1362. }
  1363. int COP_Entity::GetCurVarListSelection()
  1364. {
  1365. POSITION pos = m_VarList.GetFirstSelectedItemPosition();
  1366. if ( pos )
  1367. {
  1368. return m_VarList.GetNextSelectedItem( pos );
  1369. }
  1370. else
  1371. {
  1372. return LB_ERR;
  1373. }
  1374. }
  1375. void COP_Entity::OnItemChangedKeyValues(NMHDR* pNMHDR, LRESULT* pResult)
  1376. {
  1377. OnSelchangeKeyvalues();
  1378. }
  1379. void COP_Entity::OnDblClickKeyValues(NMHDR* pNMHDR, LRESULT* pResult)
  1380. {
  1381. // Do smart stuff if we're in SmartEdit mode.
  1382. if ( !m_bSmartedit )
  1383. return;
  1384. int iSel = GetCurVarListSelection();
  1385. if ( iSel == LB_ERR )
  1386. return;
  1387. GDinputvariable *pVar = GetVariableAt( iSel );
  1388. if ( pVar == NULL )
  1389. {
  1390. return;
  1391. }
  1392. if ( pVar->GetType() == ivColor255 || pVar->GetType() == ivColor1 )
  1393. {
  1394. OnPickColor();
  1395. }
  1396. else if ( m_pSmartControl )
  1397. {
  1398. if ( m_pSmartBrowseButton )
  1399. {
  1400. OnBrowse();
  1401. }
  1402. else if ( dynamic_cast< CMyEdit* >( m_pSmartControl ) )
  1403. {
  1404. m_pSmartControl->SetFocus();
  1405. m_pSmartControl->SendMessage( EM_SETSEL, 0, -1 );
  1406. }
  1407. else if ( dynamic_cast< CMyComboBox* >( m_pSmartControl ) || dynamic_cast< CTargetNameComboBox* >( m_pSmartControl ) )
  1408. {
  1409. m_pSmartControl->SetFocus();
  1410. }
  1411. }
  1412. }
  1413. void COP_Entity::SetCurVarListSelection( int iSel )
  1414. {
  1415. // Deselect everything.
  1416. POSITION pos = m_VarList.GetFirstSelectedItemPosition();
  1417. while ( pos )
  1418. {
  1419. int iItem = m_VarList.GetNextSelectedItem( pos );
  1420. m_VarList.SetItemState( iItem, 0, LVIS_SELECTED );
  1421. }
  1422. // Now set selection on the item we want.
  1423. m_VarList.SetItemState( iSel, LVIS_SELECTED, LVIS_SELECTED );
  1424. }
  1425. //-----------------------------------------------------------------------------
  1426. // Gets the name of the currently selected key in the properties list.
  1427. //-----------------------------------------------------------------------------
  1428. void COP_Entity::GetCurKey(CString &strKey)
  1429. {
  1430. //VPROF_BUDGET( "COP_Entity::GetCurKey", "Object Properties" );
  1431. int iSel = GetCurVarListSelection();
  1432. if (iSel == -1)
  1433. {
  1434. strKey = "";
  1435. return;
  1436. }
  1437. strKey = (CString)(const char*)m_VarList.GetItemData( iSel );
  1438. }
  1439. //-----------------------------------------------------------------------------
  1440. // Purpose: Selects the given key in the list of keys. If the key is not in the
  1441. // list, the first key is selected.
  1442. // Input : pszKey - Name of key to select.
  1443. //-----------------------------------------------------------------------------
  1444. void COP_Entity::SetCurKey(LPCTSTR pszKey)
  1445. {
  1446. //VPROF_BUDGET( "COP_Entity::SetCurKey", "Object Properties" );
  1447. int nSel = m_VarList.GetItemCount();
  1448. for (int i = 0; i < nSel; i++)
  1449. {
  1450. CString str = (CString)(const char*)m_VarList.GetItemData( i );
  1451. if ( !Q_stricmp( str, pszKey ) )
  1452. {
  1453. // found it here -
  1454. SetCurVarListSelection( i );
  1455. // FIXME: Ideally we'd only call OnSelChangeKeyvalues if the selection index
  1456. // actually changed, but that makes the keyvalue text not refresh properly
  1457. // when multiselecting entities with a sidelist key selected.
  1458. if ( m_bSmartedit )
  1459. {
  1460. OnSelchangeKeyvalues();
  1461. }
  1462. return;
  1463. }
  1464. }
  1465. //
  1466. // Not found, select the first key in the list.
  1467. //
  1468. SetCurVarListSelection( 0 );
  1469. OnSelchangeKeyvalues();
  1470. }
  1471. //-----------------------------------------------------------------------------
  1472. //-----------------------------------------------------------------------------
  1473. void COP_Entity::DestroySmartControls(void)
  1474. {
  1475. //VPROF_BUDGET( "COP_Entity::DestroySmartControls", "Object Properties" );
  1476. for (int i = 0; i < m_SmartControls.Count(); i++)
  1477. {
  1478. CWnd *pControl = m_SmartControls.Element(i);
  1479. if (pControl != NULL)
  1480. {
  1481. pControl->DestroyWindow();
  1482. delete pControl;
  1483. }
  1484. }
  1485. m_SmartControls.RemoveAll();
  1486. m_pSmartBrowseButton = NULL;
  1487. m_pSmartControl = NULL;
  1488. m_pLastSmartControlVar = NULL;
  1489. m_pEditInstanceVariable = NULL;
  1490. m_pEditInstanceValue = NULL;
  1491. m_pComboInstanceParmType = NULL;
  1492. UpdateAnchors();
  1493. }
  1494. //-----------------------------------------------------------------------------
  1495. // Purpose: Creates the smart controls based on the given type. Deletes any
  1496. // smart controls that are not appropriate to the given type.
  1497. // Input : eType - Type of keyvalue for which to create controls.
  1498. //-----------------------------------------------------------------------------
  1499. void COP_Entity::CreateSmartControls(GDinputvariable *pVar, CUtlVector<const char *>*pHelperType)
  1500. {
  1501. //VPROF_BUDGET( "COP_Entity::CreateSmartControls", "Object Properties" );
  1502. // dvs: TODO: break this monster up into smaller functions
  1503. if (pVar == NULL)
  1504. {
  1505. // UNDONE: delete smart controls?
  1506. return;
  1507. }
  1508. CString strValue = m_kv.GetValue( pVar->GetName() );
  1509. // If this is the same var that our smart controls are already setup for, then don't do anything.
  1510. if ( m_SmartControls.Count() > 0 &&
  1511. pVar == m_pLastSmartControlVar &&
  1512. Q_stricmp( strValue, m_LastSmartControlVarValue ) == 0 )
  1513. {
  1514. return;
  1515. }
  1516. //
  1517. // Set this so that we don't process notifications when we set the edit text,
  1518. // which makes us do things like assume the user changed a key when they didn't.
  1519. //
  1520. m_bIgnoreKVChange = true;
  1521. DestroySmartControls();
  1522. m_pLastSmartControlVar = pVar;
  1523. m_LastSmartControlVarValue = strValue;
  1524. //
  1525. // Build a rectangle in which to create a new control.
  1526. //
  1527. CRect ctrlrect = CalculateSmartControlRect();
  1528. GDIV_TYPE eType = pVar->GetType();
  1529. //
  1530. // Hide or show color button.
  1531. //
  1532. m_cPickColor.ShowWindow((eType == ivColor255 || eType == ivColor1) ? SW_SHOW : SW_HIDE);
  1533. HFONT hControlFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
  1534. if (hControlFont == NULL)
  1535. {
  1536. hControlFont = (HFONT)GetStockObject(ANSI_VAR_FONT);
  1537. }
  1538. //
  1539. // Hide or show the Smart angles controls.
  1540. //
  1541. bool bShowSmartAngles = false;
  1542. if (eType == ivAngle)
  1543. {
  1544. CreateSmartControls_Angle( pVar, ctrlrect, hControlFont, &bShowSmartAngles );
  1545. }
  1546. m_SmartAngle.ShowWindow(bShowSmartAngles ? SW_SHOW : SW_HIDE);
  1547. m_SmartAngleEdit.ShowWindow(bShowSmartAngles ? SW_SHOW : SW_HIDE);
  1548. //
  1549. // Choices, NPC classes and filter classes get a combo box.
  1550. //
  1551. if ((eType == ivChoices) || (eType == ivNPCClass) || (eType == ivFilterClass) || (eType == ivPointEntityClass) )
  1552. {
  1553. CreateSmartControls_Choices( pVar, ctrlrect, hControlFont );
  1554. }
  1555. else if ( eType == ivInstanceVariable )
  1556. {
  1557. CreateSmartControls_InstanceVariable( pVar, ctrlrect, hControlFont );
  1558. }
  1559. else if ( eType == ivInstanceParm )
  1560. {
  1561. CreateSmartControls_InstanceParm( pVar, ctrlrect, hControlFont );
  1562. }
  1563. else
  1564. {
  1565. if ((eType == ivTargetSrc) || (eType == ivTargetDest))
  1566. {
  1567. CreateSmartControls_TargetName( pVar, ctrlrect, hControlFont );
  1568. }
  1569. else
  1570. {
  1571. CreateSmartControls_BasicEditControl( pVar, ctrlrect, hControlFont, pHelperType );
  1572. }
  1573. //
  1574. // Create a "Browse..." button for browsing for files.
  1575. //
  1576. if ((eType == ivStudioModel) || (eType == ivSprite) || (eType == ivSound) || (eType == ivDecal) ||
  1577. (eType == ivMaterial) || (eType == ivScene) || ( eType == ivInstanceFile ) )
  1578. {
  1579. CreateSmartControls_BrowseAndPlayButtons( pVar, ctrlrect, hControlFont );
  1580. }
  1581. else if ((eType == ivTargetDest) || (eType == ivTargetNameOrClass) || (eType == ivTargetSrc) || (eType == ivNodeDest))
  1582. {
  1583. CreateSmartControls_MarkAndEyedropperButtons( pVar, ctrlrect, hControlFont );
  1584. }
  1585. else if ((eType == ivSide) || (eType == ivSideList))
  1586. {
  1587. CreateSmartControls_PickButton( pVar, ctrlrect, hControlFont );
  1588. }
  1589. }
  1590. m_pSmartControl->ShowWindow(SW_SHOW);
  1591. m_pSmartControl->SetWindowPos(&m_VarList, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE);
  1592. m_bIgnoreKVChange = false;
  1593. UpdateAnchors();
  1594. }
  1595. CRect COP_Entity::CalculateSmartControlRect()
  1596. {
  1597. CRect ctrlrect;
  1598. GetDlgItem(IDC_KEY)->GetWindowRect(ctrlrect);
  1599. ScreenToClient(ctrlrect);
  1600. int nHeight = ctrlrect.Height();
  1601. int nRight = ctrlrect.right - 10;
  1602. // Put it just to the right of the keyvalue list.
  1603. m_VarList.GetWindowRect(ctrlrect);
  1604. ScreenToClient(ctrlrect);
  1605. ctrlrect.left = ctrlrect.right + 10;
  1606. ctrlrect.right = nRight;
  1607. ctrlrect.bottom = ctrlrect.top + nHeight;
  1608. return ctrlrect;
  1609. }
  1610. void COP_Entity::CreateSmartControls_Angle( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont, bool *bShowSmartAngles )
  1611. {
  1612. if (stricmp(pVar->GetName(), "angles"))
  1613. {
  1614. *bShowSmartAngles = true;
  1615. CRect rectAngleBox;
  1616. m_SmartAngle.GetWindowRect(&rectAngleBox);
  1617. CRect rectAngleEdit;
  1618. m_SmartAngleEdit.GetWindowRect(&rectAngleEdit);
  1619. m_SmartAngle.SetWindowPos(NULL, ctrlrect.left + rectAngleEdit.Width() + 4, ctrlrect.bottom + 10, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  1620. m_SmartAngleEdit.SetWindowPos(NULL, ctrlrect.left, (ctrlrect.bottom + rectAngleBox.Height() + 10) - rectAngleEdit.Height(), 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  1621. // Update the smart control with the current value
  1622. LPCTSTR pszValue = m_kv.GetValue(pVar->GetName());
  1623. if (pszValue != NULL)
  1624. {
  1625. if (!stricmp(pszValue, VALUE_DIFFERENT_STRING))
  1626. {
  1627. m_SmartAngle.SetDifferent(true);
  1628. }
  1629. else
  1630. {
  1631. m_SmartAngle.SetDifferent(false);
  1632. m_SmartAngle.SetAngles(pszValue);
  1633. }
  1634. }
  1635. }
  1636. //
  1637. // Create an eyedropper button for picking target angles.
  1638. //
  1639. CRect ButtonRect;
  1640. if (bShowSmartAngles)
  1641. {
  1642. CRect rectAngleBox;
  1643. m_SmartAngle.GetWindowRect(&rectAngleBox);
  1644. ScreenToClient(&rectAngleBox);
  1645. CRect rectAngleEdit;
  1646. m_SmartAngleEdit.GetWindowRect(&rectAngleEdit);
  1647. ScreenToClient(&rectAngleEdit);
  1648. ButtonRect.left = rectAngleBox.right + 8;
  1649. ButtonRect.top = rectAngleEdit.top;
  1650. ButtonRect.bottom = rectAngleEdit.bottom;
  1651. }
  1652. else
  1653. {
  1654. ButtonRect.left = ctrlrect.left;
  1655. ButtonRect.top = ctrlrect.bottom + 4;
  1656. ButtonRect.bottom = ButtonRect.top + ctrlrect.Height();
  1657. }
  1658. CButton *pButton = new CButton;
  1659. pButton->CreateEx(0, "Button", "Point At...", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_PUSHLIKE,
  1660. ButtonRect.left, ButtonRect.top, 58, ButtonRect.Height() + 2, GetSafeHwnd(), (HMENU)IDC_PICK_ANGLES);
  1661. pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1662. CWinApp *pApp = AfxGetApp();
  1663. HICON hIcon = pApp->LoadIcon(IDI_CROSSHAIR);
  1664. pButton->SetIcon(hIcon);
  1665. m_SmartControls.AddToTail(pButton);
  1666. }
  1667. void COP_Entity::CreateSmartControls_Choices( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont )
  1668. {
  1669. CMyComboBox *pCombo = new CMyComboBox;
  1670. pCombo->SetParentPage(this);
  1671. ctrlrect.bottom += 150;
  1672. 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);
  1673. pCombo->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1674. pCombo->SetDroppedWidth(150);
  1675. //
  1676. // If we are editing multiple entities, start with the "(different)" string.
  1677. //
  1678. if (IsMultiEdit())
  1679. {
  1680. pCombo->AddString(VALUE_DIFFERENT_STRING);
  1681. }
  1682. //
  1683. // If this is a choices field, give combo box text choices from GameData variable
  1684. //
  1685. if (pVar->GetType() == ivChoices)
  1686. {
  1687. for (int i = 0; i < pVar->GetChoiceCount(); i++)
  1688. {
  1689. pCombo->AddString(pVar->GetChoiceCaption(i));
  1690. }
  1691. }
  1692. //
  1693. // For filterclass display a list of all the names of filters that are in the map
  1694. //
  1695. else if (pVar->GetType() == ivFilterClass)
  1696. {
  1697. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1698. CMapWorld *pWorld = pDoc->GetMapWorld();
  1699. const CMapEntityList *pEntityList = pWorld->EntityList_GetList();
  1700. FOR_EACH_OBJ( *pEntityList, pos )
  1701. {
  1702. CMapEntity *pEntity = pEntityList->Element(pos);
  1703. GDclass *pClass = pEntity->GetClass();
  1704. if (pClass && pClass->IsFilterClass())
  1705. {
  1706. const char *pString = pEntity->GetKeyValue("targetname");
  1707. if (pString)
  1708. {
  1709. pCombo->AddString(pString);
  1710. }
  1711. }
  1712. }
  1713. }
  1714. //
  1715. // For npcclass fields, fill with all the NPC classes from the FGD.
  1716. //
  1717. else if (pVar->GetType() == ivNPCClass)
  1718. {
  1719. if (pGD != NULL)
  1720. {
  1721. int nCount = pGD->GetClassCount();
  1722. for (int i = 0; i < nCount; i++)
  1723. {
  1724. GDclass *pClass = pGD->GetClass(i);
  1725. if (pClass->IsNPCClass())
  1726. {
  1727. pCombo->AddString(pClass->GetName());
  1728. }
  1729. }
  1730. }
  1731. }
  1732. //
  1733. // For pointentity fields, fill with all the point entity classes from the FGD.
  1734. //
  1735. else
  1736. {
  1737. if (pGD != NULL)
  1738. {
  1739. int nCount = pGD->GetClassCount();
  1740. for (int i = 0; i < nCount; i++)
  1741. {
  1742. GDclass *pClass = pGD->GetClass(i);
  1743. if (pClass->IsPointClass())
  1744. {
  1745. pCombo->AddString(pClass->GetName());
  1746. }
  1747. }
  1748. }
  1749. }
  1750. //
  1751. // If the current value is the "different" string, display that in the combo box.
  1752. //
  1753. LPCTSTR pszValue = m_kv.GetValue(pVar->GetName());
  1754. if (pszValue != NULL)
  1755. {
  1756. if (strcmp(pszValue, VALUE_DIFFERENT_STRING) == 0)
  1757. {
  1758. pCombo->SelectString(-1, VALUE_DIFFERENT_STRING);
  1759. }
  1760. else
  1761. {
  1762. const char *p = NULL;
  1763. //
  1764. // If this is a choices field and the current value corresponds to one of our
  1765. // choices, display the friendly name of the choice in the edit control.
  1766. //
  1767. if (pVar->GetType() == ivChoices)
  1768. {
  1769. p = pVar->ItemStringForValue(pszValue);
  1770. }
  1771. if (p != NULL)
  1772. {
  1773. pCombo->SelectString(-1, p);
  1774. }
  1775. //
  1776. // Otherwise, just display the value as-is.
  1777. //
  1778. else
  1779. {
  1780. pCombo->SetWindowText(pszValue);
  1781. }
  1782. }
  1783. }
  1784. m_pSmartControl = pCombo;
  1785. m_SmartControls.AddToTail(pCombo);
  1786. }
  1787. void COP_Entity::CreateSmartControls_TargetName( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont )
  1788. {
  1789. //
  1790. // Create a combo box for picking target names.
  1791. //
  1792. CRect ComboRect = ctrlrect;
  1793. ComboRect.bottom += 150;
  1794. 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);
  1795. pCombo->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1796. pCombo->SetDroppedWidth(150);
  1797. m_pSmartControl = pCombo;
  1798. m_SmartControls.AddToTail(m_pSmartControl);
  1799. //
  1800. // Attach the world's entity list to the add dialog.
  1801. //
  1802. // HACK: This is ugly. It should be passed in from outside.
  1803. //
  1804. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1805. CMapWorld *pWorld = pDoc->GetMapWorld();
  1806. pCombo->SetEntityList(pWorld->EntityList_GetList());
  1807. pCombo->SelectItem( m_kv.GetValue(pVar->GetName()) );
  1808. if (pVar->IsReadOnly())
  1809. {
  1810. pCombo->EnableWindow(FALSE);
  1811. }
  1812. }
  1813. void COP_Entity::CreateSmartControls_BasicEditControl( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont, CUtlVector<const char *> *pHelperType )
  1814. {
  1815. //
  1816. // Create an edit control for inputting the keyvalue as text.
  1817. //
  1818. CMyEdit *pEdit = new CMyEdit;
  1819. pEdit->SetParentPage(this);
  1820. ctrlrect.bottom += 2;
  1821. pEdit->CreateEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
  1822. ctrlrect.left, ctrlrect.top, ctrlrect.Width(), ctrlrect.Height(), GetSafeHwnd(), HMENU(IDC_SMARTCONTROL));
  1823. pEdit->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1824. const char *pValue = m_kv.GetValue( pVar->GetName() );
  1825. if ( pValue )
  1826. pEdit->SetWindowText( pValue );
  1827. if (pVar->IsReadOnly())
  1828. {
  1829. pEdit->EnableWindow(FALSE);
  1830. }
  1831. for ( int i = 0; i < pHelperType->Count(); i++ )
  1832. {
  1833. if ( !Q_strcmp( pHelperType->Element(i), "sphere" ) )
  1834. {
  1835. CRect ButtonRect = ctrlrect;
  1836. ButtonRect.top = ctrlrect.bottom + 4;
  1837. ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4;
  1838. ButtonRect.right = ButtonRect.left + 32;
  1839. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1840. CMapView3D *pView = pDoc->GetFirst3DView();
  1841. int disabled = 0;
  1842. if ( !pView )
  1843. {
  1844. disabled = WS_DISABLED;
  1845. }
  1846. CButton *pButton = new CButton;
  1847. pButton->CreateEx(0, "Button", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_ICON | BS_PUSHLIKE | disabled,
  1848. ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(),
  1849. GetSafeHwnd(), (HMENU)IDC_CAMERA_DISTANCE);
  1850. ButtonRect.left += ButtonRect.Width() + 4;
  1851. CWinApp *pApp = AfxGetApp();
  1852. HICON hIcon = pApp->LoadIcon(IDI_CAMERA);
  1853. pButton->SetIcon(hIcon);
  1854. m_SmartControls.AddToTail(pButton);
  1855. }
  1856. }
  1857. m_pSmartControl = pEdit;
  1858. m_SmartControls.AddToTail(pEdit);
  1859. }
  1860. void COP_Entity::CreateSmartControls_BrowseAndPlayButtons( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont )
  1861. {
  1862. CRect ButtonRect = ctrlrect;
  1863. ButtonRect.top = ctrlrect.bottom + 4;
  1864. ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4;
  1865. ButtonRect.right = ButtonRect.left + 54;
  1866. HMENU message = (HMENU)IDC_BROWSE;
  1867. if ( pVar->GetType() == ivInstanceFile )
  1868. {
  1869. message = (HMENU)IDC_BROWSE_INSTANCE;
  1870. }
  1871. CButton *pButton = new CButton;
  1872. pButton->CreateEx(0, "Button", "Browse...", WS_TABSTOP | WS_CHILD | WS_VISIBLE,
  1873. ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(),
  1874. GetSafeHwnd(), message);
  1875. pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1876. m_pSmartBrowseButton = pButton;
  1877. m_SmartControls.AddToTail(pButton);
  1878. if ( pVar->GetType() == ivSound || pVar->GetType() == ivScene )
  1879. {
  1880. ButtonRect.left = ButtonRect.right + 8;
  1881. ButtonRect.right = ButtonRect.left + 54;
  1882. pButton = new CButton;
  1883. pButton->CreateEx(0, "Button", "Play", WS_TABSTOP | WS_CHILD | WS_VISIBLE,
  1884. ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(),
  1885. GetSafeHwnd(), (HMENU)IDC_PLAY_SOUND);
  1886. pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1887. m_SmartControls.AddToTail(pButton);
  1888. }
  1889. }
  1890. void COP_Entity::CreateSmartControls_MarkAndEyedropperButtons( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont )
  1891. {
  1892. CRect ButtonRect = ctrlrect;
  1893. ButtonRect.top = ctrlrect.bottom + 4;
  1894. ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4;
  1895. CButton *pButton = NULL;
  1896. GDIV_TYPE eType = pVar->GetType();
  1897. if ((eType == ivTargetDest) || (eType == ivTargetNameOrClass) || (eType == ivTargetSrc))
  1898. {
  1899. //
  1900. // Create a "Mark" button for finding target entities.
  1901. //
  1902. ButtonRect.right = ButtonRect.left + 48;
  1903. pButton = new CButton;
  1904. pButton->CreateEx(0, "Button", "Mark", WS_TABSTOP | WS_CHILD | WS_VISIBLE,
  1905. ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(),
  1906. GetSafeHwnd(), (HMENU)IDC_MARK);
  1907. pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1908. ButtonRect.left += ButtonRect.Width() + 4;
  1909. m_SmartControls.AddToTail(pButton);
  1910. //
  1911. // Create a "Mark+Add" button for finding target entities.
  1912. //
  1913. ButtonRect.right = ButtonRect.left + 64;
  1914. pButton = new CButton;
  1915. pButton->CreateEx(0, "Button", "Mark+Add", WS_TABSTOP | WS_CHILD | WS_VISIBLE,
  1916. ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(),
  1917. GetSafeHwnd(), (HMENU)IDC_MARK_AND_ADD);
  1918. pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1919. ButtonRect.left += ButtonRect.Width() + 4;
  1920. m_SmartControls.AddToTail(pButton);
  1921. }
  1922. //
  1923. // Create an eyedropper button for picking entities.
  1924. //
  1925. ButtonRect.right = ButtonRect.left + 32;
  1926. pButton = new CButton;
  1927. pButton->CreateEx(0, "Button", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_ICON | BS_AUTOCHECKBOX | BS_PUSHLIKE,
  1928. ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(),
  1929. GetSafeHwnd(), (HMENU)IDC_PICK_ENTITY);
  1930. ButtonRect.left += ButtonRect.Width() + 4;
  1931. CWinApp *pApp = AfxGetApp();
  1932. HICON hIcon = pApp->LoadIcon(IDI_EYEDROPPER);
  1933. pButton->SetIcon(hIcon);
  1934. m_SmartControls.AddToTail(pButton);
  1935. }
  1936. void COP_Entity::CreateSmartControls_PickButton( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont )
  1937. {
  1938. //
  1939. // Create a "Pick" button for clicking on brush sides.
  1940. //
  1941. CRect ButtonRect = ctrlrect;
  1942. ButtonRect.top = ctrlrect.bottom + 4;
  1943. ButtonRect.bottom = ctrlrect.bottom + ctrlrect.Height() + 4;
  1944. ButtonRect.right = ButtonRect.left + 54;
  1945. CButton *pButton = new CButton;
  1946. pButton->CreateEx(0, "Button", "Pick...", WS_TABSTOP | WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_PUSHLIKE,
  1947. ButtonRect.left, ButtonRect.top, ButtonRect.Width(), ButtonRect.Height(),
  1948. GetSafeHwnd(), (HMENU)IDC_PICK_FACES);
  1949. pButton->SendMessage(WM_SETFONT, (WPARAM)hControlFont);
  1950. m_SmartControls.AddToTail(pButton);
  1951. }
  1952. //-----------------------------------------------------------------------------
  1953. // Purpose: this function will set up the smart controls for an instance variable
  1954. // Input : pVar - the kv pair
  1955. // ctrlrect - the rect space of this area
  1956. // hControlFont - the standard font
  1957. //-----------------------------------------------------------------------------
  1958. void COP_Entity::CreateSmartControls_InstanceVariable( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont )
  1959. {
  1960. const char *pValue = m_kv.GetValue( pVar->GetName() );
  1961. char ValueData[ KEYVALUE_MAX_KEY_LENGTH ];
  1962. const char *pVariable, *pReplace;
  1963. const int VariableLimit = 50;
  1964. pVariable = pReplace = "";
  1965. if ( pValue )
  1966. {
  1967. strcpy( ValueData, pValue );
  1968. pVariable = ValueData;
  1969. char *pos = strchr( ValueData, ' ' );
  1970. if ( pos )
  1971. {
  1972. *pos = 0;
  1973. pos++;
  1974. pReplace = pos;
  1975. }
  1976. }
  1977. if ( m_pObjectList->Count() == 1 )
  1978. {
  1979. CMapEntity *pEntity = static_cast< CMapEntity * >( m_pObjectList->Element( 0 ) );
  1980. CMapInstance *pMapInstance = pEntity->GetChildOfType( ( CMapInstance * )NULL );
  1981. if ( pMapInstance != NULL && pMapInstance->GetInstancedMap() != NULL )
  1982. {
  1983. CMapEntityList entityList;
  1984. pMapInstance->GetInstancedMap()->FindEntitiesByClassName( entityList, "func_instance_parms", false );
  1985. if ( entityList.Count() == 1 )
  1986. {
  1987. CMapEntity *pInstanceParmsEntity = entityList.Element( 0 );
  1988. for ( int i = pInstanceParmsEntity->GetFirstKeyValue(); i != pInstanceParmsEntity->GetInvalidKeyValue(); i = pInstanceParmsEntity->GetNextKeyValue( i ) )
  1989. {
  1990. LPCTSTR pKey = pInstanceParmsEntity->GetKey( i );
  1991. LPCTSTR psValue = pInstanceParmsEntity->GetKeyValue( i );
  1992. if ( strnicmp( pKey, "parm", strlen( "parm" ) ) == 0 )
  1993. {
  1994. if ( strnicmp( psValue, pVariable, strlen( pVariable ) ) == 0 )
  1995. {
  1996. m_kv.SetValue( "temp_parm_value", pReplace );
  1997. m_kv.SetValue( "temp_parm_name", pVar->GetName() );
  1998. m_kv.SetValue( "temp_parm_field", pVariable );
  1999. m_pInstanceVar = new GDinputvariable( "color255", "temp_parm_value" );
  2000. CUtlVector<const char *>helperNames;
  2001. m_pDisplayClass->GetHelperForGDVar( m_pInstanceVar, &helperNames );
  2002. //
  2003. // Update the keyvalue help text control with this variable's help info.
  2004. //
  2005. m_KeyValueHelpText.SetWindowText(m_pInstanceVar->GetDescription());
  2006. CreateSmartControls(m_pInstanceVar, &helperNames);
  2007. m_eEditType = m_pInstanceVar->GetType();
  2008. return;
  2009. }
  2010. }
  2011. }
  2012. }
  2013. }
  2014. }
  2015. CStatic *pStaticInstanceVariable;
  2016. pStaticInstanceVariable = new CStatic;
  2017. pStaticInstanceVariable->CreateEx( WS_EX_LEFT, "STATIC", "Variable:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) );
  2018. pStaticInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2019. m_pEditInstanceVariable = new CEdit;
  2020. ctrlrect.bottom += 2;
  2021. m_pEditInstanceVariable->CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
  2022. ctrlrect.left + 50, ctrlrect.top, ctrlrect.Width() - 50, 24, GetSafeHwnd(), HMENU( IDC_SMARTCONTROL_INSTANCE_VARIABLE ) );
  2023. m_pEditInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2024. m_pEditInstanceVariable->SetWindowText( pVariable );
  2025. m_pEditInstanceVariable->SetLimitText( VariableLimit );
  2026. if ( pVar->IsReadOnly() )
  2027. {
  2028. m_pEditInstanceVariable->EnableWindow( FALSE );
  2029. }
  2030. ctrlrect.top += 26;
  2031. ctrlrect.bottom += 26;
  2032. CStatic *pStaticInstanceValue;
  2033. pStaticInstanceValue = new CStatic;
  2034. pStaticInstanceValue->CreateEx( WS_EX_LEFT, "STATIC", "Value:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) );
  2035. pStaticInstanceValue->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2036. m_pEditInstanceValue = new CEdit;
  2037. m_pEditInstanceValue->CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
  2038. ctrlrect.left + 50, ctrlrect.top, ctrlrect.Width() - 50, 24, GetSafeHwnd(), HMENU( IDC_SMARTCONTROL_INSTANCE_VALUE ) );
  2039. m_pEditInstanceValue->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2040. m_pEditInstanceValue->SetWindowText( pReplace );
  2041. m_pEditInstanceVariable->SetLimitText( KEYVALUE_MAX_KEY_LENGTH - VariableLimit - 2 ); // to account for null and space in between
  2042. if ( pVar->IsReadOnly() )
  2043. {
  2044. m_pEditInstanceValue->EnableWindow( FALSE );
  2045. }
  2046. m_pSmartControl = m_pEditInstanceVariable;
  2047. m_SmartControls.AddToTail( m_pEditInstanceVariable );
  2048. m_SmartControls.AddToTail( m_pEditInstanceValue );
  2049. m_SmartControls.AddToTail( pStaticInstanceVariable );
  2050. m_SmartControls.AddToTail( pStaticInstanceValue );
  2051. }
  2052. //-----------------------------------------------------------------------------
  2053. // Purpose: this function will set up the smart controls for an instance parameter
  2054. // Input : pVar - the kv pair
  2055. // ctrlrect - the rect space of this area
  2056. // hControlFont - the standard font
  2057. //-----------------------------------------------------------------------------
  2058. void COP_Entity::CreateSmartControls_InstanceParm( GDinputvariable *pVar, CRect &ctrlrect, HFONT hControlFont )
  2059. {
  2060. const char *pValue = m_kv.GetValue( pVar->GetName() );
  2061. char ValueData[ KEYVALUE_MAX_KEY_LENGTH ];
  2062. const char *pVariable, *pType;
  2063. const int VariableLimit = 50;
  2064. pVariable = pType = "";
  2065. if ( pValue )
  2066. {
  2067. strcpy( ValueData, pValue );
  2068. pVariable = ValueData;
  2069. char *pos = strchr( ValueData, ' ' );
  2070. if ( pos )
  2071. {
  2072. *pos = 0;
  2073. pos++;
  2074. pType = pos;
  2075. }
  2076. }
  2077. CStatic *pStaticInstanceVariable;
  2078. pStaticInstanceVariable = new CStatic;
  2079. pStaticInstanceVariable->CreateEx( WS_EX_LEFT, "STATIC", "Variable:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) );
  2080. pStaticInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2081. m_pEditInstanceVariable = new CEdit;
  2082. ctrlrect.bottom += 2;
  2083. m_pEditInstanceVariable->CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
  2084. ctrlrect.left + 50, ctrlrect.top, ctrlrect.Width() - 50, 24, GetSafeHwnd(), HMENU( IDC_SMARTCONTROL_INSTANCE_PARM ) );
  2085. m_pEditInstanceVariable->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2086. m_pEditInstanceVariable->SetWindowText( pVariable );
  2087. m_pEditInstanceVariable->SetLimitText( VariableLimit );
  2088. if ( pVar->IsReadOnly() )
  2089. {
  2090. m_pEditInstanceVariable->EnableWindow( FALSE );
  2091. }
  2092. ctrlrect.top += 26;
  2093. ctrlrect.bottom += 26;
  2094. CStatic *pStaticInstanceValue;
  2095. pStaticInstanceValue = new CStatic;
  2096. pStaticInstanceValue->CreateEx( WS_EX_LEFT, "STATIC", "Value:", WS_CHILD | WS_VISIBLE | SS_LEFT, ctrlrect.left, ctrlrect.top, 50, 24, GetSafeHwnd(), HMENU( IDC_STATIC ) );
  2097. pStaticInstanceValue->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2098. m_pComboInstanceParmType = new CMyComboBox;
  2099. m_pComboInstanceParmType->SetParentPage( this );
  2100. ctrlrect.bottom += 150;
  2101. ctrlrect.left += 50;
  2102. 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 );
  2103. m_pComboInstanceParmType->SendMessage( WM_SETFONT, ( WPARAM )hControlFont );
  2104. m_pComboInstanceParmType->SetDroppedWidth( 150 );
  2105. if ( pVar->IsReadOnly() )
  2106. {
  2107. m_pComboInstanceParmType->EnableWindow( FALSE );
  2108. }
  2109. for( int i = 0; i < ivMax; i++ )
  2110. {
  2111. m_pComboInstanceParmType->AddString( GDinputvariable::GetVarTypeName( ( GDIV_TYPE )i ) );
  2112. }
  2113. m_pComboInstanceParmType->SelectString( -1, pType );
  2114. m_pSmartControl = m_pEditInstanceVariable;
  2115. m_SmartControls.AddToTail( m_pEditInstanceVariable );
  2116. m_SmartControls.AddToTail( m_pComboInstanceParmType );
  2117. m_SmartControls.AddToTail( pStaticInstanceVariable );
  2118. m_SmartControls.AddToTail( pStaticInstanceValue );
  2119. }
  2120. //-----------------------------------------------------------------------------
  2121. //-----------------------------------------------------------------------------
  2122. void COP_Entity::SetSmartControlText(const char *pszText)
  2123. {
  2124. // dvs: HACK: the smart control should be completely self-contained, the dialog
  2125. // should only set the type of the edited variable, then just set & get text
  2126. CTargetNameComboBox *pCombo = dynamic_cast <CTargetNameComboBox *>(m_pSmartControl);
  2127. if (pCombo)
  2128. {
  2129. pCombo->SelectItem(pszText);
  2130. }
  2131. else
  2132. {
  2133. m_pSmartControl->SetWindowText(pszText);
  2134. }
  2135. // FIXME: this should be called automatically, but it isn't
  2136. OnChangeSmartcontrol();
  2137. }
  2138. //-----------------------------------------------------------------------------
  2139. // Purpose:
  2140. //-----------------------------------------------------------------------------
  2141. void COP_Entity::OnSelchangeKeyvalues(void)
  2142. {
  2143. //VPROF_BUDGET( "COP_Entity::OnSelchangeKeyvalues", "Object Properties" );
  2144. //
  2145. // Load new selection's key/values into the key/value
  2146. // edit controls.
  2147. //
  2148. if (m_VarList.m_hWnd == NULL)
  2149. {
  2150. return;
  2151. }
  2152. if (m_bSmartedit)
  2153. {
  2154. //
  2155. // Stop face or entity picking if we are doing so.
  2156. //
  2157. StopPicking();
  2158. }
  2159. int iSel = GetCurVarListSelection();
  2160. if (iSel == LB_ERR)
  2161. {
  2162. if (!m_bSmartedit)
  2163. {
  2164. // No selection; clear our the key and value controls.
  2165. m_cKey.SetWindowText("");
  2166. m_cValue.SetWindowText("");
  2167. }
  2168. return;
  2169. }
  2170. if (!m_bSmartedit)
  2171. {
  2172. CString str, val;
  2173. str = m_VarList.GetItemText( iSel, 0 );
  2174. val = m_VarList.GetItemText( iSel, 1 );
  2175. //
  2176. // Set the edit control text, but ignore the notifications caused by that.
  2177. //
  2178. m_bIgnoreKVChange = true;
  2179. m_cKey.SetWindowText( str );
  2180. m_cValue.SetWindowText( val );
  2181. m_bChangingKeyName = false;
  2182. m_bIgnoreKVChange = false;
  2183. }
  2184. else
  2185. {
  2186. GDinputvariable *pVar = GetVariableAt( iSel );
  2187. if ( pVar == NULL || !m_pDisplayClass )
  2188. {
  2189. // This is a var added in dumbedit mode.
  2190. DestroySmartControls();
  2191. m_KeyValueHelpText.SetWindowText( "" );
  2192. }
  2193. else
  2194. {
  2195. CUtlVector<const char *>helperNames;
  2196. m_pDisplayClass->GetHelperForGDVar( pVar, &helperNames );
  2197. //
  2198. // Update the keyvalue help text control with this variable's help info.
  2199. //
  2200. m_KeyValueHelpText.SetWindowText(pVar->GetDescription());
  2201. CreateSmartControls(pVar, &helperNames);
  2202. m_eEditType = pVar->GetType();
  2203. }
  2204. }
  2205. }
  2206. //-----------------------------------------------------------------------------
  2207. // Purpose: Used only in standard (non-SmartEdit) mode. Called when the contents
  2208. // of the value edit control change. This is where the edit control
  2209. // contents are converted to keyvalue data in standard mode.
  2210. //-----------------------------------------------------------------------------
  2211. void COP_Entity::OnChangeKeyorValue(void)
  2212. {
  2213. if (m_bIgnoreKVChange)
  2214. {
  2215. return;
  2216. }
  2217. int iSel = GetCurVarListSelection();
  2218. if (iSel == LB_ERR)
  2219. {
  2220. return;
  2221. }
  2222. char szKey[KEYVALUE_MAX_KEY_LENGTH];
  2223. char szValue[KEYVALUE_MAX_VALUE_LENGTH];
  2224. m_cKey.GetWindowText(szKey, sizeof(szKey));
  2225. m_cValue.GetWindowText(szValue, sizeof(szValue));
  2226. UpdateKeyValue(szKey, szValue);
  2227. //
  2228. // Save it in our local kv storage.
  2229. //
  2230. m_kv.SetValue(szKey, szValue);
  2231. // If they changed spawnflags, notify the flags page so its changes don't overwrite ours later.
  2232. if ( V_stricmp( szKey, SPAWNFLAGS_KEYNAME ) == 0 )
  2233. {
  2234. unsigned long value;
  2235. sscanf( szValue, "%lu", &value );
  2236. m_pFlagsPage->OnUpdateSpawnFlags( value );
  2237. }
  2238. if (m_bEnableControlUpdate)
  2239. {
  2240. // Update any controls that are displaying the same data as the edit control.
  2241. // This code should only be hit as a result of user input in the edit control!
  2242. // If they changed the "angles" key, update the main angles control.
  2243. if (!stricmp(szKey, "angles"))
  2244. {
  2245. m_Angle.SetDifferent(false);
  2246. m_Angle.SetAngles(szValue, true);
  2247. }
  2248. }
  2249. }
  2250. //-----------------------------------------------------------------------------
  2251. // Purpose:
  2252. //-----------------------------------------------------------------------------
  2253. void COP_Entity::OnAddkeyvalue(void)
  2254. {
  2255. //VPROF_BUDGET( "COP_Entity::OnAddkeyvalue", "Object Properties" );
  2256. // create a new keyvalue at the end of the list
  2257. CNewKeyValue newkv;
  2258. newkv.m_Key.Format("newkey%d", m_nNewKeyCount++);
  2259. newkv.m_Value = "value";
  2260. if(newkv.DoModal() == IDCANCEL)
  2261. return;
  2262. // if the key we're adding is already in the list, do some
  2263. // stuff to make it unique
  2264. if(m_kv.GetValue(newkv.m_Key))
  2265. {
  2266. CString strTemp;
  2267. for(int i = 1; ; i++)
  2268. {
  2269. strTemp.Format("%s#%d", newkv.m_Key.GetBuffer(), i);
  2270. if(!m_kv.GetValue(strTemp))
  2271. break;
  2272. }
  2273. newkv.m_Key = strTemp;
  2274. }
  2275. m_kvAdded.SetValue( newkv.m_Key, "1" );
  2276. m_kv.SetValue( newkv.m_Key, newkv.m_Value );
  2277. PresentProperties();
  2278. SetCurKey( newkv.m_Key );
  2279. }
  2280. //-----------------------------------------------------------------------------
  2281. // Purpose: Deletes the selected keyvalue.
  2282. //-----------------------------------------------------------------------------
  2283. void COP_Entity::OnRemovekeyvalue(void)
  2284. {
  2285. int iSel = GetCurVarListSelection();
  2286. if (iSel == LB_ERR)
  2287. {
  2288. return;
  2289. }
  2290. CString strBuf;
  2291. strBuf = (CString)(const char*)m_VarList.GetItemData(iSel);
  2292. //
  2293. // Remove the keyvalue from local storage.
  2294. //
  2295. m_kv.RemoveKey( strBuf );
  2296. m_VarList.DeleteItem(iSel);
  2297. if (iSel == m_VarList.GetItemCount())
  2298. {
  2299. SetCurVarListSelection( iSel - 1 );
  2300. }
  2301. ResortItems();
  2302. OnSelchangeKeyvalues();
  2303. }
  2304. //-----------------------------------------------------------------------------
  2305. // Returns a mask indicating which flags have the same caption and bit value
  2306. // between the two classes.
  2307. //-----------------------------------------------------------------------------
  2308. static unsigned long GetMatchingFlagsMask( GDinputvariable *pVar1, GDinputvariable *pVar2 )
  2309. {
  2310. unsigned long nMatchingMask = 0;
  2311. for ( int i=0; i < pVar1->GetFlagCount(); i++ )
  2312. {
  2313. for ( int j=0; j < pVar2->GetFlagCount(); j++ )
  2314. {
  2315. if ( pVar1->GetFlagMask( i ) == pVar2->GetFlagMask( j ) )
  2316. {
  2317. if ( V_stricmp( pVar1->GetFlagCaption( i ), pVar2->GetFlagCaption( j ) ) == 0 )
  2318. {
  2319. unsigned long iMask = (unsigned long)pVar1->GetFlagMask( i );
  2320. nMatchingMask |= iMask;
  2321. break;
  2322. }
  2323. }
  2324. }
  2325. }
  2326. return nMatchingMask;
  2327. }
  2328. //-----------------------------------------------------------------------------
  2329. // Assign default values to keys that are in the FGD but missing from the entity.
  2330. //-----------------------------------------------------------------------------
  2331. void COP_Entity::AssignClassDefaults(GDclass *pClass, GDclass *pOldClass)
  2332. {
  2333. //VPROF_BUDGET( "COP_Entity::AssignClassDefaults", "Object Properties" );
  2334. if (!pClass)
  2335. return;
  2336. for (int i = 0; i < pClass->GetVariableCount(); i++)
  2337. {
  2338. GDinputvariable *pVar = pClass->GetVariableAt(i);
  2339. int iIndex;
  2340. LPCTSTR p = m_kv.GetValue(pVar->GetName(), &iIndex);
  2341. // Always reset spawnflags.
  2342. if (!strcmpi(pVar->GetName(), SPAWNFLAGS_KEYNAME))
  2343. {
  2344. unsigned long nOriginalFlagsValue = 0;
  2345. if (p)
  2346. {
  2347. sscanf( p, "%lu", &nOriginalFlagsValue );
  2348. }
  2349. unsigned long nCurrent = nOriginalFlagsValue;
  2350. if ( pOldClass && (pOldClass != pClass) )
  2351. {
  2352. // First, just use the defaults for the new class.
  2353. int defaultValue;
  2354. pVar->GetDefault( &defaultValue );
  2355. nCurrent = (unsigned long)defaultValue;
  2356. // But.. if the old class and the new class have any flags with the same name and value,
  2357. // then keep the current value for that flag.
  2358. GDinputvariable *pOldVar = pOldClass->VarForName( SPAWNFLAGS_KEYNAME );
  2359. if ( p && pOldVar )
  2360. {
  2361. unsigned long mask = GetMatchingFlagsMask( pOldVar, pVar );
  2362. nCurrent &= ~mask; // Get rid of the default value.
  2363. nCurrent |= (nOriginalFlagsValue & mask); // Add back in the current values.
  2364. }
  2365. // Notify the flags page so it'll have the right data if they tab to it.
  2366. // It'll also SAVE the right data when they save.
  2367. m_pFlagsPage->OnUpdateSpawnFlags( nCurrent );
  2368. }
  2369. else
  2370. {
  2371. unsigned long nMask = 0;
  2372. int nCount = pVar->GetFlagCount();
  2373. for (int j = 0; j < nCount; j++)
  2374. {
  2375. nMask |= (unsigned int)pVar->GetFlagMask(j);
  2376. }
  2377. // Mask off any bits that aren't defined in the FGD.
  2378. nCurrent &= nMask;
  2379. }
  2380. char szValue[128];
  2381. Q_snprintf( szValue, sizeof( szValue ), "%lu", nCurrent );
  2382. // Remember that we added or changed this key.
  2383. if (!p || Q_stricmp(p, szValue) != 0)
  2384. {
  2385. m_kvAdded.SetValue(SPAWNFLAGS_KEYNAME, "1");
  2386. }
  2387. m_kv.SetValue(SPAWNFLAGS_KEYNAME, szValue);
  2388. }
  2389. else if (!p)
  2390. {
  2391. MDkeyvalue newkv;
  2392. pVar->ResetDefaults();
  2393. pVar->ToKeyValue(&newkv);
  2394. m_kv.SetValue(newkv.szKey, newkv.szValue);
  2395. // Remember that we added this key.
  2396. m_kvAdded.SetValue(newkv.szKey, "1");
  2397. }
  2398. }
  2399. }
  2400. //-----------------------------------------------------------------------------
  2401. // Purpose: Updates the dialog based on a change to the entity class name.
  2402. //-----------------------------------------------------------------------------
  2403. void COP_Entity::UpdateEditClass(const char *pszClass, bool bForce)
  2404. {
  2405. //VPROF_BUDGET( "COP_Entity::UpdateClass", "Object Properties" );
  2406. GDclass *pOldEditClass = m_pEditClass;
  2407. m_pEditClass = pGD->ClassForName(pszClass);
  2408. if (!bForce && (m_pEditClass == pOldEditClass))
  2409. return;
  2410. //DBG("UpdateEditClass - BEFORE PRUNE\n");
  2411. //DumpKeyvalues(m_kv);
  2412. //
  2413. // Remove unused keyvalues.
  2414. //
  2415. if (m_pEditClass != pOldEditClass && m_pEditClass && pOldEditClass && strcmpi(pszClass, "multi_manager"))
  2416. {
  2417. int iNext;
  2418. for ( int i=m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=iNext )
  2419. {
  2420. iNext = m_kv.GetNext( i );
  2421. MDkeyvalue &KeyValue = m_kv.GetKeyValue(i);
  2422. if (m_pEditClass->VarForName(KeyValue.szKey) == NULL)
  2423. {
  2424. m_kv.RemoveKey(KeyValue.szKey);
  2425. }
  2426. }
  2427. }
  2428. //DBG("UpdateEditClass - AFTER PRUNE\n");
  2429. //DumpKeyvalues(m_kv);
  2430. AssignClassDefaults(m_pEditClass, pOldEditClass);
  2431. PresentProperties();
  2432. SetReadOnly( ( m_pDisplayClass != m_pEditClass ) || ( m_bCanEdit == false ) );
  2433. }
  2434. //-----------------------------------------------------------------------------
  2435. // Purpose: Called when a keyvalue is modified by the user.
  2436. // Input : szKey - The key name.
  2437. // szValue - The new value.
  2438. //-----------------------------------------------------------------------------
  2439. void COP_Entity::UpdateKeyValue(const char *szKey, const char *szValue)
  2440. {
  2441. //VPROF_BUDGET( "COP_Entity::UpdateKeyValue", "Object Properties" );
  2442. m_kvAdded.SetValue(szKey, "1");
  2443. m_kv.SetValue(szKey, szValue);
  2444. int index = m_InstanceParmData.Find( szKey );
  2445. if ( index != m_InstanceParmData.InvalidIndex() )
  2446. {
  2447. CString NewValue = m_InstanceParmData[ index ].m_VariableName + " " + szValue;
  2448. m_kvAdded.SetValue( m_InstanceParmData[ index ].m_ParmKey, "1" );
  2449. m_kv.SetValue( m_InstanceParmData[ index ].m_ParmKey, NewValue );
  2450. RefreshKVListValues( m_InstanceParmData[ index ].m_ParmKey );
  2451. }
  2452. RefreshKVListValues( szKey );
  2453. }
  2454. //-----------------------------------------------------------------------------
  2455. // Purpose: Enables or disables SmartEdit mode, hiding showing the appropriate
  2456. // dialog controls.
  2457. // Input : b - TRUE to enable, FALSE to disable SmartEdit.
  2458. //-----------------------------------------------------------------------------
  2459. void COP_Entity::SetSmartedit(bool bSet)
  2460. {
  2461. // Nothing to do?
  2462. if ( m_bSmartedit == bSet )
  2463. return;
  2464. m_bSmartedit = bSet;
  2465. //
  2466. // If disabling smartedit, remove any smartedit-specific controls that may
  2467. // or may not be currently visible.
  2468. //
  2469. if (!m_bSmartedit)
  2470. {
  2471. m_cPickColor.ShowWindow(SW_HIDE);
  2472. m_SmartAngle.ShowWindow(SW_HIDE);
  2473. m_SmartAngleEdit.ShowWindow(SW_HIDE);
  2474. DestroySmartControls();
  2475. }
  2476. m_KeyValueHelpText.ShowWindow(m_bSmartedit ? SW_SHOW : SW_HIDE);
  2477. GetDlgItem(IDC_KEYVALUE_HELP_GROUP)->ShowWindow(m_bSmartedit ? SW_SHOW : SW_HIDE);
  2478. //
  2479. // Hide or show all controls after and including "delete kv" button.
  2480. //
  2481. for (int i = 0; i < ARRAYSIZE(g_DumbEditControls); i++)
  2482. {
  2483. CWnd *pWnd = GetDlgItem(g_DumbEditControls[i]);
  2484. if ( pWnd )
  2485. pWnd->ShowWindow(m_bSmartedit ? SW_HIDE : SW_SHOW);
  2486. }
  2487. ((CButton*)GetDlgItem(IDC_SMARTEDIT))->SetCheck(m_bSmartedit);
  2488. PresentProperties();
  2489. }
  2490. //-----------------------------------------------------------------------------
  2491. //-----------------------------------------------------------------------------
  2492. void COP_Entity::SetReadOnly(bool bReadOnly)
  2493. {
  2494. m_VarList.EnableWindow(bReadOnly ? FALSE : TRUE);
  2495. m_cPickColor.EnableWindow(bReadOnly ? FALSE : TRUE);
  2496. m_SmartAngle.EnableWindow(bReadOnly ? FALSE : TRUE);
  2497. m_SmartAngleEdit.EnableWindow(bReadOnly ? FALSE : TRUE);
  2498. m_PasteControl.EnableWindow(bReadOnly ? FALSE : TRUE);
  2499. m_KeyValueHelpText.EnableWindow(bReadOnly ? FALSE : TRUE);
  2500. GetDlgItem(IDC_KEYVALUE_HELP_GROUP)->EnableWindow(bReadOnly ? FALSE : TRUE);
  2501. //
  2502. // Hide or show all controls after and including "delete kv" button.
  2503. //
  2504. for (int i = 0; i < ARRAYSIZE(g_DumbEditControls); i++)
  2505. {
  2506. CWnd *pWnd = GetDlgItem(g_DumbEditControls[i]);
  2507. if ( pWnd )
  2508. pWnd->EnableWindow( !bReadOnly );
  2509. }
  2510. for (int i = 0; i < m_SmartControls.Count(); i++)
  2511. {
  2512. if (m_SmartControls.Element(i) != NULL)
  2513. {
  2514. m_SmartControls.Element(i)->EnableWindow(bReadOnly ? FALSE : TRUE);
  2515. }
  2516. }
  2517. }
  2518. //-----------------------------------------------------------------------------
  2519. // Purpose:
  2520. //-----------------------------------------------------------------------------
  2521. void COP_Entity::OnSmartedit(void)
  2522. {
  2523. m_iSortColumn = -1; // Go back to FGD sorting.
  2524. SetSmartedit(!m_bSmartedit);
  2525. m_bWantSmartedit = m_bSmartedit;
  2526. }
  2527. //-----------------------------------------------------------------------------
  2528. // Updates the UI to show properties for the given FGD class. If the class
  2529. // differs from the actual class of the edited objects, the UI is set to be
  2530. // read only until the Apply button is pressed.
  2531. //-----------------------------------------------------------------------------
  2532. void COP_Entity::UpdateDisplayClass(const char *szClass)
  2533. {
  2534. UpdateDisplayClass( pGD->ClassForName( szClass ) );
  2535. }
  2536. void COP_Entity::UpdateDisplayClass(GDclass *pClass)
  2537. {
  2538. // The outer check here is lame, but somewhere along the line, all the controls get enabled
  2539. // behind our backs. So verify that our state information is sane. If it's not, then we'll redo some state in here.
  2540. bool bForceRefresh = false;
  2541. if ( GetDlgItem(IDC_SMARTEDIT)->IsWindowEnabled() != (m_pDisplayClass != 0) )
  2542. bForceRefresh = true;
  2543. if ( pClass == m_pDisplayClass && !bForceRefresh )
  2544. {
  2545. return;
  2546. }
  2547. int lastNumPresentPropertiesCalls = m_nPresentPropertiesCalls;
  2548. bool bNeedsSetupForMode = true;
  2549. m_pDisplayClass = pClass;
  2550. // In case we're not allowed to present the properties yet, make sure nobody uses m_VarMap.
  2551. if ( !m_bAllowPresentProperties )
  2552. {
  2553. ClearVarList();
  2554. }
  2555. if (!m_pDisplayClass)
  2556. {
  2557. //
  2558. // Object has no known class - get rid of smartedit.
  2559. //
  2560. if (m_bSmartedit || bForceRefresh)
  2561. {
  2562. m_bSmartedit = true; // In case bForceRefresh was on, force it to refresh the controls.
  2563. SetSmartedit(false);
  2564. bNeedsSetupForMode = false;
  2565. }
  2566. GetDlgItem(IDC_SMARTEDIT)->EnableWindow(FALSE);
  2567. }
  2568. else
  2569. {
  2570. CEntityHelpDlg::SetEditGameClass(m_pDisplayClass);
  2571. //
  2572. // Known class - enable smartedit.
  2573. //
  2574. GetDlgItem(IDC_SMARTEDIT)->EnableWindow(TRUE);
  2575. if (bForceRefresh)
  2576. m_bSmartedit = !m_bWantSmartedit;
  2577. SetSmartedit(m_bWantSmartedit);
  2578. }
  2579. // No need to call PresentProperties an extra time if it was called because of SetSmartedit..
  2580. if ( bNeedsSetupForMode && (m_nPresentPropertiesCalls == lastNumPresentPropertiesCalls) )
  2581. {
  2582. PresentProperties();
  2583. }
  2584. SetReadOnly( ( m_pDisplayClass != m_pEditClass ) || ( m_bCanEdit == false ) );
  2585. }
  2586. //-----------------------------------------------------------------------------
  2587. //-----------------------------------------------------------------------------
  2588. bool COP_Entity::BrowseModels( char *szModelName, int length, int &nSkin )
  2589. {
  2590. bool bChanged = false;
  2591. if (pModelBrowser == NULL)
  2592. {
  2593. pModelBrowser = new CModelBrowser( GetMainWnd() );
  2594. }
  2595. else
  2596. {
  2597. pModelBrowser->Show();
  2598. }
  2599. pModelBrowser->SetModelName( szModelName );
  2600. pModelBrowser->SetSkin( nSkin );
  2601. if (pModelBrowser->DoModal() == IDOK)
  2602. {
  2603. pModelBrowser->GetModelName( szModelName, length );
  2604. pModelBrowser->GetSkin( nSkin );
  2605. bChanged = true;
  2606. }
  2607. pModelBrowser->Hide();
  2608. return bChanged;
  2609. }
  2610. //-----------------------------------------------------------------------------
  2611. //-----------------------------------------------------------------------------
  2612. void COP_Entity::BrowseTextures( const char *szFilter, bool bSprite )
  2613. {
  2614. // browse for textures
  2615. int iSel = GetCurVarListSelection();
  2616. GDinputvariable * pVar = GetVariableAt( iSel );
  2617. if ( pVar == NULL )
  2618. {
  2619. return;
  2620. }
  2621. // get current texture
  2622. char szInitialTexture[128];
  2623. m_pSmartControl->GetWindowText(szInitialTexture, 128);
  2624. // create a texture browser and set it to browse decals
  2625. CTextureBrowser *pBrowser = new CTextureBrowser(GetMainWnd());
  2626. // setup filter - if any
  2627. if( szFilter[0] != '\0' )
  2628. {
  2629. pBrowser->SetFilter( szFilter );
  2630. }
  2631. pBrowser->SetInitialTexture(szInitialTexture);
  2632. if (pBrowser->DoModal() == IDOK)
  2633. {
  2634. IEditorTexture *pTex = g_Textures.FindActiveTexture(pBrowser->m_cTextureWindow.szCurTexture);
  2635. char szName[MAX_PATH];
  2636. if (pTex)
  2637. {
  2638. pTex->GetShortName(szName);
  2639. }
  2640. else
  2641. {
  2642. szName[0] = '\0';
  2643. }
  2644. if (bSprite && g_pGameConfig->GetTextureFormat() == tfVMT)
  2645. {
  2646. char sprExt[4];
  2647. Q_snprintf(sprExt, 4, ".vmt");
  2648. Q_snprintf(szName, MAX_PATH, "%s.vmt", szName);
  2649. //Strcat is being zee stupido. I prolly have to strip the other string or something.
  2650. //Q_strcat( szName, sprExt, MAX_PATH );
  2651. }
  2652. m_pSmartControl->SetWindowText(szName);
  2653. // also set variable
  2654. m_kv.SetValue(pVar->GetName(), szName);
  2655. m_kvAdded.SetValue(pVar->GetName(), "1");
  2656. }
  2657. delete pBrowser;
  2658. }
  2659. void COP_Entity::OnChangeSmartcontrol(void)
  2660. {
  2661. if ( m_pSmartControl )
  2662. {
  2663. char szValue[KEYVALUE_MAX_VALUE_LENGTH];
  2664. m_pSmartControl->GetWindowText(szValue, sizeof(szValue));
  2665. InternalOnChangeSmartcontrol( szValue );
  2666. }
  2667. }
  2668. //-----------------------------------------------------------------------------
  2669. // Purpose: Used only in SmartEdit mode. Called when the contents of the value
  2670. // edit control change. This is where the edit control contents are
  2671. // converted to keyvalue data in SmartEdit mode.
  2672. //-----------------------------------------------------------------------------
  2673. void COP_Entity::InternalOnChangeSmartcontrol( const char *szValue )
  2674. {
  2675. //VPROF_BUDGET( "COP_Entity::OnChangeSmartcontrol", "Object Properties" );
  2676. //
  2677. // We only respond to this message when it is due to user input.
  2678. // Don't do anything if we're creating the smart control.
  2679. //
  2680. if (m_bIgnoreKVChange)
  2681. {
  2682. return;
  2683. }
  2684. m_LastSmartControlVarValue = szValue;
  2685. int iSel = GetCurVarListSelection();
  2686. GDinputvariable * pVar = GetVariableAt( iSel );
  2687. if ( pVar == NULL )
  2688. {
  2689. return;
  2690. }
  2691. char szKey[KEYVALUE_MAX_KEY_LENGTH];
  2692. V_strncpy(szKey, pVar->GetName(), sizeof( szKey ));
  2693. CString strValue = szValue;
  2694. if (pVar->GetType() == ivChoices)
  2695. {
  2696. //
  2697. // If a choicelist, change buffer to the string value of what we chose.
  2698. //
  2699. const char *pszValueString = pVar->ItemValueForString(szValue);
  2700. if (pszValueString != NULL)
  2701. {
  2702. strValue = pszValueString;
  2703. }
  2704. }
  2705. UpdateKeyValue(szKey, strValue);
  2706. if (m_bEnableControlUpdate)
  2707. {
  2708. // Update any controls that are displaying the same data as the edit control.
  2709. // This code should only be hit as a result of user input in the edit control!
  2710. if (pVar->GetType() == ivAngle)
  2711. {
  2712. // If they changed the "angles" key, update the main angles control.
  2713. if (!stricmp(pVar->GetName(), "angles"))
  2714. {
  2715. m_Angle.SetDifferent(false);
  2716. m_Angle.SetAngles(strValue, true);
  2717. }
  2718. // Otherwise update the Smart angles control.
  2719. else
  2720. {
  2721. m_SmartAngle.SetDifferent(false);
  2722. m_SmartAngle.SetAngles(strValue, true);
  2723. }
  2724. }
  2725. }
  2726. // We have to do this because it's an owner draw control and we're redoing the background colors.
  2727. // Normally, Windows will only invalidate the second column.
  2728. RECT rc;
  2729. if ( m_VarList.GetItemRect( iSel, &rc, LVIR_BOUNDS ) )
  2730. m_VarList.InvalidateRect( &rc, FALSE );
  2731. }
  2732. //-----------------------------------------------------------------------------
  2733. // Purpose: this function is called whenever the instance variable or value is changed
  2734. //-----------------------------------------------------------------------------
  2735. void COP_Entity::OnChangeInstanceVariableControl( void )
  2736. {
  2737. if ( m_pEditInstanceVariable && m_pEditInstanceValue )
  2738. {
  2739. char szVariable[ KEYVALUE_MAX_VALUE_LENGTH ], szValue[ KEYVALUE_MAX_VALUE_LENGTH ];
  2740. m_pEditInstanceVariable->GetWindowText( szVariable, sizeof( szVariable ) );
  2741. m_pEditInstanceValue->GetWindowText( szValue, sizeof( szValue ) );
  2742. if ( szValue[ 0 ] )
  2743. {
  2744. strcat( szVariable, " " );
  2745. strcat( szVariable, szValue );
  2746. }
  2747. int iSel = GetCurVarListSelection();
  2748. GDinputvariable * pVar = GetVariableAt( iSel );
  2749. if ( pVar == NULL )
  2750. {
  2751. return;
  2752. }
  2753. char szKey[ KEYVALUE_MAX_KEY_LENGTH ];
  2754. V_strncpy( szKey, pVar->GetName(), sizeof( szKey ) );
  2755. UpdateKeyValue( szKey, szVariable );
  2756. }
  2757. }
  2758. //-----------------------------------------------------------------------------
  2759. // Purpose: this function is called whenever the instance parameter is changed
  2760. //-----------------------------------------------------------------------------
  2761. void COP_Entity::OnChangeInstanceParmControl( void )
  2762. {
  2763. if ( m_pEditInstanceVariable && m_pComboInstanceParmType )
  2764. {
  2765. char szVariable[ KEYVALUE_MAX_VALUE_LENGTH ], szValue[ KEYVALUE_MAX_VALUE_LENGTH ];
  2766. m_pEditInstanceVariable->GetWindowText( szVariable, sizeof( szVariable ) );
  2767. int iSmartsel = m_pComboInstanceParmType->GetCurSel();
  2768. if ( iSmartsel != LB_ERR )
  2769. {
  2770. // found a selection - now get the text
  2771. m_pComboInstanceParmType->GetLBText( iSmartsel, szValue );
  2772. }
  2773. else
  2774. {
  2775. m_pComboInstanceParmType->GetWindowText( szValue, sizeof( szValue ) );
  2776. }
  2777. if ( szValue[ 0 ] )
  2778. {
  2779. strcat( szVariable, " " );
  2780. strcat( szVariable, szValue );
  2781. }
  2782. int iSel = GetCurVarListSelection();
  2783. GDinputvariable * pVar = GetVariableAt( iSel );
  2784. if ( pVar == NULL )
  2785. {
  2786. return;
  2787. }
  2788. char szKey[ KEYVALUE_MAX_KEY_LENGTH ];
  2789. V_strncpy( szKey, pVar->GetName(), sizeof( szKey ) );
  2790. UpdateKeyValue( szKey, szVariable );
  2791. }
  2792. }
  2793. //-----------------------------------------------------------------------------
  2794. // Purpose:
  2795. //-----------------------------------------------------------------------------
  2796. void COP_Entity::OnChangeSmartcontrolSel(void)
  2797. {
  2798. // update current value with this
  2799. int iSel = GetCurVarListSelection();
  2800. if ( !m_pDisplayClass )
  2801. return;
  2802. GDinputvariable * pVar = GetVariableAt( iSel );
  2803. if ( pVar == NULL )
  2804. {
  2805. return;
  2806. }
  2807. if ((pVar->GetType() != ivTargetSrc) &&
  2808. (pVar->GetType() != ivTargetDest) &&
  2809. (pVar->GetType() != ivTargetNameOrClass) &&
  2810. (pVar->GetType() != ivChoices) &&
  2811. (pVar->GetType() != ivNPCClass) &&
  2812. (pVar->GetType() != ivFilterClass) &&
  2813. (pVar->GetType() != ivPointEntityClass))
  2814. {
  2815. return;
  2816. }
  2817. CComboBox *pCombo = (CComboBox *)m_pSmartControl;
  2818. char szBuf[128];
  2819. // get current selection
  2820. int iSmartsel = pCombo->GetCurSel();
  2821. if (iSmartsel != LB_ERR)
  2822. {
  2823. // found a selection - now get the text
  2824. pCombo->GetLBText(iSmartsel, szBuf);
  2825. }
  2826. else
  2827. {
  2828. // just get the text from the combo box (no selection)
  2829. pCombo->GetWindowText(szBuf, 128);
  2830. }
  2831. if (pVar->GetType() == ivChoices)
  2832. {
  2833. const char *pszValue = pVar->ItemValueForString(szBuf);
  2834. if (pszValue != NULL)
  2835. {
  2836. strcpy(szBuf, pszValue);
  2837. }
  2838. }
  2839. m_LastSmartControlVarValue = szBuf;
  2840. m_kvAdded.SetValue(pVar->GetName(), "1");
  2841. m_kv.SetValue(pVar->GetName(), szBuf);
  2842. RefreshKVListValues( pVar->GetName() );
  2843. // We have to do this because it's an owner draw control and we're redoing the background colors.
  2844. // Normally, Windows will only invalidate the second column.
  2845. RECT rc;
  2846. if ( m_VarList.GetItemRect( iSel, &rc, LVIR_BOUNDS ) )
  2847. m_VarList.InvalidateRect( &rc, FALSE );
  2848. }
  2849. //-----------------------------------------------------------------------------
  2850. // Purpose:
  2851. // Input : cmd -
  2852. //-----------------------------------------------------------------------------
  2853. void COP_Entity::SetNextVar(int cmd)
  2854. {
  2855. int iSel = GetCurVarListSelection();
  2856. int nCount = m_VarList.GetItemCount();
  2857. if(iSel == LB_ERR)
  2858. return; // no!
  2859. iSel += cmd;
  2860. if(iSel == nCount)
  2861. --iSel;
  2862. if(iSel == -1)
  2863. ++iSel;
  2864. SetCurVarListSelection( iSel );
  2865. OnSelchangeKeyvalues();
  2866. }
  2867. //-----------------------------------------------------------------------------
  2868. // Purpose: Set flags page
  2869. //-----------------------------------------------------------------------------
  2870. void COP_Entity::SetFlagsPage( COP_Flags *pFlagsPage )
  2871. {
  2872. m_pFlagsPage = pFlagsPage;
  2873. }
  2874. //-----------------------------------------------------------------------------
  2875. // Purpose: Plays the sound
  2876. //-----------------------------------------------------------------------------
  2877. void COP_Entity::OnPlaySound(void)
  2878. {
  2879. if ( m_eEditType != ivSound && m_eEditType != ivScene )
  2880. return;
  2881. // Get the name of the sound or VCD.
  2882. char szCurrentSound[256];
  2883. m_pSmartControl->GetWindowText(szCurrentSound, 256);
  2884. if (!szCurrentSound[0])
  2885. return;
  2886. // Get rid of "scenes/" for scenes.
  2887. CString filename = szCurrentSound;
  2888. if ( m_eEditType == ivScene )
  2889. filename = StripDirPrefix( szCurrentSound, "scenes" );
  2890. // Now play the sound..
  2891. SoundType_t type;
  2892. int nIndex;
  2893. if ( g_Sounds.FindSoundByName( filename, &type, &nIndex ) )
  2894. g_Sounds.Play( type, nIndex );
  2895. }
  2896. // Filesystem dialog module wrappers.
  2897. CSysModule *g_pFSDialogModule = 0;
  2898. CreateInterfaceFn g_FSDialogFactory = 0;
  2899. void LoadFileSystemDialogModule()
  2900. {
  2901. Assert( !g_pFSDialogModule );
  2902. // Load the module with the file system open dialog.
  2903. const char *pDLLName = "FileSystemOpenDialog.dll";
  2904. g_pFSDialogModule = Sys_LoadModule( pDLLName );
  2905. if ( !g_pFSDialogModule ||
  2906. (g_FSDialogFactory = Sys_GetFactory( g_pFSDialogModule )) == NULL
  2907. )
  2908. {
  2909. if ( g_pFSDialogModule )
  2910. {
  2911. Sys_UnloadModule( g_pFSDialogModule );
  2912. g_pFSDialogModule = 0;
  2913. }
  2914. char str[512];
  2915. Q_snprintf( str, sizeof( str ), "Can't load %s.\n", pDLLName );
  2916. AfxMessageBox( str, MB_OK );
  2917. }
  2918. }
  2919. void UnloadFileSystemDialogModule()
  2920. {
  2921. if ( g_pFSDialogModule )
  2922. {
  2923. Sys_UnloadModule( g_pFSDialogModule );
  2924. g_pFSDialogModule = 0;
  2925. }
  2926. }
  2927. //-----------------------------------------------------------------------------
  2928. // Purpose: Brings up the file browser in the appropriate default directory
  2929. // based on the type of file being browsed for.
  2930. //-----------------------------------------------------------------------------
  2931. void COP_Entity::OnBrowse(void)
  2932. {
  2933. // handle browsing for .fgd "material" type
  2934. if( m_eEditType == ivMaterial )
  2935. {
  2936. BrowseTextures( "\0" );
  2937. return;
  2938. }
  2939. if ( m_eEditType == ivStudioModel && Options.IsVGUIModelBrowserEnabled() )
  2940. {
  2941. char szCurrentModel[512];
  2942. char szCurrentSkin[512];
  2943. const char *result = m_kv.GetValue( "skin" );
  2944. int nSkin = ( result ) ? Q_atoi( result ) : 0;
  2945. m_pSmartControl->GetWindowText( szCurrentModel, sizeof(szCurrentModel) );
  2946. if ( BrowseModels( szCurrentModel, sizeof(szCurrentModel), nSkin ) )
  2947. {
  2948. // model was changed
  2949. m_pSmartControl->SetWindowText( szCurrentModel );
  2950. UpdateKeyValue("skin", itoa( nSkin, szCurrentSkin, 10 ));
  2951. }
  2952. return;
  2953. }
  2954. // handle browsing for .fgd "decal" type
  2955. if(m_eEditType == ivDecal)
  2956. {
  2957. if (g_pGameConfig->GetTextureFormat() == tfVMT)
  2958. {
  2959. BrowseTextures("decals/");
  2960. }
  2961. else
  2962. {
  2963. BrowseTextures("{");
  2964. }
  2965. return;
  2966. }
  2967. if ( m_eEditType == ivSprite )
  2968. {
  2969. BrowseTextures( "sprites/", true);
  2970. return;
  2971. }
  2972. if ( m_eEditType == ivInstanceFile )
  2973. {
  2974. OnBrowseInstance();
  2975. return;
  2976. }
  2977. char *pszInitialDir = 0;
  2978. // Instantiate a dialog.
  2979. if ( !g_FSDialogFactory )
  2980. return;
  2981. IFileSystemOpenDialog *pDlg;
  2982. pDlg = (IFileSystemOpenDialog*)g_FSDialogFactory( FILESYSTEMOPENDIALOG_VERSION, NULL );
  2983. if ( !pDlg )
  2984. {
  2985. char str[512];
  2986. Q_snprintf( str, sizeof( str ), "Can't create %s interface.", FILESYSTEMOPENDIALOG_VERSION );
  2987. AfxMessageBox( str, MB_OK );
  2988. return;
  2989. }
  2990. pDlg->Init( g_Factory, NULL );
  2991. const char *pPathID = "GAME";
  2992. //
  2993. // Based on the type of file that we are picking, set up the default extension,
  2994. // default directory, and filters. Each type of file remembers its last directory.
  2995. //
  2996. switch (m_eEditType)
  2997. {
  2998. case ivStudioModel:
  2999. {
  3000. static char szInitialDir[MAX_PATH] = "models";
  3001. pszInitialDir = szInitialDir;
  3002. pDlg->AddFileMask( "*.jpg" );
  3003. pDlg->AddFileMask( "*.mdl" );
  3004. pDlg->SetInitialDir( pszInitialDir, pPathID );
  3005. pDlg->SetFilterMdlAndJpgFiles( true );
  3006. break;
  3007. }
  3008. case ivSound:
  3009. {
  3010. CString currentValue;
  3011. m_pSmartControl->GetWindowText( currentValue );
  3012. CSoundBrowser soundDlg( currentValue );
  3013. if ( soundDlg.m_SoundType != SOUND_TYPE_RAW && soundDlg.m_SoundType != SOUND_TYPE_GAMESOUND )
  3014. soundDlg.m_SoundType = SOUND_TYPE_GAMESOUND;
  3015. int nRet = soundDlg.DoModal();
  3016. if ( nRet == IDOK )
  3017. {
  3018. m_pSmartControl->SetWindowText(soundDlg.GetSelectedSound());
  3019. }
  3020. goto Cleanup;
  3021. }
  3022. case ivScene:
  3023. {
  3024. CString currentValue;
  3025. m_pSmartControl->GetWindowText( currentValue );
  3026. CString stripped = StripDirPrefix( currentValue, "scenes" );
  3027. CSoundBrowser soundDlg( stripped );
  3028. soundDlg.m_SoundType = SOUND_TYPE_SCENE;
  3029. int nRet = soundDlg.DoModal();
  3030. if ( nRet == IDOK )
  3031. {
  3032. m_pSmartControl->SetWindowText(CString("scenes\\") + soundDlg.GetSelectedSound());
  3033. }
  3034. goto Cleanup;
  3035. }
  3036. default:
  3037. {
  3038. pDlg->AddFileMask( "*.*" );
  3039. pDlg->SetInitialDir( ".", pPathID );
  3040. break;
  3041. }
  3042. }
  3043. //
  3044. // If they picked a file and hit OK, put everything after the last backslash
  3045. // into the SmartEdit control. If there is no backslash, put the whole filename.
  3046. //
  3047. int ret;
  3048. if ( 1/*g_pFullFileSystem->IsSteam()*/ || CommandLine()->FindParm( "-NewDialogs" ) )
  3049. ret = pDlg->DoModal();
  3050. else
  3051. ret = pDlg->DoModal_WindowsDialog();
  3052. if ( ret == IDOK )
  3053. {
  3054. //
  3055. // Save the default folder for next time.
  3056. //
  3057. pDlg->GetFilename( pszInitialDir, MAX_PATH );
  3058. char *pchSlash = strrchr(pszInitialDir, '\\');
  3059. if (pchSlash != NULL)
  3060. {
  3061. *pchSlash = '\0';
  3062. }
  3063. if (m_pSmartControl != NULL)
  3064. {
  3065. //
  3066. // Reverse the slashes, because the engine expects them that way.
  3067. //
  3068. char szTemp[MAX_PATH];
  3069. pDlg->GetFilename( szTemp, sizeof( szTemp ) );
  3070. for (unsigned int i = 0; i < strlen(szTemp); i++)
  3071. {
  3072. if (szTemp[i] == '\\')
  3073. {
  3074. szTemp[i] = '/';
  3075. }
  3076. }
  3077. m_pSmartControl->SetWindowText(szTemp);
  3078. }
  3079. }
  3080. Cleanup:;
  3081. pDlg->Release();
  3082. }
  3083. //-----------------------------------------------------------------------------
  3084. // Purpose: this function will display a file dialog to locate an instance vmf.
  3085. //-----------------------------------------------------------------------------
  3086. void COP_Entity::OnBrowseInstance(void)
  3087. {
  3088. const char *pszMapPath = "\\maps\\";
  3089. CString MapFileName;
  3090. char FileName[ MAX_PATH ];
  3091. CString currentValue;
  3092. CMapDoc *activeDoc = CMapDoc::GetActiveMapDoc();
  3093. m_pSmartControl->GetWindowText( currentValue );
  3094. MapFileName = activeDoc->GetPathName();
  3095. CMapInstance::DeterminePath( MapFileName, currentValue, FileName );
  3096. CFileDialog dlg(
  3097. true, // open dialog?
  3098. ".vmf", // default file extension
  3099. FileName, // initial filename
  3100. OFN_ENABLESIZING, // flags
  3101. "Valve Map Files (*.vmf)|*.vmf||",
  3102. this );
  3103. if ( dlg.DoModal() == IDOK )
  3104. {
  3105. strcpy( FileName, dlg.GetPathName() );
  3106. V_RemoveDotSlashes( FileName );
  3107. V_FixDoubleSlashes( FileName );
  3108. V_strlower( FileName );
  3109. char *pos = strstr( FileName, pszMapPath );
  3110. if ( pos )
  3111. {
  3112. pos += strlen( pszMapPath );
  3113. *( pos - 1 ) = 0;
  3114. }
  3115. else if ( pos == NULL )
  3116. {
  3117. const char *pszInstancePath = CMapInstance::GetInstancePath();
  3118. if ( pszInstancePath[ 0 ] != 0 )
  3119. {
  3120. pos = strstr( FileName, pszInstancePath );
  3121. if ( pos )
  3122. {
  3123. pos += strlen( pszInstancePath );
  3124. *( pos - 1 ) = 0;
  3125. }
  3126. }
  3127. }
  3128. if ( pos )
  3129. {
  3130. m_pSmartControl->SetWindowText( pos );
  3131. }
  3132. }
  3133. }
  3134. //-----------------------------------------------------------------------------
  3135. // Purpose:
  3136. //-----------------------------------------------------------------------------
  3137. void COP_Entity::OnCopy(void)
  3138. {
  3139. // copy entity keyvalues
  3140. kvClipboard.RemoveAll();
  3141. bKvClipEmpty = FALSE;
  3142. for ( int i=m_kv.GetFirst(); i != m_kv.GetInvalidIndex(); i=m_kv.GetNext( i ) )
  3143. {
  3144. if (stricmp(m_kv.GetKey(i), "origin"))
  3145. {
  3146. kvClipboard.SetValue(m_kv.GetKey(i), m_kv.GetValue(i));
  3147. }
  3148. }
  3149. CString strClass = m_cClasses.GetCurrentItem();
  3150. kvClipboard.SetValue("xxxClassxxx", strClass);
  3151. }
  3152. //-----------------------------------------------------------------------------
  3153. // Purpose:
  3154. //-----------------------------------------------------------------------------
  3155. void COP_Entity::OnEntityHelp(void)
  3156. {
  3157. CEntityHelpDlg::ShowEntityHelpDialog();
  3158. CEntityHelpDlg::SetEditGameClass(m_pDisplayClass);
  3159. }
  3160. //-----------------------------------------------------------------------------
  3161. // Purpose:
  3162. //-----------------------------------------------------------------------------
  3163. void COP_Entity::OnKillfocusKey(void)
  3164. {
  3165. if (!m_bChangingKeyName)
  3166. return;
  3167. m_bChangingKeyName = false;
  3168. CString strOutput;
  3169. m_cKey.GetWindowText(strOutput);
  3170. if (strOutput.IsEmpty())
  3171. {
  3172. AfxMessageBox("Use the delete button to remove key/value pairs.");
  3173. return;
  3174. }
  3175. strOutput.MakeLower();
  3176. // No change
  3177. if (strOutput == m_szOldKeyName)
  3178. return;
  3179. char szSaveValue[KEYVALUE_MAX_VALUE_LENGTH];
  3180. memset(szSaveValue, 0, sizeof(szSaveValue));
  3181. V_strcpy_safe(szSaveValue, m_kv.GetValue(m_szOldKeyName, NULL));
  3182. int iSel = GetCurVarListSelection();
  3183. if (iSel == LB_ERR)
  3184. return;
  3185. // Get rid of the old key.
  3186. CString strBuf;
  3187. strBuf = (CString)(const char*)m_VarList.GetItemData(iSel);
  3188. m_kv.RemoveKey( strBuf ); // remove from local kv storage
  3189. m_VarList.DeleteItem(iSel);
  3190. // Add a new key with the new keyname + old value.
  3191. CNewKeyValue newkv;
  3192. newkv.m_Key = strOutput;
  3193. newkv.m_Value = szSaveValue;
  3194. m_kvAdded.SetValue(newkv.m_Key, "1");
  3195. m_kv.SetValue(newkv.m_Key, newkv.m_Value);
  3196. PresentProperties();
  3197. // Select this property.
  3198. SetCurVarListSelection( GetKeyValueRowByShortName( newkv.m_Key ) );
  3199. OnSelchangeKeyvalues();
  3200. }
  3201. //-----------------------------------------------------------------------------
  3202. // Does the dirty marking deed
  3203. //-----------------------------------------------------------------------------
  3204. void COP_Entity::PerformMark( const char *szTargetName, bool bClear, bool bNameOrClass )
  3205. {
  3206. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  3207. if (pDoc != NULL)
  3208. {
  3209. if (szTargetName[0] != '\0')
  3210. {
  3211. CMapEntityList Found;
  3212. pDoc->FindEntitiesByName(Found, szTargetName, false);
  3213. if ((Found.Count() == 0) && bNameOrClass)
  3214. {
  3215. pDoc->FindEntitiesByClassName(Found, szTargetName, false);
  3216. }
  3217. if (Found.Count() != 0)
  3218. {
  3219. CMapObjectList Select;
  3220. FOR_EACH_OBJ( Found, pos )
  3221. {
  3222. CMapEntity *pEntity = Found.Element(pos);
  3223. Select.AddToTail(pEntity);
  3224. }
  3225. if ( bClear )
  3226. {
  3227. // clear & safe previous selection
  3228. pDoc->SelectObjectList(&Select);
  3229. }
  3230. else
  3231. {
  3232. // don't save changes and add object to selection
  3233. pDoc->SelectObjectList(&Select, scSelect );
  3234. }
  3235. pDoc->Center2DViewsOnSelection();
  3236. }
  3237. else
  3238. {
  3239. MessageBox("No entities were found with that targetname.", "No entities found", MB_ICONINFORMATION | MB_OK);
  3240. }
  3241. }
  3242. }
  3243. }
  3244. //-----------------------------------------------------------------------------
  3245. // Purpose: Marks all entities whose targetnames match the currently selected
  3246. // key value.
  3247. //-----------------------------------------------------------------------------
  3248. void COP_Entity::OnMark(void)
  3249. {
  3250. int iSel = GetCurVarListSelection();
  3251. GDinputvariable * pVar = GetVariableAt( iSel );
  3252. if ( pVar == NULL )
  3253. {
  3254. return;
  3255. }
  3256. char szTargetName[MAX_IO_NAME_LEN];
  3257. m_pSmartControl->GetWindowText(szTargetName, sizeof(szTargetName));
  3258. bool bNameOrClass = false;
  3259. if (pVar && (pVar->GetType() == ivTargetNameOrClass))
  3260. {
  3261. bNameOrClass = true;
  3262. }
  3263. PerformMark( szTargetName, true, bNameOrClass );
  3264. }
  3265. //-----------------------------------------------------------------------------
  3266. // Add the mark to the selection
  3267. //-----------------------------------------------------------------------------
  3268. void COP_Entity::OnMarkAndAdd(void)
  3269. {
  3270. int iSel = GetCurVarListSelection();
  3271. GDinputvariable * pVar = GetVariableAt( iSel );
  3272. if ( pVar == NULL )
  3273. {
  3274. return;
  3275. }
  3276. // Build a temporary list of all the currently selected objects because this
  3277. // process will change the selected objects.
  3278. CMapEntity **pTemp = (CMapEntity**)stackalloc( m_pObjectList->Count() * sizeof(CMapEntity*) );
  3279. CUtlVector<CMapEntity*> temp( pTemp, m_pObjectList->Count() );
  3280. FOR_EACH_OBJ( *m_pObjectList, pos )
  3281. {
  3282. CMapEntity *pEntity = static_cast<CMapEntity *>(m_pObjectList->Element(pos));
  3283. temp.AddToTail( pEntity );
  3284. }
  3285. // Mark all the entities referred to by the current keyvalue in the selected entities.
  3286. bool bNameOrClass = false;
  3287. if (pVar && (pVar->GetType() == ivTargetNameOrClass))
  3288. {
  3289. bNameOrClass = true;
  3290. }
  3291. for ( int i = 0; i < temp.Count(); ++i )
  3292. {
  3293. const char *pTargetName = temp[i]->GetKeyValue( pVar->GetName() );
  3294. if ( pTargetName )
  3295. {
  3296. PerformMark( pTargetName, false, bNameOrClass );
  3297. }
  3298. }
  3299. }
  3300. //-----------------------------------------------------------------------------
  3301. // Purpose:
  3302. //-----------------------------------------------------------------------------
  3303. void COP_Entity::OnPaste(void)
  3304. {
  3305. if(bKvClipEmpty)
  3306. return;
  3307. CString str;
  3308. GetCurKey(str);
  3309. // copy entity keyvalues
  3310. for (int i = kvClipboard.GetFirst(); i != kvClipboard.GetInvalidIndex(); i=kvClipboard.GetNext( i ) )
  3311. {
  3312. if (!strcmp(kvClipboard.GetKey(i), "xxxClassxxx"))
  3313. {
  3314. m_cClasses.SelectItem( kvClipboard.GetValue(i) );
  3315. UpdateEditClass(kvClipboard.GetValue(i), false);
  3316. UpdateDisplayClass(kvClipboard.GetValue(i));
  3317. continue;
  3318. }
  3319. m_kv.SetValue(kvClipboard.GetKey(i), kvClipboard.GetValue(i));
  3320. m_kvAdded.SetValue(kvClipboard.GetKey(i), "1");
  3321. }
  3322. PresentProperties();
  3323. SetCurKey(str);
  3324. }
  3325. //-----------------------------------------------------------------------------
  3326. // Purpose: For the given key name, builds a list of faces that are common
  3327. // to all entitie being edited and a list of faces that are found in at
  3328. // least one entity being edited.
  3329. // Input : FullFaces -
  3330. // PartialFaces -
  3331. // pszKey - the name of the key.
  3332. //-----------------------------------------------------------------------------
  3333. void COP_Entity::GetFaceIDListsForKey(CMapFaceIDList &FullFaces, CMapFaceIDList &PartialFaces, const char *pszKey)
  3334. {
  3335. CMapWorld *pWorld = GetActiveWorld();
  3336. if ((m_pObjectList != NULL) && (pWorld != NULL))
  3337. {
  3338. bool bFirst = true;
  3339. FOR_EACH_OBJ( *m_pObjectList, pos )
  3340. {
  3341. CMapClass *pObject = m_pObjectList->Element(pos);
  3342. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  3343. if (pEntity != NULL)
  3344. {
  3345. const char *pszValue = pEntity->GetKeyValue(pszKey);
  3346. if (bFirst)
  3347. {
  3348. pWorld->FaceID_StringToFaceIDLists(&FullFaces, NULL, pszValue);
  3349. bFirst = false;
  3350. }
  3351. else
  3352. {
  3353. CMapFaceIDList TempFaces;
  3354. pWorld->FaceID_StringToFaceIDLists(&TempFaces, NULL, pszValue);
  3355. CMapFaceIDList TempFullFaces = FullFaces;
  3356. FullFaces.RemoveAll();
  3357. TempFaces.Intersect(TempFullFaces, FullFaces, PartialFaces);
  3358. }
  3359. }
  3360. }
  3361. }
  3362. }
  3363. //-----------------------------------------------------------------------------
  3364. // Purpose: For the given key name, builds a list of faces that are common
  3365. // to all entitie being edited and a list of faces that are found in at
  3366. // least one entity being edited.
  3367. // Input : FullFaces -
  3368. // PartialFaces -
  3369. // pszKey - the name of the key.
  3370. //-----------------------------------------------------------------------------
  3371. // dvs: FIXME: try to eliminate this function
  3372. void COP_Entity::GetFaceListsForKey(CMapFaceList &FullFaces, CMapFaceList &PartialFaces, const char *pszKey)
  3373. {
  3374. CMapWorld *pWorld = GetActiveWorld();
  3375. if ((m_pObjectList != NULL) && (pWorld != NULL))
  3376. {
  3377. bool bFirst = true;
  3378. FOR_EACH_OBJ( *m_pObjectList, pos )
  3379. {
  3380. CMapClass *pObject = m_pObjectList->Element(pos);
  3381. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  3382. if (pEntity != NULL)
  3383. {
  3384. const char *pszValue = pEntity->GetKeyValue(pszKey);
  3385. if (bFirst)
  3386. {
  3387. pWorld->FaceID_StringToFaceLists(&FullFaces, NULL, pszValue);
  3388. bFirst = false;
  3389. }
  3390. else
  3391. {
  3392. CMapFaceList TempFaces;
  3393. pWorld->FaceID_StringToFaceLists(&TempFaces, NULL, pszValue);
  3394. CMapFaceList TempFullFaces = FullFaces;
  3395. FullFaces.RemoveAll();
  3396. TempFaces.Intersect(TempFullFaces, FullFaces, PartialFaces);
  3397. }
  3398. }
  3399. }
  3400. }
  3401. }
  3402. //-----------------------------------------------------------------------------
  3403. // Purpose:
  3404. //-----------------------------------------------------------------------------
  3405. void COP_Entity::OnPickFaces(void)
  3406. {
  3407. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  3408. Assert(pDoc != NULL);
  3409. if (pDoc == NULL)
  3410. {
  3411. return;
  3412. }
  3413. CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_FACES);
  3414. Assert(pButton != NULL);
  3415. if (pButton != NULL)
  3416. {
  3417. if (pButton->GetCheck())
  3418. {
  3419. int nSel = GetCurVarListSelection();
  3420. Assert(nSel != LB_ERR);
  3421. if (nSel != LB_ERR )
  3422. {
  3423. GDinputvariable * pVar = GetVariableAt( nSel );
  3424. if ( pVar != NULL )
  3425. {
  3426. Assert((pVar->GetType() == ivSideList) || (pVar->GetType() == ivSide));
  3427. // FACEID TODO: make the faces highlight even when the tool is not active
  3428. //
  3429. // Build the list of faces that are in all selected entities, and a list
  3430. // of faces that are in at least one selected entity, so that we can do
  3431. // multiselect properly.
  3432. //
  3433. CMapFaceList FullFaces;
  3434. CMapFaceList PartialFaces;
  3435. GetFaceListsForKey(FullFaces, PartialFaces, pVar->GetName());
  3436. // Save the old tool so we can reset to the correct tool when we stop picking.
  3437. m_ToolPrePick = ToolManager()->GetActiveToolID();
  3438. m_bPicking = true;
  3439. //
  3440. // Activate the face picker tool. It will handle the picking of faces.
  3441. //
  3442. ToolManager()->SetTool(TOOL_PICK_FACE);
  3443. CToolPickFace *pTool = (CToolPickFace *)ToolManager()->GetToolForID(TOOL_PICK_FACE);
  3444. pTool->SetSelectedFaces(FullFaces, PartialFaces);
  3445. m_PickFaceTarget.AttachEntityDlg(this);
  3446. pTool->Attach(&m_PickFaceTarget);
  3447. pTool->AllowMultiSelect(pVar->GetType() == ivSideList);
  3448. }
  3449. }
  3450. }
  3451. else
  3452. {
  3453. //
  3454. // Get the face IDs from the face picker tool.
  3455. //
  3456. m_bPicking = false;
  3457. CToolPickFace *pTool = (CToolPickFace *)ToolManager()->GetToolForID(TOOL_PICK_FACE);
  3458. UpdatePickFaceText(pTool);
  3459. ToolManager()->SetTool(TOOL_POINTER);
  3460. }
  3461. }
  3462. }
  3463. //-----------------------------------------------------------------------------
  3464. // Purpose:
  3465. //-----------------------------------------------------------------------------
  3466. void COP_Entity::OnPickAngles(void)
  3467. {
  3468. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  3469. Assert(pDoc != NULL);
  3470. if (pDoc == NULL)
  3471. {
  3472. return;
  3473. }
  3474. CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_ANGLES);
  3475. Assert(pButton != NULL);
  3476. if (pButton != NULL)
  3477. {
  3478. if (pButton->GetCheck())
  3479. {
  3480. int nSel = GetCurVarListSelection();
  3481. Assert(nSel != LB_ERR);
  3482. if (nSel != LB_ERR)
  3483. {
  3484. GDinputvariable * pVar = GetVariableAt( nSel );
  3485. if ( pVar != NULL )
  3486. {
  3487. Assert(pVar->GetType() == ivAngle);
  3488. // Save the old tool so we can reset to the correct tool when we stop picking.
  3489. m_ToolPrePick = ToolManager()->GetActiveToolID();
  3490. m_bPicking = true;
  3491. //
  3492. // Activate the angles picker tool.
  3493. //
  3494. CToolPickAngles *pTool = (CToolPickAngles *)ToolManager()->GetToolForID(TOOL_PICK_ANGLES);
  3495. m_PickAnglesTarget.AttachEntityDlg(this);
  3496. pTool->Attach(&m_PickAnglesTarget);
  3497. ToolManager()->SetTool(TOOL_PICK_ANGLES);
  3498. }
  3499. }
  3500. }
  3501. else
  3502. {
  3503. StopPicking();
  3504. }
  3505. }
  3506. }
  3507. //-----------------------------------------------------------------------------
  3508. // Purpose:
  3509. //-----------------------------------------------------------------------------
  3510. void COP_Entity::OnPickEntity(void)
  3511. {
  3512. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  3513. Assert(pDoc != NULL);
  3514. if (pDoc == NULL)
  3515. {
  3516. return;
  3517. }
  3518. CButton *pButton = (CButton *)GetDlgItem(IDC_PICK_ENTITY);
  3519. Assert(pButton != NULL);
  3520. if (pButton != NULL)
  3521. {
  3522. if (pButton->GetCheck())
  3523. {
  3524. int nSel = GetCurVarListSelection();
  3525. Assert(nSel != LB_ERR);
  3526. if (nSel != LB_ERR)
  3527. {
  3528. GDinputvariable * pVar = GetVariableAt( nSel );
  3529. if ( pVar != NULL )
  3530. {
  3531. // Save the old tool so we can reset to the correct tool when we stop picking.
  3532. m_ToolPrePick = ToolManager()->GetActiveToolID();
  3533. m_bPicking = true;
  3534. //
  3535. // Activate the entity picker tool.
  3536. //
  3537. CToolPickEntity *pTool = (CToolPickEntity *)ToolManager()->GetToolForID(TOOL_PICK_ENTITY);
  3538. m_PickEntityTarget.AttachEntityDlg(this);
  3539. switch (pVar->GetType())
  3540. {
  3541. case ivTargetDest:
  3542. case ivTargetNameOrClass:
  3543. case ivTargetSrc:
  3544. {
  3545. m_PickEntityTarget.SetKeyToRetrieve("targetname");
  3546. break;
  3547. }
  3548. case ivNodeDest:
  3549. {
  3550. m_PickEntityTarget.SetKeyToRetrieve("nodeid");
  3551. break;
  3552. }
  3553. default:
  3554. {
  3555. Assert(false);
  3556. }
  3557. }
  3558. pTool->Attach(&m_PickEntityTarget);
  3559. ToolManager()->SetTool(TOOL_PICK_ENTITY);
  3560. }
  3561. }
  3562. }
  3563. else
  3564. {
  3565. StopPicking();
  3566. }
  3567. }
  3568. }
  3569. //-----------------------------------------------------------------------------
  3570. // Purpose: Load custom colors
  3571. //-----------------------------------------------------------------------------
  3572. void COP_Entity::LoadCustomColors()
  3573. {
  3574. if (m_bCustomColorsLoaded)
  3575. return;
  3576. char szRootDir[MAX_PATH];
  3577. char szFullPath[MAX_PATH];
  3578. APP()->GetDirectory(DIR_PROGRAM, szRootDir);
  3579. Q_MakeAbsolutePath( szFullPath, MAX_PATH, "customcolors.dat", szRootDir );
  3580. std::ifstream file(szFullPath, std::ios::in | std::ios::binary);
  3581. if(!file.is_open())
  3582. {
  3583. //Nothing to load, but don't keep trying every time the dialog pops up.
  3584. m_bCustomColorsLoaded = true;
  3585. return;
  3586. }
  3587. file.read((char*)CustomColors, sizeof(CustomColors));
  3588. file.close();
  3589. m_bCustomColorsLoaded = true;
  3590. }
  3591. //-----------------------------------------------------------------------------
  3592. // Purpose: Save custom colors out to a file
  3593. //-----------------------------------------------------------------------------
  3594. void COP_Entity::SaveCustomColors()
  3595. {
  3596. char szRootDir[MAX_PATH];
  3597. char szFullPath[MAX_PATH];
  3598. APP()->GetDirectory(DIR_PROGRAM, szRootDir);
  3599. Q_MakeAbsolutePath( szFullPath, MAX_PATH, "customcolors.dat", szRootDir );
  3600. std::ofstream file( szFullPath, std::ios::out | std::ios::binary );
  3601. file.write((char*)CustomColors, sizeof(CustomColors));
  3602. file.close();
  3603. }
  3604. //-----------------------------------------------------------------------------
  3605. // Purpose: this function will attempt to look up a variable from the variable map
  3606. // Input : index - non-negative, it is the index into the variable map.
  3607. // -1 = invalid
  3608. // negative starting at the INSTANCE_VAR_MAP_START indicates it is a
  3609. // custom instance parameter.
  3610. // Output :
  3611. //-----------------------------------------------------------------------------
  3612. GDinputvariable *COP_Entity::GetVariableAt( int index )
  3613. {
  3614. if ( m_VarMap[ index ] == -1 )
  3615. {
  3616. return NULL;
  3617. }
  3618. if ( m_VarMap[ index ] <= INSTANCE_VAR_MAP_START )
  3619. {
  3620. return m_InstanceParmData[ INSTANCE_VAR_MAP_START - m_VarMap[ index ] ].m_ParmVariable;
  3621. }
  3622. return m_pDisplayClass->GetVariableAt( m_VarMap[ index ] );
  3623. }
  3624. //-----------------------------------------------------------------------------
  3625. // Purpose:
  3626. //-----------------------------------------------------------------------------
  3627. void COP_Entity::OnPickColor(void)
  3628. {
  3629. int iSel = GetCurVarListSelection();
  3630. GDinputvariable * pVar = GetVariableAt( iSel );
  3631. if ( pVar == NULL )
  3632. {
  3633. return;
  3634. }
  3635. if (!m_bCustomColorsLoaded)
  3636. LoadCustomColors();
  3637. // find current color
  3638. COLORREF clr;
  3639. BYTE r = 255, g = 255, b = 255;
  3640. DWORD brightness = 0xffffffff;
  3641. char szTmp[128], *pTmp;
  3642. m_pSmartControl->GetWindowText(szTmp, sizeof szTmp);
  3643. pTmp = strtok(szTmp, " ");
  3644. int iCurToken = 0;
  3645. while(pTmp)
  3646. {
  3647. if(pTmp[0])
  3648. {
  3649. if(iCurToken == 3)
  3650. {
  3651. brightness = atol(pTmp);
  3652. }
  3653. else if(pVar->GetType() == ivColor255)
  3654. {
  3655. if(iCurToken == 0)
  3656. r = BYTE(atol(pTmp));
  3657. if(iCurToken == 1)
  3658. g = BYTE(atol(pTmp));
  3659. if(iCurToken == 2)
  3660. b = BYTE(atol(pTmp));
  3661. }
  3662. else
  3663. {
  3664. if(iCurToken == 0)
  3665. r = BYTE(atof(pTmp) * 255.f);
  3666. if(iCurToken == 1)
  3667. g = BYTE(atof(pTmp) * 255.f);
  3668. if(iCurToken == 2)
  3669. b = BYTE(atof(pTmp) * 255.f);
  3670. }
  3671. ++iCurToken;
  3672. }
  3673. pTmp = strtok(NULL, " ");
  3674. }
  3675. clr = RGB(r, g, b);
  3676. CColorDialog dlg(clr, CC_FULLOPEN);
  3677. dlg.m_cc.lpCustColors = CustomColors;
  3678. if(dlg.DoModal() != IDOK)
  3679. return;
  3680. SaveCustomColors();
  3681. r = GetRValue(dlg.m_cc.rgbResult);
  3682. g = GetGValue(dlg.m_cc.rgbResult);
  3683. b = GetBValue(dlg.m_cc.rgbResult);
  3684. // set back in field
  3685. if(pVar->GetType() == ivColor255)
  3686. {
  3687. sprintf(szTmp, "%d %d %d", r, g, b);
  3688. }
  3689. else
  3690. {
  3691. sprintf(szTmp, "%.3f %.3f %.3f", float(r) / 255.f,
  3692. float(g) / 255.f, float(b) / 255.f);
  3693. }
  3694. if(brightness != 0xffffffff)
  3695. sprintf(szTmp + strlen(szTmp), " %d", brightness);
  3696. m_pSmartControl->SetWindowText(szTmp);
  3697. RefreshKVListValues();
  3698. }
  3699. //-----------------------------------------------------------------------------
  3700. // Purpose:
  3701. //-----------------------------------------------------------------------------
  3702. void COP_Entity::OnSetfocusKey(void)
  3703. {
  3704. m_cKey.GetWindowText(m_szOldKeyName);
  3705. if (m_szOldKeyName.IsEmpty())
  3706. return;
  3707. m_szOldKeyName.MakeLower();
  3708. m_bChangingKeyName = true;
  3709. }
  3710. //-----------------------------------------------------------------------------
  3711. // Purpose: Called whenever we are hidden or shown.
  3712. // Input : bShow - TRUE if we are being shown, FALSE if we are being hidden.
  3713. // nStatus -
  3714. //-----------------------------------------------------------------------------
  3715. void COP_Entity::OnShowPropertySheet(BOOL bShow, UINT nStatus)
  3716. {
  3717. if (bShow)
  3718. {
  3719. //
  3720. // Being shown. Make sure the data in the smartedit control is correct.
  3721. //
  3722. OnSelchangeKeyvalues();
  3723. }
  3724. else
  3725. {
  3726. //
  3727. // Being hidden. Abort face picking if we are doing so.
  3728. //
  3729. StopPicking();
  3730. }
  3731. }
  3732. //-----------------------------------------------------------------------------
  3733. // Purpose:
  3734. // Input : nChar -
  3735. // nRepCnt -
  3736. // nFlags -
  3737. //-----------------------------------------------------------------------------
  3738. void CMyEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
  3739. {
  3740. CEdit::OnChar(nChar, nRepCnt, nFlags);
  3741. return;
  3742. if(nChar == 1) // ctrl+a
  3743. {
  3744. m_pParent->SetNextVar(1);
  3745. }
  3746. else if(nChar == 11) // ctrl+q
  3747. {
  3748. m_pParent->SetNextVar(-1);
  3749. }
  3750. else
  3751. {
  3752. CEdit::OnChar(nChar, nRepCnt, nFlags);
  3753. }
  3754. }
  3755. //-----------------------------------------------------------------------------
  3756. // Purpose:
  3757. // Input : nChar -
  3758. // nRepCnt -
  3759. // nFlags -
  3760. //-----------------------------------------------------------------------------
  3761. void CMyComboBox::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
  3762. {
  3763. CComboBox::OnChar(nChar, nRepCnt, nFlags);
  3764. return;
  3765. if(nChar == 1) // ctrl+a
  3766. {
  3767. m_pParent->SetNextVar(1);
  3768. }
  3769. else if(nChar == 11) // ctrl+q
  3770. {
  3771. m_pParent->SetNextVar(-1);
  3772. }
  3773. else
  3774. {
  3775. CComboBox::OnChar(nChar, nRepCnt, nFlags);
  3776. }
  3777. }
  3778. //-----------------------------------------------------------------------------
  3779. // Purpose: Gets the new face ID list from the pick face tool and updates the
  3780. // contents of the edit control with space-delimited face IDs.
  3781. //-----------------------------------------------------------------------------
  3782. void COP_Entity::UpdatePickFaceText(CToolPickFace *pTool)
  3783. {
  3784. char szList[KEYVALUE_MAX_VALUE_LENGTH];
  3785. szList[0] = '\0';
  3786. CMapFaceList FaceListFull;
  3787. CMapFaceList FaceListPartial;
  3788. pTool->GetSelectedFaces(FaceListFull, FaceListPartial);
  3789. if (!CMapWorld::FaceID_FaceListsToString(szList, sizeof(szList), &FaceListFull, &FaceListPartial))
  3790. {
  3791. MessageBox("Too many faces selected for this keyvalue to hold. Deselect some faces.", "Error", MB_OK);
  3792. }
  3793. //
  3794. // Update the edit control text with the new face IDs. This text will be
  3795. // stuffed into the local keyvalue storage in OnChangeSmartControl.
  3796. //
  3797. m_pSmartControl->SetWindowText(szList);
  3798. }
  3799. //-----------------------------------------------------------------------------
  3800. // Purpose: Handles the message sent by the angles custom control when the user
  3801. // changes the angle via the angle box or edit combo.
  3802. // Input : WPARAM - The ID of the control that sent the message.
  3803. // LPARAM - Unused.
  3804. //-----------------------------------------------------------------------------
  3805. LRESULT COP_Entity::OnChangeAngleBox(WPARAM nID, LPARAM)
  3806. {
  3807. CString strKey;
  3808. GetCurKey(strKey);
  3809. char szValue[KEYVALUE_MAX_VALUE_LENGTH];
  3810. bool bUpdateControl = false;
  3811. if ((nID == IDC_ANGLEBOX) || (nID == IDC_ANGLEEDIT))
  3812. {
  3813. // From the main "angles" box.
  3814. m_Angle.GetAngles(szValue);
  3815. // Only update the edit control text if the "angles" key is selected.
  3816. if (!strKey.CompareNoCase("angles"))
  3817. {
  3818. bUpdateControl = true;
  3819. }
  3820. // Slam "angles" into the key name since that's the key we're modifying.
  3821. strKey = "angles";
  3822. }
  3823. else
  3824. {
  3825. // From the secondary angles box that edits the selected keyvalue.
  3826. m_SmartAngle.GetAngles(szValue);
  3827. bUpdateControl = true;
  3828. }
  3829. // Commit the change to our local storage.
  3830. UpdateKeyValue(strKey, szValue);
  3831. if (bUpdateControl)
  3832. {
  3833. if (m_bSmartedit)
  3834. {
  3835. // Reflect the change in the SmartEdit control.
  3836. Assert(m_pSmartControl);
  3837. if (m_pSmartControl)
  3838. {
  3839. m_bEnableControlUpdate = false;
  3840. m_pSmartControl->SetWindowText(szValue);
  3841. m_bEnableControlUpdate = true;
  3842. }
  3843. }
  3844. else
  3845. {
  3846. // Reflect the change in the keyvalue control.
  3847. m_bEnableControlUpdate = false;
  3848. m_cValue.SetWindowText(szValue);
  3849. m_bEnableControlUpdate = true;
  3850. }
  3851. }
  3852. return 0;
  3853. }
  3854. void COP_Entity::OnCameraDistance(void)
  3855. {
  3856. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  3857. CMapView3D *pView = pDoc->GetFirst3DView();
  3858. if ( !pView )
  3859. return;
  3860. const CCamera *camera = pView->GetCamera();
  3861. Vector cameraPos;
  3862. camera->GetViewPoint( cameraPos );
  3863. Assert(pDoc != NULL);
  3864. if (pDoc == NULL)
  3865. {
  3866. return;
  3867. }
  3868. int nSel = GetCurVarListSelection();
  3869. Assert(nSel != LB_ERR);
  3870. if (nSel != LB_ERR)
  3871. {
  3872. GDinputvariable * pVar = GetVariableAt( nSel );
  3873. if ( pVar == NULL )
  3874. {
  3875. return;
  3876. }
  3877. Vector objectPos;
  3878. const CMapObjectList *pSelection = pDoc->GetSelection()->GetList();
  3879. int iSelectionCount = pSelection->Count();
  3880. if ( iSelectionCount == 1 )
  3881. {
  3882. // Only 1 entity selected.. we can just set our SmartControl text and the change will get applied
  3883. // when they close the properties dialog or click Apply.
  3884. CMapClass *selectedObject = pSelection->Element(iSelectionCount - 1);
  3885. selectedObject->GetOrigin( objectPos );
  3886. int distance = VectorLength( cameraPos - objectPos );
  3887. char buf[255];
  3888. itoa( distance, buf, 10 );
  3889. m_pSmartControl->SetWindowText(buf);
  3890. }
  3891. else
  3892. {
  3893. // Multiple entities selected. We have to apply the current set of changes,
  3894. // Set the value in each entity and set the kv text to VALUE_DIFFERENT_STRING so it doesn't overwrite anything when we Apply().
  3895. int index = m_kv.FindByKeyName( pVar->GetName() );
  3896. if ( index == m_kv.GetInvalidIndex() )
  3897. return;
  3898. // First set VALUE_DIFFERENT_STRING in our smart control and in m_kv.
  3899. m_pSmartControl->SetWindowText( VALUE_DIFFERENT_STRING );
  3900. MDkeyvalue &kvCur = m_kv.GetKeyValue( index );
  3901. V_strncpy( kvCur.szValue, VALUE_DIFFERENT_STRING, sizeof( kvCur.szValue ) );
  3902. // Get the list of objects we'll apply this to.
  3903. CMapObjectList objectList;
  3904. FOR_EACH_OBJ( *m_pObjectList, pos )
  3905. {
  3906. CMapClass *pObject = m_pObjectList->Element(pos);
  3907. if ( pObject && !IsWorldObject( pObject ) && dynamic_cast <CEditGameClass *>(pObject) )
  3908. objectList.AddToTail( pObject );
  3909. }
  3910. // Now set the distance property directly on the selected entities.
  3911. if ( objectList.Count() > 0 )
  3912. {
  3913. // Setup undo stuff.
  3914. GetHistory()->MarkUndoPosition( pDoc->GetSelection()->GetList(), "Change Properties");
  3915. GetHistory()->Keep( &objectList );
  3916. FOR_EACH_OBJ( objectList, pos )
  3917. {
  3918. CMapClass *pObject = m_pObjectList->Element(pos);
  3919. CEditGameClass *pEdit = dynamic_cast <CEditGameClass *>(pObject);
  3920. Assert( pObject && pEdit );
  3921. pObject->GetOrigin( objectPos );
  3922. int distance = VectorLength( cameraPos - objectPos );
  3923. char buf[255];
  3924. itoa( distance, buf, 10 );
  3925. pEdit->SetKeyValue( pVar->GetName(), buf );
  3926. }
  3927. }
  3928. }
  3929. }
  3930. }
  3931. void COP_Entity::OnTextChanged( const char *pText )
  3932. {
  3933. m_bClassSelectionEmpty = false;
  3934. UpdateDisplayClass( pText );
  3935. }
  3936. bool COP_Entity::OnUnknownEntry( const char *pText )
  3937. {
  3938. // They entered a classname we don't recognize. Get rid of all keys.
  3939. // It's about to call OnTextChanged, and we'll null m_pDisplayClass, disable SmartEdit, etc.
  3940. m_kv.RemoveAll();
  3941. return true;
  3942. }
  3943. void COP_Entity::OnSmartControlTargetNameChanged( const char *pText )
  3944. {
  3945. CFilteredComboBox *pCombo = dynamic_cast<CFilteredComboBox*>( m_pSmartControl );
  3946. Assert( pCombo );
  3947. if ( pCombo )
  3948. {
  3949. InternalOnChangeSmartcontrol( pCombo->GetCurrentItem() );
  3950. }
  3951. }
  3952. void COP_Entity::GetItemColor( int iItem, COLORREF *pBackgroundColor, COLORREF *pTextColor )
  3953. {
  3954. // Setup the background color.
  3955. EKeyState eState;
  3956. bool bMissingTarget;
  3957. GetKeyState( (const char*)m_VarList.GetItemData( iItem ), &eState, &bMissingTarget );
  3958. if ( eState == k_EKeyState_Modified )
  3959. *pBackgroundColor = g_BgColor_Edited;
  3960. else if ( eState == k_EKeyState_AddedManually )
  3961. *pBackgroundColor = g_BgColor_Added;
  3962. else if ( eState == k_EKeyState_InstanceParm )
  3963. *pBackgroundColor = g_BgColor_InstanceParm;
  3964. else
  3965. *pBackgroundColor = g_BgColor_Default;
  3966. // Setup the text color.
  3967. if ( bMissingTarget )
  3968. *pTextColor = g_TextColor_MissingTarget;
  3969. else
  3970. *pTextColor = g_TextColor_Normal;
  3971. }
  3972. bool COP_Entity::CustomDrawItemValue( const LPDRAWITEMSTRUCT p, const RECT *pRect )
  3973. {
  3974. if ( !m_bSmartedit || p->itemID < 0 || p->itemID >= ARRAYSIZE(m_VarMap) || m_VarMap[p->itemID] < 0 )
  3975. return false;
  3976. if ( !m_pDisplayClass )
  3977. return false;
  3978. GDinputvariable * pVar = GetVariableAt( p->itemID );
  3979. if ( pVar == NULL )
  3980. {
  3981. return false;
  3982. }
  3983. if ( pVar && (pVar->GetType() == ivColor255 || pVar->GetType() == ivColor1) )
  3984. {
  3985. const char *pValue = m_kv.GetValue( pVar->GetName() );
  3986. if ( pValue )
  3987. {
  3988. int r, g, b;
  3989. if ( pVar->GetType() == ivColor255 )
  3990. {
  3991. sscanf( pValue, "%d %d %d", &r, &g, &b );
  3992. }
  3993. else
  3994. {
  3995. float fr, fg, fb;
  3996. sscanf( pValue, "%f %f %f", &fr, &fg, &fb );
  3997. r = (int)(fr * 255.0);
  3998. g = (int)(fg * 255.0);
  3999. b = (int)(fb * 255.0);
  4000. }
  4001. HBRUSH hBrush = CreateSolidBrush( RGB( r, g, b ) );
  4002. HPEN hPen = CreatePen( PS_SOLID, 0, RGB(0,0,0) );
  4003. SelectObject( p->hDC, hBrush );
  4004. SelectObject( p->hDC, hPen );
  4005. RECT rc = *pRect;
  4006. Rectangle( p->hDC, rc.left+6, rc.top+2, rc.right-6, rc.bottom-2 );
  4007. return true;
  4008. }
  4009. }
  4010. return false;
  4011. }
  4012. //-----------------------------------------------------------------------------
  4013. // Purpose: The flags page calls this whenever a spawnflag changes in it.
  4014. // Input : preserveMask tells the bits you should NOT change in the spawnflags value.
  4015. // newValues is the values of the other bits.
  4016. //-----------------------------------------------------------------------------
  4017. void COP_Entity::OnUpdateSpawnFlags( unsigned long preserveMask, unsigned long newValues )
  4018. {
  4019. const char *p = m_kv.GetValue( SPAWNFLAGS_KEYNAME );
  4020. if ( !p )
  4021. return;
  4022. unsigned long oldValue = 0;
  4023. sscanf( p, "%lu", &oldValue );
  4024. unsigned long newValue = (oldValue & preserveMask) | (newValues & ~preserveMask);
  4025. // We print the string ourselves here because the int version of SetValue will show a negative number
  4026. // if we exceed 1<<31.
  4027. char str[512];
  4028. V_snprintf( str, sizeof( str ), "%lu", newValue );
  4029. m_kv.SetValue( SPAWNFLAGS_KEYNAME, str );
  4030. RefreshKVListValues( SPAWNFLAGS_KEYNAME );
  4031. OnSelchangeKeyvalues(); // Refresh the control with its value in case it's selected currently.
  4032. }
  4033. void COP_Entity::OnSize( UINT nType, int cx, int cy )
  4034. {
  4035. m_AnchorMgr.OnSize();
  4036. }