Counter Strike : Global Offensive Source Code
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.

5126 lines
139 KiB

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