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.

1446 lines
36 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "hammer.h"
  9. #include "ObjectProperties.h"
  10. #include "ObjectPage.h"
  11. #include "OP_Flags.h"
  12. #include "OP_Groups.h"
  13. #include "OP_Entity.h"
  14. #include "OP_Output.h"
  15. #include "OP_Model.h"
  16. #include "OP_Input.h"
  17. #include "MapDoc.h"
  18. #include "MapView.h"
  19. #include "MapEntity.h"
  20. #include "MapGroup.h"
  21. #include "MapInstance.h"
  22. #include "MapSolid.h"
  23. #include "MapStudioModel.h"
  24. #include "MapWorld.h"
  25. #include "History.h"
  26. #include "GlobalFunctions.h"
  27. #include "Selection.h"
  28. #include "CustomMessages.h"
  29. #include "Camera.h"
  30. #include "Manifest.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include <tier0/memdbgon.h>
  33. //
  34. // Layout types for remembering the last layout of the dialog. We could
  35. // also remember this as an array of booleans for which pages were visible.
  36. //
  37. enum LayoutType_t
  38. {
  39. ltZero, // Special enums for initialization
  40. ltNone,
  41. ltSolid, // Enable groups only
  42. ltSolidMulti, // Enable none
  43. ltEntity, // Enable entity, flags, groups
  44. ltEntityMulti, // Enable entity, flags
  45. ltWorld, // Enable entity, flags, groups
  46. ltModelEntity, // Enable entity, flags, groups, model,
  47. ltMulti // Enable none
  48. };
  49. IMPLEMENT_DYNAMIC(CObjectProperties, CPropertySheet)
  50. BEGIN_MESSAGE_MAP(CObjectProperties, CPropertySheet)
  51. //{{AFX_MSG_MAP(CObjectProperties)
  52. ON_WM_KILLFOCUS()
  53. ON_WM_ACTIVATE()
  54. ON_WM_CLOSE()
  55. ON_WM_PAINT()
  56. ON_WM_SIZE()
  57. ON_WM_SHOWWINDOW()
  58. ON_WM_CREATE()
  59. ON_COMMAND(IDOK, OnApply )
  60. ON_COMMAND(ID_APPLY_NOW, OnApply )
  61. ON_COMMAND(IDCANCEL, OnCancel)
  62. ON_COMMAND(IDI_INPUT, OnInputs)
  63. ON_COMMAND(IDI_OUTPUT, OnOutputs)
  64. ON_COMMAND(IDD_EDIT_INSTANCE, OnEditInstance)
  65. //}}AFX_MSG_MAP
  66. END_MESSAGE_MAP()
  67. IMPLEMENT_DYNAMIC(editCMapClass, CObject);
  68. IMPLEMENT_DYNAMIC(editCEditGameClass, CObject);
  69. static editCMapClass e_CMapClass;
  70. static editCEditGameClass e_CEditGameClass;
  71. //-----------------------------------------------------------------------------
  72. // Purpose: Constructor.
  73. //-----------------------------------------------------------------------------
  74. CObjectProperties::CObjectProperties(void) :
  75. CPropertySheet()
  76. {
  77. m_bDummy = false;
  78. m_pDummy = NULL;
  79. m_pInputButton = NULL;
  80. m_pOutputButton = NULL;
  81. m_pInstanceButton = NULL;
  82. m_pOrgObjects = NULL;
  83. m_bDataDirty = false;
  84. m_bCanEdit = false;
  85. CreatePages();
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose: Constructor.
  89. // Input : nIDCaption -
  90. // pParentWnd -
  91. // iSelectPage -
  92. //-----------------------------------------------------------------------------
  93. CObjectProperties::CObjectProperties(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
  94. :CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
  95. {
  96. m_bDummy = false;
  97. m_pDummy = NULL;
  98. m_pInputButton = NULL;
  99. m_pOutputButton = NULL;
  100. m_pInstanceButton = NULL;
  101. m_bCanEdit = false;
  102. CreatePages();
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose: Constructor.
  106. // Input : pszCaption -
  107. // pParentWnd -
  108. // iSelectPage -
  109. //-----------------------------------------------------------------------------
  110. CObjectProperties::CObjectProperties(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
  111. :CPropertySheet(pszCaption, pParentWnd, iSelectPage)
  112. {
  113. m_bDummy = false;
  114. m_pDummy = NULL;
  115. m_pInputButton = NULL;
  116. m_pOutputButton = NULL;
  117. m_pInstanceButton = NULL;
  118. m_bCanEdit = false;
  119. CreatePages();
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Destructor.
  123. //-----------------------------------------------------------------------------
  124. CObjectProperties::~CObjectProperties()
  125. {
  126. delete m_pDummy;
  127. delete m_pEntity;
  128. delete m_pFlags;
  129. delete m_pGroups;
  130. delete m_pOutput;
  131. delete m_pInput;
  132. delete m_pModel;
  133. delete m_pInputButton;
  134. delete m_pOutputButton;
  135. delete m_pInstanceButton;
  136. delete[] m_ppPages;
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose: Creates all possible pages and attaches our object list to them.
  140. // Not all will be used depending on the types of objects being edited.
  141. //-----------------------------------------------------------------------------
  142. void CObjectProperties::CreatePages(void)
  143. {
  144. //VPROF_BUDGET( "CObjectProperties::CreatePages", "Object Properties" );
  145. m_pEntity = new COP_Entity;
  146. m_pEntity->SetObjectList(&m_DstObjects);
  147. m_pFlags = new COP_Flags;
  148. m_pFlags->SetObjectList(&m_DstObjects);
  149. // There are some dependencies between the entity and flags tabs since
  150. // they both edit the spawnflags property.
  151. m_pEntity->SetFlagsPage( m_pFlags );
  152. m_pFlags->SetEntityPage( m_pEntity );
  153. m_pGroups = new COP_Groups;
  154. m_pGroups->SetObjectList(&m_DstObjects);
  155. m_pOutput = new COP_Output;
  156. m_pOutput->SetObjectList(&m_DstObjects);
  157. m_pInput = new COP_Input;
  158. m_pInput->SetObjectList(&m_DstObjects);
  159. m_pModel = new COP_Model;
  160. m_pModel->SetObjectList(&m_DstObjects);
  161. m_pDummy = new CPropertyPage(IDD_OBJPAGE_DUMMY);
  162. m_ppPages = NULL;
  163. m_nPages = 0;
  164. m_pLastActivePage = NULL;
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. // Input : pType -
  169. //-----------------------------------------------------------------------------
  170. PVOID CObjectProperties::GetEditObject(CRuntimeClass *pType)
  171. {
  172. //VPROF_BUDGET( "CObjectProperties::GetEditObject", "Object Properties" );
  173. if (pType == RUNTIME_CLASS(editCMapClass))
  174. {
  175. return PVOID((CMapClass*)&e_CMapClass);
  176. }
  177. else if (pType == RUNTIME_CLASS(editCEditGameClass))
  178. {
  179. return PVOID((CEditGameClass*)&e_CEditGameClass);
  180. }
  181. Assert(0);
  182. return NULL;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose:
  186. // Input : pobj -
  187. // pType -
  188. //-----------------------------------------------------------------------------
  189. PVOID CObjectProperties::GetEditObjectFromMapObject(CMapClass *pobj, CRuntimeClass *pType)
  190. {
  191. //VPROF_BUDGET( "CObjectProperties::GetEditObjectFromMapObject", "Object Properties" );
  192. if (pType == RUNTIME_CLASS(editCMapClass))
  193. {
  194. return PVOID(pobj);
  195. }
  196. else if (pType == RUNTIME_CLASS(editCEditGameClass))
  197. {
  198. if (pobj->IsMapClass(MAPCLASS_TYPE(CMapEntity)))
  199. {
  200. return PVOID((CEditGameClass*)((CMapEntity*)pobj));
  201. }
  202. if (pobj->IsMapClass(MAPCLASS_TYPE(CMapWorld)))
  203. {
  204. return PVOID((CEditGameClass*)((CMapWorld*)pobj));
  205. }
  206. }
  207. return NULL;
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose:
  211. // Input : *pobj -
  212. //-----------------------------------------------------------------------------
  213. void CObjectProperties::CopyDataToEditObjects(CMapClass *pobj)
  214. {
  215. //VPROF_BUDGET( "CObjectProperties::CopyDataToEditObjects", "Object Properties" );
  216. //
  217. // All copies here are done without updating object dependencies, because
  218. // we're copying to a place that is outside of the world.
  219. //
  220. e_CMapClass.CopyFrom(pobj, false);
  221. if (pobj->IsMapClass(MAPCLASS_TYPE(CMapEntity)))
  222. {
  223. e_CEditGameClass.CopyFrom((CEditGameClass *)((CMapEntity *)pobj));
  224. }
  225. else if (pobj->IsMapClass(MAPCLASS_TYPE(CMapWorld)))
  226. {
  227. e_CEditGameClass.CopyFrom((CEditGameClass *)((CMapWorld *)pobj));
  228. }
  229. }
  230. //------------------------------------------------------------------------------
  231. // Purpose:
  232. // Input : nState -
  233. //------------------------------------------------------------------------------
  234. void CObjectProperties::SetOutputButtonState(int nState)
  235. {
  236. //VPROF_BUDGET( "CObjectProperties::SetOutputButtonState", "Object Properties" );
  237. if (nState == CONNECTION_GOOD)
  238. {
  239. m_pOutputButton->SetIcon(m_hIconOutputGood);
  240. m_pOutputButton->ShowWindow(SW_SHOW);
  241. m_pOutputButton->Invalidate();
  242. m_pOutputButton->UpdateWindow();
  243. }
  244. else if (nState == CONNECTION_BAD)
  245. {
  246. m_pOutputButton->SetIcon(m_hIconOutputBad);
  247. m_pOutputButton->ShowWindow(SW_SHOW);
  248. m_pOutputButton->Invalidate();
  249. m_pOutputButton->UpdateWindow();
  250. }
  251. else
  252. {
  253. m_pOutputButton->ShowWindow(SW_HIDE);
  254. m_pOutputButton->Invalidate();
  255. m_pOutputButton->UpdateWindow();
  256. }
  257. }
  258. //------------------------------------------------------------------------------
  259. // Purpose:
  260. // Input : nState -
  261. //------------------------------------------------------------------------------
  262. void CObjectProperties::SetInputButtonState(int nState)
  263. {
  264. //VPROF_BUDGET( "CObjectProperties::SetInputButtonState", "Object Properties" );
  265. if (nState == CONNECTION_GOOD)
  266. {
  267. m_pInputButton->SetIcon(m_hIconInputGood);
  268. m_pInputButton->ShowWindow(SW_SHOW);
  269. m_pInputButton->Invalidate();
  270. m_pInputButton->UpdateWindow();
  271. }
  272. else if (nState == CONNECTION_BAD)
  273. {
  274. m_pInputButton->SetIcon(m_hIconInputBad);
  275. m_pInputButton->ShowWindow(SW_SHOW);
  276. m_pInputButton->Invalidate();
  277. m_pInputButton->UpdateWindow();
  278. }
  279. else
  280. {
  281. m_pInputButton->ShowWindow(SW_HIDE);
  282. m_pInputButton->Invalidate();
  283. m_pInputButton->UpdateWindow();
  284. }
  285. }
  286. //------------------------------------------------------------------------------
  287. // Purpose: Set icon being displayed on output button.
  288. //------------------------------------------------------------------------------
  289. void CObjectProperties::UpdateOutputButton(void)
  290. {
  291. //VPROF_BUDGET( "CObjectProperties::UpdateOutputButton", "Object Properties" );
  292. if (!m_pOutputButton)
  293. {
  294. return;
  295. }
  296. bool bHaveConnection = false;
  297. bool bIgnoreHiddenTargets = false;
  298. if ( m_pOutput )
  299. bIgnoreHiddenTargets = !m_pOutput->ShouldShowHiddenTargets();
  300. FOR_EACH_OBJ( m_DstObjects, pos )
  301. {
  302. CMapClass *pObject = m_DstObjects.Element(pos);
  303. if ((pObject != NULL) && (pObject->IsMapClass(MAPCLASS_TYPE(CMapEntity))))
  304. {
  305. CMapEntity *pEntity = (CMapEntity *)pObject;
  306. int nStatus = CEntityConnection::ValidateOutputConnections( pEntity, true, bIgnoreHiddenTargets, true );
  307. if (nStatus == CONNECTION_BAD)
  308. {
  309. SetOutputButtonState(CONNECTION_BAD);
  310. return;
  311. }
  312. else if (nStatus == CONNECTION_GOOD)
  313. {
  314. bHaveConnection = true;
  315. }
  316. }
  317. }
  318. if (bHaveConnection)
  319. {
  320. SetOutputButtonState(CONNECTION_GOOD);
  321. }
  322. else
  323. {
  324. SetOutputButtonState(CONNECTION_NONE);
  325. }
  326. }
  327. //------------------------------------------------------------------------------
  328. // Purpose: Set icon being displayed on input button.
  329. //------------------------------------------------------------------------------
  330. void CObjectProperties::UpdateInputButton()
  331. {
  332. //VPROF_BUDGET( "CObjectProperties::UpdateInputButton", "Object Properties" );
  333. if (!m_pInputButton)
  334. {
  335. return;
  336. }
  337. bool bHaveConnection = false;
  338. FOR_EACH_OBJ( m_DstObjects, pos )
  339. {
  340. CMapClass *pObject = m_DstObjects.Element(pos);
  341. if ((pObject != NULL) && (pObject->IsMapClass(MAPCLASS_TYPE(CMapEntity))))
  342. {
  343. CMapEntity *pEntity = (CMapEntity *)pObject;
  344. int nStatus = CEntityConnection::ValidateInputConnections(pEntity, false);
  345. if (nStatus == CONNECTION_BAD)
  346. {
  347. SetInputButtonState(CONNECTION_BAD);
  348. return;
  349. }
  350. else if (nStatus == CONNECTION_GOOD)
  351. {
  352. bHaveConnection = true;
  353. }
  354. }
  355. }
  356. if (bHaveConnection)
  357. {
  358. SetInputButtonState(CONNECTION_GOOD);
  359. }
  360. else
  361. {
  362. SetInputButtonState(CONNECTION_NONE);
  363. }
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose: Finds/Creates the buttons.
  367. //-----------------------------------------------------------------------------
  368. void CObjectProperties::CreateButtons(void)
  369. {
  370. //VPROF_BUDGET( "CObjectProperties::CreateButtons", "Object Properties" );
  371. #if 0
  372. // Get the screen location of the hidden apply button(ID_APPLY_NOW)
  373. rect rcButton;
  374. pApplyButton->GetWindowRect( &rcButton );
  375. // Grab, enable and rename the OK button to be Apply
  376. // (Because <enter> only accelerates IDOK)
  377. // and we dont want "OK" (apply+close) functionality
  378. CButton *pOKButton = reinterpret_cast<CButton *>(GetDlgItem(IDOK));
  379. pOKButton->SetWindowTextA("Apply");
  380. pOKButton->EnableWindow();
  381. pOKButton->ShowWindow(SW_SHOWNA);
  382. pOKButton->MoveWindow(&rcButton);
  383. #else
  384. // Grab, enable and DONT show the OK button
  385. // (Because <enter> only accelerates IDOK)
  386. // and we dont want "OK" (apply+close) functionality
  387. CButton *pOKButton = reinterpret_cast<CButton *>(GetDlgItem(IDOK));
  388. pOKButton->EnableWindow();
  389. // Dont show the window, just make it active to forward <enter> -> IDOK -> OnApply
  390. // Grab and enable & show the hidden Apply button too
  391. CButton *pApplyButton = reinterpret_cast<CButton *>(GetDlgItem(ID_APPLY_NOW));
  392. pApplyButton->SetButtonStyle( pApplyButton->GetButtonStyle() | BS_DEFPUSHBUTTON );
  393. pApplyButton->EnableWindow();
  394. pApplyButton->ShowWindow(SW_SHOWNA);
  395. #endif
  396. // Grab and enable & show the hidden Cancel button too
  397. CButton *pCancelButton = reinterpret_cast<CButton *>(GetDlgItem(IDCANCEL));
  398. pCancelButton->EnableWindow();
  399. pCancelButton->ShowWindow(SW_SHOWNA);
  400. //
  401. // Load Icons
  402. //
  403. CWinApp *pApp = AfxGetApp();
  404. m_hIconOutputGood = pApp->LoadIcon(IDI_OUTPUT);
  405. m_hIconOutputBad = pApp->LoadIcon(IDI_OUTPUTBAD);
  406. m_hIconInputGood = pApp->LoadIcon(IDI_INPUT);
  407. m_hIconInputBad = pApp->LoadIcon(IDI_INPUTBAD);
  408. // Create buttons to display connection status icons
  409. CRect rect;
  410. GetWindowRect(&rect);
  411. rect.InflateRect(0, 0, 0, 32);
  412. MoveWindow(&rect, FALSE);
  413. GetClientRect(&rect);
  414. m_pInputButton = new CButton;
  415. m_pInputButton->Create(_T("My button"), WS_CHILD|WS_VISIBLE|BS_ICON|BS_FLAT, CRect(6,rect.bottom - 34,38,rect.bottom - 2), this, IDI_INPUT);
  416. m_pOutputButton = new CButton;
  417. m_pOutputButton->Create(_T("My button"), WS_CHILD|WS_VISIBLE|BS_ICON|BS_FLAT, CRect(40,rect.bottom - 34,72,rect.bottom - 2), this, IDI_OUTPUT);
  418. m_pInstanceButton = new CButton;
  419. m_pInstanceButton->Create( _T( "Edit Instance" ), WS_CHILD|WS_VISIBLE|BS_TEXT, CRect( 6, rect.bottom - 28, 140, rect.bottom - 4 ), this, IDD_EDIT_INSTANCE );
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: Returns the appropriate page layout for the current object list.
  423. //-----------------------------------------------------------------------------
  424. void CObjectProperties::GetTabsForLayout(LayoutType_t eLayoutType, bool &bEntity, bool &bGroups, bool &bFlags, bool &bModel)
  425. {
  426. //VPROF_BUDGET( "CObjectProperties::GetTabsForLayout", "Object Properties" );
  427. bEntity = bGroups = bFlags = bModel = false;
  428. switch (eLayoutType)
  429. {
  430. case ltEntity:
  431. case ltEntityMulti:
  432. case ltModelEntity:
  433. {
  434. bFlags = true;
  435. bEntity = true;
  436. bGroups = true;
  437. bModel = (eLayoutType == ltModelEntity);
  438. break;
  439. }
  440. case ltSolid:
  441. {
  442. bGroups = true;
  443. break;
  444. }
  445. case ltWorld:
  446. {
  447. bEntity = true;
  448. break;
  449. }
  450. case ltMulti:
  451. case ltSolidMulti:
  452. {
  453. bGroups = true;
  454. break;
  455. }
  456. }
  457. }
  458. //-----------------------------------------------------------------------------
  459. // Purpose: Returns the appropriate page layout for the current object list.
  460. //-----------------------------------------------------------------------------
  461. LayoutType_t CObjectProperties::GetLayout(void)
  462. {
  463. //VPROF_BUDGET( "CObjectProperties::GetLayout", "Object Properties" );
  464. LayoutType_t eLayoutType = ltNone;
  465. if ((m_DstObjects.Count() == 0) || (CMapDoc::GetActiveMapDoc() == NULL))
  466. {
  467. eLayoutType = ltNone;
  468. }
  469. else
  470. {
  471. //
  472. // Figure out which layout to use based on the objects being edited.
  473. //
  474. bool bFirst = true;
  475. MAPCLASSTYPE PrevType = MAPCLASS_TYPE(CMapEntity);
  476. FOR_EACH_OBJ( m_DstObjects, pos )
  477. {
  478. CMapClass *pObject = m_DstObjects.Element(pos);
  479. MAPCLASSTYPE ThisType = pObject->GetType();
  480. if (bFirst)
  481. {
  482. bFirst = false;
  483. if (ThisType == MAPCLASS_TYPE(CMapEntity))
  484. {
  485. CMapEntity *pEntity = (CMapEntity *)pObject;
  486. //
  487. // Only show the model tab when we have a single entity selected that
  488. // has a model helper.
  489. //
  490. if (m_DstObjects.Count() == 1)
  491. {
  492. if (pEntity->GetChildOfType((CMapStudioModel *)NULL))
  493. {
  494. eLayoutType = ltModelEntity;
  495. }
  496. else
  497. {
  498. eLayoutType = ltEntity;
  499. }
  500. }
  501. else
  502. {
  503. eLayoutType = ltEntityMulti;
  504. }
  505. }
  506. else if ((ThisType == MAPCLASS_TYPE(CMapSolid)) ||
  507. (ThisType == MAPCLASS_TYPE(CMapGroup)))
  508. {
  509. eLayoutType = (m_DstObjects.Count() == 1) ? ltSolid : ltSolidMulti;
  510. }
  511. else if (ThisType == MAPCLASS_TYPE(CMapWorld))
  512. {
  513. eLayoutType = ltWorld;
  514. }
  515. }
  516. else if (ThisType != PrevType)
  517. {
  518. eLayoutType = ltMulti;
  519. }
  520. PrevType = ThisType;
  521. }
  522. }
  523. return eLayoutType;
  524. }
  525. //-----------------------------------------------------------------------------
  526. // Purpose:
  527. //-----------------------------------------------------------------------------
  528. void CObjectProperties::RestoreActivePage(void)
  529. {
  530. //VPROF_BUDGET( "CObjectProperties::RestoreActivePage", "Object Properties" );
  531. //
  532. // Try to restore the previously active page. If it is not in the page list
  533. // just activate page zero.
  534. //
  535. bool bPageSet = false;
  536. for (int i = 0; i < m_nPages; i++)
  537. {
  538. if (m_ppPages[i] == m_pLastActivePage)
  539. {
  540. SetActivePage(m_pLastActivePage);
  541. bPageSet = true;
  542. break;
  543. }
  544. }
  545. if (!bPageSet)
  546. {
  547. SetActivePage(0);
  548. }
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Purpose:
  552. //-----------------------------------------------------------------------------
  553. void CObjectProperties::SaveActivePage(void)
  554. {
  555. //VPROF_BUDGET( "CObjectProperties::SaveActivePage", "Object Properties" );
  556. CObjectPage *pPage = (CObjectPage *)GetActivePage();
  557. if (pPage != NULL)
  558. {
  559. m_pLastActivePage = pPage;
  560. }
  561. }
  562. //-----------------------------------------------------------------------------
  563. // Purpose: Sets up pages to display based on "m_DstObjects".
  564. // Output : Returns TRUE if the page structure changed, FALSE if not.
  565. //-----------------------------------------------------------------------------
  566. BOOL CObjectProperties::SetupPages(void)
  567. {
  568. //VPROF_BUDGET( "CObjectProperties::SetupPages", "Object Properties" );
  569. static bool bFirstTime = true;
  570. static LayoutType_t eLastLayoutType = ltZero;
  571. static LayoutType_t eLastValidLayoutType = ltZero;
  572. //
  573. // Save the current active page.
  574. //
  575. if ((eLastLayoutType != ltZero) && (eLastLayoutType != ltNone))
  576. {
  577. SaveActivePage();
  578. }
  579. //
  580. // Determine the appropriate layout for the current object list.
  581. //
  582. LayoutType_t eLayoutType = GetLayout();
  583. bool bEntity;
  584. bool bGroups;
  585. bool bFlags;
  586. bool bModel;
  587. GetTabsForLayout(eLayoutType, bEntity, bGroups, bFlags, bModel);
  588. //
  589. // If the layout has not changed, we're done. All the pages are already set up.
  590. //
  591. if (eLayoutType == eLastLayoutType)
  592. {
  593. //
  594. // Try to restore the previously active page. If it has been deleted just
  595. // activate page zero.
  596. //
  597. RestoreActivePage();
  598. return(FALSE);
  599. }
  600. //
  601. // Forget the last active page when the layout changes from one
  602. // valid layout to another (such as from entity to solid).
  603. // Don't reset when switching between model entities and non-model entities,
  604. // because it's annoying to be switched away from the Outputs tab.
  605. //
  606. if ((eLayoutType != ltNone) && (eLayoutType != eLastValidLayoutType) &&
  607. !((eLayoutType == ltEntity) && (eLastValidLayoutType == ltModelEntity)) &&
  608. !((eLayoutType == ltModelEntity) && (eLastValidLayoutType == ltEntity)))
  609. {
  610. m_pLastActivePage = NULL;
  611. eLastValidLayoutType = eLayoutType;
  612. }
  613. eLastLayoutType = eLayoutType;
  614. CObjectPage::s_bRESTRUCTURING = TRUE;
  615. UINT nAddPages = bEntity + bGroups + bFlags + bModel;
  616. // don't want to change focus .. just pages!
  617. CWnd *pActiveWnd = GetActiveWindow();
  618. bool bDisabledraw = false;
  619. if (::IsWindow(m_hWnd) && IsWindowVisible())
  620. {
  621. SetRedraw(FALSE);
  622. bDisabledraw = true;
  623. }
  624. if (!m_bDummy && (nAddPages == 0))
  625. {
  626. AddPage(m_pDummy);
  627. m_bDummy = true;
  628. }
  629. else if (m_bDummy && (nAddPages > 0))
  630. {
  631. RemovePage(m_pDummy);
  632. m_bDummy = false;
  633. }
  634. struct
  635. {
  636. bool m_bIsVisible;
  637. bool m_bWantVisible;
  638. CObjectPage *m_pPage;
  639. } pages[] =
  640. {
  641. {false, bEntity, m_pEntity},
  642. {false, bEntity, m_pOutput},
  643. {false, bEntity, m_pInput},
  644. {false, bModel, m_pModel},
  645. {false, bFlags, m_pFlags},
  646. {false, bGroups, m_pGroups}
  647. };
  648. // First, remove pages that we don't want visible.
  649. // Also store if they're visible.
  650. for ( int i=0; i < ARRAYSIZE( pages ); i++ )
  651. {
  652. pages[i].m_bIsVisible = ( GetPageIndex( pages[i].m_pPage ) != -1 );
  653. if ( pages[i].m_bIsVisible && !pages[i].m_bWantVisible)
  654. {
  655. // It's visible but they don't want it there.
  656. RemovePage( pages[i].m_pPage );
  657. pages[i].m_bIsVisible = false;
  658. }
  659. }
  660. // We're about to add pages, but it'll only add them to the right of what's already there,
  661. // so we must get rid of anything to the right of our leftmost addition.
  662. for ( int i=0; i < ARRAYSIZE( pages ); i++ )
  663. {
  664. if ( !pages[i].m_bIsVisible && pages[i].m_bWantVisible )
  665. {
  666. // Ok, page i needs to be on, so nuke everything to the right of it.
  667. for ( int j=i+1; j < ARRAYSIZE( pages ); j++ )
  668. {
  669. if ( pages[j].m_bIsVisible )
  670. {
  671. RemovePage( pages[j].m_pPage );
  672. pages[j].m_bIsVisible = false;
  673. }
  674. }
  675. break;
  676. }
  677. }
  678. for ( int i=0; i < ARRAYSIZE( pages ); i++ )
  679. {
  680. if ( !pages[i].m_bIsVisible && pages[i].m_bWantVisible )
  681. AddPage( pages[i].m_pPage );
  682. }
  683. //
  684. // Store active pages in our array.
  685. //
  686. if (!m_bDummy)
  687. {
  688. delete[] m_ppPages;
  689. m_nPages = GetPageCount();
  690. m_ppPages = new CObjectPage*[m_nPages];
  691. for (int i = 0; i < m_nPages; i++)
  692. {
  693. m_ppPages[i] = (CObjectPage *)GetPage(i);
  694. m_ppPages[i]->m_bFirstTimeActive = true;
  695. m_ppPages[i]->m_bHasUpdatedData = false;
  696. }
  697. }
  698. CObjectPage::s_bRESTRUCTURING = FALSE;
  699. //VPROF_BUDGET( "CObjectProperties::RestoreActivePage", "Object Properties" );
  700. RestoreActivePage();
  701. //
  702. // Enable redraws if they were disabled above.
  703. //
  704. if (bDisabledraw)
  705. {
  706. SetRedraw(TRUE);
  707. Invalidate(FALSE);
  708. }
  709. // Set button status
  710. UpdateOutputButton();
  711. UpdateInputButton();
  712. if (pActiveWnd != NULL)
  713. {
  714. pActiveWnd->SetActiveWindow();
  715. }
  716. bFirstTime = false;
  717. return TRUE; // pages changed - return true
  718. }
  719. //------------------------------------------------------------------------------
  720. // Purpose: Set object properties dialogue to the Output tab and highlight
  721. // the given item
  722. // Input : pConnection -
  723. //------------------------------------------------------------------------------
  724. void CObjectProperties::SetPageToOutput(CEntityConnection *pConnection)
  725. {
  726. if ( m_bDataDirty )
  727. ReloadData();
  728. SetActivePage(m_pOutput);
  729. m_pOutput->SetSelectedConnection(pConnection);
  730. }
  731. void CObjectProperties::SetPageToInput(CEntityConnection *pConnection)
  732. {
  733. if ( m_bDataDirty )
  734. ReloadData();
  735. SetActivePage(m_pInput);
  736. m_pInput->SetSelectedConnection(pConnection);
  737. }
  738. //-----------------------------------------------------------------------------
  739. // Purpose:
  740. //-----------------------------------------------------------------------------
  741. void CObjectProperties::SaveData( SaveData_Reason_t reason )
  742. {
  743. //VPROF_BUDGET( "CObjectProperties::SaveData", "Object Properties" );
  744. //
  745. // Make sure window is visible - don't want to save otherwise.
  746. //
  747. if (!IsWindowVisible())
  748. {
  749. return;
  750. }
  751. // we should never save in a dirty state
  752. if ( m_bDataDirty )
  753. return;
  754. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  755. if (!pDoc || !m_DstObjects.Count() || m_bDummy)
  756. {
  757. return;
  758. }
  759. //
  760. // Transfer all page data to the objects being edited.
  761. //
  762. GetHistory()->MarkUndoPosition( pDoc->GetSelection()->GetList(), "Change Properties");
  763. // Don't keep all the world's children when we're editing the world, because
  764. // that's really slow (and pointless since all we're changing is keyvalues).
  765. bool bKeptWorld = false;
  766. if (m_DstObjects.Count() == 1)
  767. {
  768. CMapClass *pObject = m_DstObjects.Element( 0 );
  769. if ( IsWorldObject(pObject) )
  770. {
  771. GetHistory()->KeepNoChildren(pObject);
  772. bKeptWorld = true;
  773. }
  774. }
  775. if (!bKeptWorld)
  776. {
  777. GetHistory()->Keep(&m_DstObjects);
  778. }
  779. for (int i = 0; i < m_nPages; i++)
  780. {
  781. //
  782. // Pages that have never been shown have no hwnd.
  783. //
  784. if (IsWindow(m_ppPages[i]->m_hWnd) && m_ppPages[i]->m_bHasUpdatedData )
  785. {
  786. m_ppPages[i]->SaveData( reason );
  787. }
  788. }
  789. // Objects may have changed. Update the views.
  790. pDoc->SetModifiedFlag();
  791. }
  792. //-----------------------------------------------------------------------------
  793. // Purpose: Submits the objects to be edited to the property pages so they can
  794. // update their controls.
  795. // Input : iPage - Page index or -1 to update all pages.
  796. //-----------------------------------------------------------------------------
  797. void CObjectProperties::LoadDataForPages(int iPage)
  798. {
  799. //VPROF_BUDGET( "CObjectProperties::LoadDataForPages", "Object Properties" );
  800. if (m_bDummy)
  801. {
  802. return;
  803. }
  804. //
  805. // Determine whether we are editing multiple objects or not.
  806. //
  807. bool bMultiEdit = (m_DstObjects.Count() > 1);
  808. m_bCanEdit = true;
  809. //
  810. // Submit the edit objects to each page one at a time.
  811. //
  812. int nMode = CObjectPage::LoadFirstData;
  813. FOR_EACH_OBJ( m_DstObjects, pos )
  814. {
  815. CMapClass *pobj = m_DstObjects.Element(pos);
  816. if ( pobj->IsEditable() == false )
  817. {
  818. m_bCanEdit = false;
  819. }
  820. if (iPage != -1)
  821. {
  822. //
  823. // Specific page.
  824. //
  825. m_ppPages[iPage]->SetMultiEdit(bMultiEdit);
  826. void *pObject = GetEditObjectFromMapObject(pobj, m_ppPages[iPage]->GetEditObjectRuntimeClass());
  827. if (pObject != NULL)
  828. {
  829. m_ppPages[iPage]->UpdateData(nMode, pObject, m_bCanEdit);
  830. m_ppPages[iPage]->m_bHasUpdatedData = true;
  831. }
  832. }
  833. else for (int i = 0; i < m_nPages; i++)
  834. {
  835. //
  836. // All pages.
  837. //
  838. m_ppPages[i]->SetMultiEdit(bMultiEdit);
  839. // This page hasn't even been shown yet. Don't bother updating its data.
  840. if (m_ppPages[i]->m_bFirstTimeActive)
  841. continue;
  842. void *pObject = GetEditObjectFromMapObject(pobj, m_ppPages[i]->GetEditObjectRuntimeClass());
  843. if (pObject != NULL)
  844. {
  845. m_ppPages[i]->UpdateData(nMode, pObject, m_bCanEdit);
  846. m_ppPages[i]->m_bHasUpdatedData = true;
  847. }
  848. }
  849. nMode = CObjectPage::LoadData;
  850. }
  851. CButton *pApplyButton = reinterpret_cast<CButton *>(GetDlgItem(ID_APPLY_NOW));
  852. pApplyButton->EnableWindow( ( m_bCanEdit ? TRUE : FALSE ) );
  853. //
  854. // Tell the pages that we are done submitting data.
  855. //
  856. if (iPage != -1)
  857. {
  858. //
  859. // Specific page.
  860. //
  861. m_ppPages[iPage]->UpdateData(CObjectPage::LoadFinished, NULL, m_bCanEdit);
  862. }
  863. else for (int i = 0; i < m_nPages; i++)
  864. {
  865. //
  866. // All pages.
  867. //
  868. // This page hasn't even been shown yet. Don't bother updating its data.
  869. if (m_ppPages[i]->m_bFirstTimeActive)
  870. continue;
  871. m_ppPages[i]->UpdateData(CObjectPage::LoadFinished, NULL, m_bCanEdit);
  872. }
  873. //
  874. // Update the input/output icons based on the new data.
  875. //
  876. UpdateOutputButton();
  877. UpdateInputButton();
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose: Adds the object to m_DstObjects unless it is a group, in which case
  881. // it is expanded (recursively) to its children.
  882. //-----------------------------------------------------------------------------
  883. void CObjectProperties::AddObjectExpandGroups(CMapClass *pObject)
  884. {
  885. //VPROF_BUDGET( "CObjectProperties::AddObjectExpandGroups", "Object Properties" );
  886. if (pObject->IsGroup())
  887. {
  888. const CMapObjectList *pChildren = pObject->GetChildren();
  889. FOR_EACH_OBJ( *pChildren, pos )
  890. {
  891. AddObjectExpandGroups( (CUtlReference< CMapClass >)pChildren->Element(pos) );
  892. }
  893. }
  894. else
  895. {
  896. m_DstObjects.AddToTail(pObject);
  897. }
  898. }
  899. //-----------------------------------------------------------------------------
  900. // Purpose: Updates the property page data when the selection contents change.
  901. // Input : pObjects - List of currently selected objects.
  902. //-----------------------------------------------------------------------------
  903. void CObjectProperties::ReloadData()
  904. {
  905. //VPROF_BUDGET( "CObjectProperties::LoadData", "Object Properties" );
  906. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  907. //
  908. // Disable window so it does not gain focus during this operation.
  909. //
  910. EnableWindow(FALSE);
  911. //
  912. // Transfer the objects from pObjects to m_DstObjects, expanding
  913. // groups to their member children.
  914. //
  915. m_DstObjects.RemoveAll();
  916. if ( m_pOrgObjects )
  917. {
  918. FOR_EACH_OBJ( (*m_pOrgObjects), pos )
  919. {
  920. AddObjectExpandGroups( (CUtlReference< CMapClass >)m_pOrgObjects->Element(pos) );
  921. }
  922. }
  923. m_pInstanceButton->ShowWindow( SW_HIDE );
  924. //
  925. // If there is only one object selected, copy its data to our temporary
  926. // edit objects.
  927. //
  928. if (m_DstObjects.Count() == 1)
  929. {
  930. //
  931. // Copy the single destination object's data to our temporary
  932. // edit objects.
  933. //
  934. CMapClass *pobj = m_DstObjects.Element(0);
  935. CopyDataToEditObjects( pobj );
  936. //
  937. // Set the window title to include the object's description.
  938. //
  939. char szTitle[MAX_PATH];
  940. sprintf(szTitle, "Object Properties: %s", pobj->GetDescription());
  941. SetWindowText(szTitle);
  942. CManifestInstance *pManifestInstance = dynamic_cast< CManifestInstance * >( pobj );
  943. if ( pManifestInstance )
  944. {
  945. CManifest *pManifest = CMapDoc::GetManifest();
  946. if ( pManifest )
  947. {
  948. ShowWindow( SW_HIDE );
  949. if ( pDoc )
  950. {
  951. pDoc->UpdateAllViews( MAPVIEW_UPDATE_SELECTION | MAPVIEW_UPDATE_TOOL | MAPVIEW_RENDER_NOW );
  952. }
  953. pManifest->SetPrimaryMap( pManifestInstance->GetManifestMap() );
  954. return;
  955. }
  956. }
  957. CMapEntity *pEntity = dynamic_cast< CMapEntity * >( pobj );
  958. if ( pEntity )
  959. {
  960. if ( strcmpi( pEntity->GetClassName(), "func_instance" ) == 0 )
  961. {
  962. pDoc->PopulateInstance( pEntity );
  963. CMapInstance *pMapInstance = pEntity->GetChildOfType( ( CMapInstance * )NULL );
  964. if ( pMapInstance && pMapInstance->GetInstancedMap() )
  965. {
  966. m_pInstanceButton->ShowWindow( SW_SHOW );
  967. }
  968. }
  969. else if ( strcmpi( pEntity->GetClassName(), "func_instance_parms" ) == 0 )
  970. {
  971. if ( pDoc )
  972. {
  973. pDoc->PopulateInstanceParms( pEntity );
  974. }
  975. }
  976. }
  977. }
  978. else if (m_DstObjects.Count() > 1)
  979. {
  980. SetWindowText("Object Properties: multiple objects");
  981. }
  982. else
  983. {
  984. SetWindowText("Object Properties");
  985. }
  986. SetupPages();
  987. LoadDataForPages();
  988. EnableWindow(TRUE);
  989. m_bDataDirty = false;
  990. }
  991. BOOL CObjectProperties::OnInitDialog()
  992. {
  993. BOOL b = CPropertySheet::OnInitDialog();
  994. SetWindowText("Object Properties");
  995. CreateButtons();
  996. UpdateAnchors( NULL );
  997. return b;
  998. }
  999. void CObjectProperties::UpdateAnchors( CWnd *pPage )
  1000. {
  1001. if ( !GetSafeHwnd() )
  1002. return;
  1003. // Anchor stuff.
  1004. HWND hTab = NULL;
  1005. if ( GetTabControl() )
  1006. hTab = GetTabControl()->GetSafeHwnd();
  1007. CAnchorDef anchorDefs[] =
  1008. {
  1009. CAnchorDef( IDOK, k_eSimpleAnchorBottomRight ),
  1010. CAnchorDef( ID_APPLY_NOW, k_eSimpleAnchorBottomRight ),
  1011. CAnchorDef( IDCANCEL, k_eSimpleAnchorBottomRight ),
  1012. CAnchorDef( IDI_INPUT, k_eSimpleAnchorBottomRight ),
  1013. CAnchorDef( IDI_OUTPUT, k_eSimpleAnchorBottomRight ),
  1014. CAnchorDef( IDD_EDIT_INSTANCE, k_eSimpleAnchorBottomRight ),
  1015. CAnchorDef( hTab, k_eSimpleAnchorAllSides ),
  1016. CAnchorDef( pPage ? pPage->GetSafeHwnd() : (HWND)NULL, k_eSimpleAnchorAllSides )
  1017. };
  1018. m_AnchorMgr.Init( GetSafeHwnd(), anchorDefs, ARRAYSIZE( anchorDefs ) );
  1019. }
  1020. //-----------------------------------------------------------------------------
  1021. // Purpose: Closes the object properties dialog, saving changes.
  1022. //-----------------------------------------------------------------------------
  1023. void CObjectProperties::OnClose(void)
  1024. {
  1025. //VPROF_BUDGET( "CObjectProperties::OnClose", "Object Properties" );
  1026. ApplyChanges( true );
  1027. ShowWindow(SW_HIDE);
  1028. }
  1029. void CObjectProperties::OnPaint()
  1030. {
  1031. CPaintDC dc(this); // device context for painting
  1032. if ( m_bDataDirty )
  1033. ReloadData();
  1034. }
  1035. //-----------------------------------------------------------------------------
  1036. // Purpose:
  1037. // Input : bShow -
  1038. // nStatus -
  1039. //-----------------------------------------------------------------------------
  1040. void CObjectProperties::OnShowWindow(BOOL bShow, UINT nStatus)
  1041. {
  1042. //VPROF_BUDGET( "CObjectProperties::OnShowWindow", "Object Properties" );
  1043. // Forget the last active page when the window is hidden or shown.
  1044. // FIXME: SetupPages calls SaveActivePage, so we must switch to page 0 here
  1045. SetActivePage(0);
  1046. m_pLastActivePage = NULL;
  1047. CPropertySheet::OnShowWindow(bShow, nStatus);
  1048. for (int i = 0; i < m_nPages; i++)
  1049. {
  1050. m_ppPages[i]->OnShowPropertySheet(bShow, nStatus);
  1051. }
  1052. }
  1053. void CObjectProperties::OnSize( UINT nType, int cx, int cy )
  1054. {
  1055. m_AnchorMgr.OnSize();
  1056. }
  1057. //-----------------------------------------------------------------------------
  1058. // Purpose: Handles the Apply button.
  1059. //-----------------------------------------------------------------------------
  1060. void CObjectProperties::ApplyChanges( bool bCalledOnClose )
  1061. {
  1062. //VPROF_BUDGET( "CObjectProperties::OnApply", "Object Properties" );
  1063. if ( !m_bCanEdit )
  1064. {
  1065. return;
  1066. }
  1067. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1068. if ( !pDoc )
  1069. return;
  1070. //We lock visgroup updates here because activities in the object properties dialog can
  1071. //change visgroups which, if updated, will change the object properties, causing problems.
  1072. //All visgroup updates will occur at the end of this apply operation.
  1073. bool bLocked = pDoc->VisGroups_LockUpdates( true );
  1074. for (int i = 0; i < m_nPages; i++)
  1075. {
  1076. if (!m_ppPages[i]->OnApply())
  1077. {
  1078. return;
  1079. }
  1080. }
  1081. //
  1082. // Save and reload the data so the GUI updates.
  1083. //
  1084. SaveData_Reason_t reason = ( bCalledOnClose ) ? SAVEDATA_CLOSE : SAVEDATA_APPLY;
  1085. SaveData( reason );
  1086. ReloadData();
  1087. // Pass along the apply message to the entities.
  1088. FOR_EACH_OBJ( m_DstObjects, pos )
  1089. {
  1090. CMapClass *pObject = m_DstObjects.Element( pos );
  1091. if ( pObject )
  1092. {
  1093. pObject->OnApply();
  1094. }
  1095. }
  1096. if ( bLocked )
  1097. {
  1098. pDoc->VisGroups_LockUpdates( false );
  1099. }
  1100. }
  1101. //-----------------------------------------------------------------------------
  1102. // Purpose: Handles the Apply button.
  1103. //-----------------------------------------------------------------------------
  1104. void CObjectProperties::OnApply(void)
  1105. {
  1106. ApplyChanges( false );
  1107. }
  1108. //-----------------------------------------------------------------------------
  1109. // Purpose: Handles <return> keys sent to OK -> apply instead
  1110. //-----------------------------------------------------------------------------
  1111. void CObjectProperties::OnOK(void)
  1112. {
  1113. //VPROF_BUDGET( "CObjectProperties::OnClose", "Object Properties" );
  1114. ApplyChanges( false );
  1115. }
  1116. //-----------------------------------------------------------------------------
  1117. // Purpose: Handles the Apply button.
  1118. //-----------------------------------------------------------------------------
  1119. void CObjectProperties::OnCancel(void)
  1120. {
  1121. ShowWindow(SW_HIDE);
  1122. // reload original data and overwrite any changes made prio
  1123. ReloadData();
  1124. }
  1125. //-----------------------------------------------------------------------------
  1126. // Purpose: Handles the input icon button.
  1127. //-----------------------------------------------------------------------------
  1128. void CObjectProperties::OnInputs(void)
  1129. {
  1130. SetActivePage(m_pInput);
  1131. }
  1132. //-----------------------------------------------------------------------------
  1133. // Purpose: Handles the output icon button.
  1134. //-----------------------------------------------------------------------------
  1135. void CObjectProperties::OnOutputs(void)
  1136. {
  1137. SetActivePage(m_pOutput);
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. // Purpose: handle the pushing of the Edit Instance button. Will attempt to
  1141. // switch to the map document containing the instance.
  1142. // Input : none
  1143. // Output : none
  1144. //-----------------------------------------------------------------------------
  1145. void CObjectProperties::OnEditInstance(void)
  1146. {
  1147. if (m_DstObjects.Count() == 1)
  1148. {
  1149. CMapClass *pObj = m_DstObjects.Element( 0 );
  1150. CMapEntity *pEntity = dynamic_cast< CMapEntity * >( pObj );
  1151. if ( pEntity )
  1152. {
  1153. EnumChildrenPos_t pos;
  1154. CMapClass *pChild = pEntity->GetFirstDescendent( pos );
  1155. while ( pChild != NULL )
  1156. {
  1157. CMapInstance *pMapInstance = dynamic_cast< CMapInstance * >( pChild );
  1158. if ( pMapInstance != NULL )
  1159. {
  1160. OnClose();
  1161. pMapInstance->SwitchTo();
  1162. }
  1163. pChild = pEntity->GetNextDescendent( pos );
  1164. }
  1165. }
  1166. }
  1167. }
  1168. //-----------------------------------------------------------------------------
  1169. //-----------------------------------------------------------------------------
  1170. int CObjectProperties::OnCreate(LPCREATESTRUCT lpCreateStruct)
  1171. {
  1172. //VPROF_BUDGET( "CObjectProperties::OnCreate", "Object Properties" );
  1173. lpCreateStruct->dwExStyle |= WS_EX_TOOLWINDOW;
  1174. if (CPropertySheet::OnCreate(lpCreateStruct) == -1)
  1175. {
  1176. return -1;
  1177. }
  1178. return 0;
  1179. }
  1180. void CObjectProperties::SetObjectList(const CMapObjectList *pObjectList)
  1181. {
  1182. m_pOrgObjects = pObjectList;
  1183. MarkDataDirty();
  1184. }
  1185. //-----------------------------------------------------------------------------
  1186. // Purpose:
  1187. //-----------------------------------------------------------------------------
  1188. void CObjectProperties::MarkDataDirty()
  1189. {
  1190. //VPROF_BUDGET( "CObjectProperties::RefreshData", "Object Properties" );
  1191. // if flag already set, dont touch anything
  1192. if ( m_bDataDirty )
  1193. return;
  1194. for (int i = 0; i < m_nPages; i++)
  1195. {
  1196. if (m_ppPages[i]->m_hWnd)
  1197. {
  1198. m_ppPages[i]->RememberState();
  1199. m_ppPages[i]->MarkDataDirty();
  1200. }
  1201. }
  1202. Invalidate( false );
  1203. m_DstObjects.RemoveAll();
  1204. m_bDataDirty = true;
  1205. }