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

730 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the entity/prefab placement tool.
  4. //
  5. //=============================================================================//
  6. #include "stdafx.h"
  7. #include "History.h"
  8. #include "MainFrm.h"
  9. #include "MapDefs.h"
  10. #include "MapSolid.h"
  11. #include "MapDoc.h"
  12. #include "MapView2D.h"
  13. #include "MapView3D.h"
  14. #include "Material.h"
  15. #include "materialsystem/imesh.h"
  16. #include "Render2D.h"
  17. #include "Render3D.h"
  18. #include "StatusBarIDs.h"
  19. #include "TextureSystem.h"
  20. #include "ToolEntity.h"
  21. #include "ToolManager.h"
  22. #include "hammer.h"
  23. #include "vgui/Cursor.h"
  24. #include "Selection.h"
  25. #include "vstdlib/random.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include <tier0/memdbgon.h>
  28. //#pragma warning(disable:4244)
  29. static HCURSOR s_hcurEntity = NULL;
  30. class CToolEntityMessageWnd : public CWnd
  31. {
  32. public:
  33. bool Create(void);
  34. void PreMenu2D(CToolEntity *pTool, CMapView2D *pView);
  35. protected:
  36. //{{AFX_MSG_MAP(CToolEntityMessageWnd)
  37. afx_msg void OnCreateObject();
  38. //}}AFX_MSG
  39. DECLARE_MESSAGE_MAP()
  40. private:
  41. CToolEntity *m_pToolEntity;
  42. CMapView2D *m_pView2D;
  43. };
  44. static CToolEntityMessageWnd s_wndToolMessage;
  45. static const char *g_pszClassName = "ValveEditor_EntityToolWnd";
  46. BEGIN_MESSAGE_MAP(CToolEntityMessageWnd, CWnd)
  47. //{{AFX_MSG_MAP(CToolMessageWnd)
  48. ON_COMMAND(ID_CREATEOBJECT, OnCreateObject)
  49. //}}AFX_MSG_MAP
  50. END_MESSAGE_MAP()
  51. //-----------------------------------------------------------------------------
  52. // Purpose: Creates the hidden window that receives context menu commands for the
  53. // entity tool.
  54. // Output : Returns true on success, false on failure.
  55. //-----------------------------------------------------------------------------
  56. bool CToolEntityMessageWnd::Create(void)
  57. {
  58. WNDCLASS wndcls;
  59. memset(&wndcls, 0, sizeof(WNDCLASS));
  60. wndcls.lpfnWndProc = AfxWndProc;
  61. wndcls.hInstance = AfxGetInstanceHandle();
  62. wndcls.lpszClassName = g_pszClassName;
  63. if (!AfxRegisterClass(&wndcls))
  64. {
  65. return(false);
  66. }
  67. return(CWnd::CreateEx(0, g_pszClassName, g_pszClassName, 0, CRect(0, 0, 10, 10), NULL, 0) == TRUE);
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Purpose: Attaches the entity tool to this window before activating the context
  71. // menu.
  72. //-----------------------------------------------------------------------------
  73. void CToolEntityMessageWnd::PreMenu2D(CToolEntity *pToolEntity, CMapView2D *pView)
  74. {
  75. Assert(pToolEntity != NULL);
  76. m_pToolEntity = pToolEntity;
  77. m_pView2D = pView;
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose:
  81. //-----------------------------------------------------------------------------
  82. void CToolEntityMessageWnd::OnCreateObject()
  83. {
  84. m_pToolEntity->CreateMapObject(m_pView2D);
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. //-----------------------------------------------------------------------------
  89. CToolEntity::CToolEntity(void)
  90. {
  91. SetEmpty();
  92. m_vecPos.Init();
  93. if (s_hcurEntity == NULL)
  94. {
  95. s_hcurEntity = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_ENTITY));
  96. }
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose:
  100. //-----------------------------------------------------------------------------
  101. CToolEntity::~CToolEntity(void)
  102. {
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose:
  106. // Input : pt -
  107. // BOOL -
  108. // Output :
  109. //-----------------------------------------------------------------------------
  110. int CToolEntity::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles)
  111. {
  112. return HitRect( pView, ptClient, m_vecPos, 8 )?TRUE:FALSE;
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose:
  116. // Input : bSave -
  117. //-----------------------------------------------------------------------------
  118. void CToolEntity::FinishTranslation(bool bSave)
  119. {
  120. if (bSave)
  121. {
  122. TranslatePoint( m_vecPos );
  123. m_bEmpty = false;
  124. }
  125. Tool3D::FinishTranslation(bSave);
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Purpose:
  129. // Input : pt -
  130. // uFlags -
  131. // size -
  132. // Output : Returns true if the translation delta was nonzero.
  133. //-----------------------------------------------------------------------------
  134. bool CToolEntity::UpdateTranslation( const Vector &vUpdate, UINT uFlags)
  135. {
  136. Vector vOldDelta = m_vTranslation;
  137. if ( !Tool3D::UpdateTranslation( vUpdate, uFlags ) )
  138. return false;
  139. // apply snap to grid constrain
  140. if ( uFlags )
  141. {
  142. ProjectOnTranslationPlane( m_vecPos + m_vTranslation, m_vTranslation, uFlags );
  143. m_vTranslation -= m_vecPos;
  144. }
  145. return true;
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose:
  149. // Input : pRender -
  150. //-----------------------------------------------------------------------------
  151. void CToolEntity::RenderTool2D(CRender2D *pRender)
  152. {
  153. Vector v = m_vecPos;
  154. if ( IsTranslating() )
  155. {
  156. TranslatePoint( v );
  157. }
  158. else if ( IsEmpty() )
  159. {
  160. return;
  161. }
  162. pRender->SetDrawColor( 35, 255, 75 );
  163. //
  164. // Draw center rect.
  165. //
  166. pRender->DrawRectangle( v, v, false, 6.0f );
  167. //
  168. // Draw crosshair
  169. //
  170. pRender->DrawLine( Vector( g_MIN_MAP_COORD, v.y, v.z), Vector( g_MAX_MAP_COORD, v.y , v.z) );
  171. pRender->DrawLine( Vector( v.x, g_MIN_MAP_COORD, v.z), Vector( v.x, g_MAX_MAP_COORD, v.z) );
  172. pRender->DrawLine( Vector( v.x, v.y, g_MIN_MAP_COORD), Vector( v.x, v.y, g_MAX_MAP_COORD) );
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose:
  176. // Input : pView -
  177. // point -
  178. // Output :
  179. //-----------------------------------------------------------------------------
  180. bool CToolEntity::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  181. {
  182. if (!IsEmpty())
  183. {
  184. CMapDoc *pDoc = pView->GetMapDoc();
  185. if (pDoc == NULL)
  186. {
  187. return true;
  188. }
  189. if (!pView->PointInClientRect(vPoint))
  190. {
  191. return true;
  192. }
  193. if ( HitTest( pView, vPoint, false) )
  194. {
  195. static CMenu menu, menuCreate;
  196. static bool bInit = false;
  197. if (!bInit)
  198. {
  199. bInit = true;
  200. menu.LoadMenu(IDR_POPUPS);
  201. menuCreate.Attach(::GetSubMenu(menu.m_hMenu, 1));
  202. // Create the window that handles menu messages.
  203. s_wndToolMessage.Create();
  204. }
  205. CPoint ptScreen( vPoint.x,vPoint.y);
  206. pView->ClientToScreen(&ptScreen);
  207. s_wndToolMessage.PreMenu2D(this, pView);
  208. menuCreate.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, &s_wndToolMessage);
  209. }
  210. }
  211. return true;
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. // Input : *pView -
  216. // nChar -
  217. // nRepCnt -
  218. // nFlags -
  219. // Output : Returns true on success, false on failure.
  220. //-----------------------------------------------------------------------------
  221. bool CToolEntity::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
  222. {
  223. switch (nChar)
  224. {
  225. case VK_RETURN:
  226. {
  227. if (!IsEmpty())
  228. {
  229. CreateMapObject(pView);
  230. }
  231. return true;
  232. }
  233. case VK_ESCAPE:
  234. {
  235. OnEscape();
  236. return true;
  237. }
  238. }
  239. return false;
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose:
  243. // Input : pView -
  244. // nFlags -
  245. // point -
  246. // Output : Returns true on success, false on failure.
  247. //-----------------------------------------------------------------------------
  248. bool CToolEntity::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  249. {
  250. unsigned int uConstraints = GetConstraints( nFlags );
  251. Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);
  252. if ( HitTest( pView, vPoint, false) )
  253. {
  254. // translate existing object
  255. StartTranslation( pView, vPoint );
  256. }
  257. else
  258. {
  259. Vector vecWorld;
  260. pView->ClientToWorld(vecWorld, vPoint );
  261. //
  262. // Snap starting position to grid.
  263. //
  264. if ( uConstraints & constrainSnap )
  265. m_pDocument->Snap(vecWorld, uConstraints);
  266. // create new one, keep old third axis
  267. m_vecPos[pView->axHorz] = vecWorld[pView->axHorz];
  268. m_vecPos[pView->axVert] = vecWorld[pView->axVert];
  269. m_bEmpty = false;
  270. StartTranslation( pView, vPoint );
  271. }
  272. return true;
  273. }
  274. // set temp transformation plane
  275. void CToolEntity::StartTranslation( CMapView *pView, const Vector2D &vPoint )
  276. {
  277. Vector vOrigin, v1,v2,v3;
  278. pView->GetBestTransformPlane( v1,v2,v3 );
  279. SetTransformationPlane(m_vecPos, v1, v2, v3 );
  280. // align translation plane to world origin
  281. ProjectOnTranslationPlane( vec3_origin, vOrigin, 0 );
  282. // set transformation plane
  283. SetTransformationPlane(vOrigin, v1, v2, v3 );
  284. Tool3D::StartTranslation( pView, vPoint, false );
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose:
  288. // Input : Pre CWnd::OnLButtonUp.
  289. // Output : Returns true on success, false on failure.
  290. //-----------------------------------------------------------------------------
  291. bool CToolEntity::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  292. {
  293. Tool3D::OnLMouseUp2D(pView, nFlags, vPoint);
  294. if (IsTranslating())
  295. {
  296. FinishTranslation( true );
  297. }
  298. m_pDocument->UpdateStatusbar();
  299. return true;
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Returns true if the message was handled, false otherwise.
  303. //-----------------------------------------------------------------------------
  304. bool CToolEntity::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  305. {
  306. Tool3D::OnMouseMove2D(pView, nFlags, vPoint);
  307. vgui::HCursor hCursor = vgui::dc_arrow;
  308. unsigned int uConstraints = GetConstraints( nFlags );
  309. // Convert to world coords.
  310. Vector vecWorld;
  311. pView->ClientToWorld(vecWorld, vPoint);
  312. // Update status bar position display.
  313. char szBuf[128];
  314. if ( uConstraints & constrainSnap )
  315. m_pDocument->Snap(vecWorld,uConstraints);
  316. sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert] );
  317. SetStatusText(SBI_COORDS, szBuf);
  318. //
  319. // If we are currently dragging the marker, update that operation based on
  320. // the current cursor position and keyboard state.
  321. //
  322. if (IsTranslating())
  323. {
  324. Tool3D::UpdateTranslation( pView, vPoint, uConstraints );
  325. // Don't change the cursor while dragging - it should remain a cross.
  326. hCursor = vgui::dc_none;
  327. }
  328. else if (!IsEmpty())
  329. {
  330. // Don't change the cursor while dragging - it should remain a cross.
  331. hCursor = vgui::dc_crosshair;
  332. }
  333. if ( hCursor != vgui::dc_none )
  334. pView->SetCursor( hCursor );
  335. return true;
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Returns true if the message was handled, false otherwise.
  339. //-----------------------------------------------------------------------------
  340. bool CToolEntity::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  341. {
  342. return true;
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Returns true if the message was handled, false otherwise.
  346. //-----------------------------------------------------------------------------
  347. bool CToolEntity::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
  348. {
  349. CMapDoc *pDoc = pView->GetMapDoc();
  350. if (pDoc == NULL)
  351. {
  352. return false;
  353. }
  354. switch (nChar)
  355. {
  356. case VK_RETURN:
  357. {
  358. //
  359. // Create the entity or prefab.
  360. //
  361. if (!IsEmpty())
  362. {
  363. //CreateMapObject(pView); // TODO: support in 3D
  364. }
  365. return true;
  366. }
  367. case VK_ESCAPE:
  368. {
  369. OnEscape();
  370. return true;
  371. }
  372. }
  373. return false;
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: Handles the escape key in the 2D or 3D views.
  377. //-----------------------------------------------------------------------------
  378. void CToolEntity::OnEscape(void)
  379. {
  380. //
  381. // Cancel the object creation tool.
  382. //
  383. if (!IsEmpty())
  384. {
  385. SetEmpty();
  386. }
  387. else
  388. {
  389. ToolManager()->SetTool(TOOL_POINTER);
  390. }
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Purpose:
  394. // Input : *pView -
  395. // nFlags -
  396. // point -
  397. // Output : Returns true on success, false on failure.
  398. //-----------------------------------------------------------------------------
  399. bool CToolEntity::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  400. {
  401. ULONG ulFace;
  402. VMatrix LocalMatrix, LocalMatrixNeg;
  403. CMapClass *pObject = pView->NearestObjectAt( vPoint, ulFace, FLAG_OBJECTS_AT_RESOLVE_INSTANCES, &LocalMatrix );
  404. Tool3D::OnLMouseDown3D(pView, nFlags, vPoint);
  405. if (pObject != NULL)
  406. {
  407. CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pObject);
  408. if (pSolid == NULL)
  409. {
  410. // Clicked on a point entity - do nothing.
  411. return true;
  412. }
  413. LocalMatrix.InverseTR( LocalMatrixNeg );
  414. // Build a ray to trace against the face that they clicked on to
  415. // find the point of intersection.
  416. Vector Start,End;
  417. pView->GetCamera()->BuildRay( vPoint, Start, End);
  418. Vector HitPos, HitNormal;
  419. CMapFace *pFace = pSolid->GetFace(ulFace);
  420. Vector vFinalStart, vFinalEnd;
  421. LocalMatrixNeg.V3Mul( Start, vFinalStart );
  422. LocalMatrixNeg.V3Mul( End, vFinalEnd );
  423. if (pFace->TraceLine( HitPos, HitNormal, vFinalStart, vFinalEnd))
  424. {
  425. Vector vFinalHitPos, vFinalHitNormal;
  426. LocalMatrix.V3Mul( HitPos, vFinalHitPos );
  427. vFinalHitNormal = LocalMatrix.ApplyRotation( HitNormal );
  428. CMapClass *pNewObject = NULL;
  429. if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingPrefab())
  430. {
  431. //
  432. // Prefab creation.
  433. //
  434. unsigned int uConstraints = GetConstraints( nFlags );
  435. m_pDocument->Snap(vFinalHitPos,uConstraints);
  436. GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Prefab");
  437. // Get prefab object
  438. CMapClass *pPrefabObject = GetMainWnd()->m_ObjectBar.BuildPrefabObjectAtPoint(vFinalHitPos);
  439. //
  440. // Add prefab to the world.
  441. //
  442. CMapWorld *pWorld = m_pDocument->GetMapWorld();
  443. m_pDocument->ExpandObjectKeywords(pPrefabObject, pWorld);
  444. pNewObject = pPrefabObject;
  445. }
  446. else if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingEntity())
  447. {
  448. //
  449. // Entity creation.
  450. //
  451. GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Entity");
  452. CMapEntity *pEntity = new CMapEntity;
  453. pEntity->SetPlaceholder(TRUE);
  454. pEntity->SetOrigin(vFinalHitPos);
  455. pEntity->SetClass(CObjectBar::GetDefaultEntityClass());
  456. VPlane BeforeTransform( pFace->plane.normal, pFace->plane.dist ), AfterTransform;
  457. LocalMatrix.TransformPlane( BeforeTransform, AfterTransform );
  458. PLANE NewPlane;
  459. NewPlane.dist = AfterTransform.m_Dist;
  460. NewPlane.normal = AfterTransform.m_Normal;
  461. // Align the entity on the plane properly
  462. pEntity->AlignOnPlane(vFinalHitPos, &NewPlane, (vFinalHitNormal.z > 0.0f) ? CMapEntity::ALIGN_BOTTOM : CMapEntity::ALIGN_TOP);
  463. pNewObject = pEntity;
  464. }
  465. if ( pNewObject )
  466. {
  467. if ( GetMainWnd()->m_ObjectBar.UseRandomYawOnEntityPlacement() )
  468. {
  469. // They checked "random yaw" on the object bar, so come up with a random yaw.
  470. VMatrix vmRotate, vmT1, vmT2;
  471. Vector vOrigin;
  472. QAngle angRandom( 0, RandomInt( -180, 180 ), 0 );
  473. pNewObject->GetOrigin( vOrigin );
  474. // Setup a matrix that translates them to the origin, rotates it, then translates back.
  475. MatrixFromAngles( angRandom, vmRotate );
  476. MatrixBuildTranslation( vmT1, -vOrigin );
  477. MatrixBuildTranslation( vmT2, vOrigin );
  478. // Transform the object.
  479. pNewObject->Transform( vmT2 * vmRotate * vmT1 );
  480. }
  481. m_pDocument->AddObjectToWorld( pNewObject );
  482. GetHistory()->KeepNew( pNewObject );
  483. // Select the new object.
  484. m_pDocument->SelectObject( pNewObject, scClear|scSelect|scSaveChanges );
  485. m_pDocument->SetModifiedFlag();
  486. }
  487. }
  488. }
  489. return true;
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose: Renders a selection gizmo at our bounds center.
  493. // Input : pRender - Rendering interface.
  494. //-----------------------------------------------------------------------------
  495. void CToolEntity::RenderTool3D(CRender3D *pRender)
  496. {
  497. Vector pos = m_vecPos;
  498. if ( IsTranslating() )
  499. {
  500. TranslatePoint( pos );
  501. }
  502. else if ( IsEmpty() )
  503. {
  504. return;
  505. }
  506. //
  507. // Setup the renderer.
  508. //
  509. pRender->PushRenderMode( RENDER_MODE_WIREFRAME);
  510. CMeshBuilder meshBuilder;
  511. CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
  512. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  513. meshBuilder.Begin(pMesh, MATERIAL_LINES, 3);
  514. meshBuilder.Position3f(g_MIN_MAP_COORD, pos.y, pos.z);
  515. meshBuilder.Color3ub(255, 0, 0);
  516. meshBuilder.AdvanceVertex();
  517. meshBuilder.Position3f(g_MAX_MAP_COORD, pos.y, pos.z);
  518. meshBuilder.Color3ub(255, 0, 0);
  519. meshBuilder.AdvanceVertex();
  520. meshBuilder.Position3f(pos.x, g_MIN_MAP_COORD, pos.z);
  521. meshBuilder.Color3ub(0, 255, 0);
  522. meshBuilder.AdvanceVertex();
  523. meshBuilder.Position3f(pos.x, g_MAX_MAP_COORD, pos.z);
  524. meshBuilder.Color3ub(0, 255, 0);
  525. meshBuilder.AdvanceVertex();
  526. meshBuilder.Position3f(pos.x, pos.y, g_MIN_MAP_COORD);
  527. meshBuilder.Color3ub(0, 0, 255);
  528. meshBuilder.AdvanceVertex();
  529. meshBuilder.Position3f(pos.x, pos.y, g_MAX_MAP_COORD);
  530. meshBuilder.Color3ub(0, 0, 255);
  531. meshBuilder.AdvanceVertex();
  532. meshBuilder.End();
  533. pMesh->Draw();
  534. pRender->PopRenderMode();
  535. }
  536. void CToolEntity::CreateMapObject(CMapView2D *pView)
  537. {
  538. CMapWorld *pWorld = m_pDocument->GetMapWorld();
  539. CMapClass *pobj = NULL;
  540. //
  541. // Handle prefab creation.
  542. //
  543. if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingPrefab())
  544. {
  545. GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Prefab");
  546. CMapClass *pPrefabObject = GetMainWnd()->m_ObjectBar.BuildPrefabObjectAtPoint(m_vecPos);
  547. if (pPrefabObject == NULL)
  548. {
  549. pView->MessageBox("Unable to load prefab", "Error", MB_OK);
  550. SetEmpty();
  551. return;
  552. }
  553. m_pDocument->ExpandObjectKeywords(pPrefabObject, pWorld);
  554. m_pDocument->AddObjectToWorld(pPrefabObject);
  555. GetHistory()->KeepNew(pPrefabObject);
  556. pobj = pPrefabObject;
  557. }
  558. //
  559. // Handle entity creation.
  560. //
  561. else if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingEntity())
  562. {
  563. GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Entity");
  564. CMapEntity *pEntity = new CMapEntity;
  565. pEntity->SetPlaceholder(TRUE);
  566. pEntity->SetOrigin(m_vecPos);
  567. pEntity->SetClass(CObjectBar::GetDefaultEntityClass());
  568. m_pDocument->AddObjectToWorld(pEntity);
  569. pobj = pEntity;
  570. GetHistory()->KeepNew(pEntity);
  571. }
  572. //
  573. // Select the new object.
  574. //
  575. m_pDocument->SelectObject(pobj, scClear |scSelect|scSaveChanges);
  576. SetEmpty();
  577. m_pDocument->SetModifiedFlag();
  578. }