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.

517 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "History.h"
  9. #include "MainFrm.h"
  10. #include "MapDefs.h"
  11. #include "MapDoc.h"
  12. #include "MapView2D.h"
  13. #include "MapView3D.h"
  14. #include "Options.h"
  15. #include "StatusBarIDs.h"
  16. #include "ToolBlock.h"
  17. #include "ToolManager.h"
  18. #include "vgui/Cursor.h"
  19. #include "Selection.h"
  20. class CToolBlockMessageWnd : public CWnd
  21. {
  22. public:
  23. bool Create(void);
  24. void PreMenu2D(CToolBlock *pTool, CMapView2D *pView);
  25. protected:
  26. //{{AFX_MSG_MAP(CToolBlockMessageWnd)
  27. afx_msg void OnCreateObject();
  28. //}}AFX_MSG
  29. DECLARE_MESSAGE_MAP()
  30. private:
  31. CToolBlock *m_pToolBlock;
  32. CMapView2D *m_pView2D;
  33. };
  34. static CToolBlockMessageWnd s_wndToolMessage;
  35. static const char *g_pszClassName = "ValveEditor_BlockToolWnd";
  36. BEGIN_MESSAGE_MAP(CToolBlockMessageWnd, CWnd)
  37. //{{AFX_MSG_MAP(CToolMessageWnd)
  38. ON_COMMAND(ID_CREATEOBJECT, OnCreateObject)
  39. //}}AFX_MSG_MAP
  40. END_MESSAGE_MAP()
  41. //-----------------------------------------------------------------------------
  42. // Purpose: Creates the hidden window that receives context menu commands for the
  43. // block tool.
  44. // Output : Returns true on success, false on failure.
  45. //-----------------------------------------------------------------------------
  46. bool CToolBlockMessageWnd::Create(void)
  47. {
  48. WNDCLASS wndcls;
  49. memset(&wndcls, 0, sizeof(WNDCLASS));
  50. wndcls.lpfnWndProc = AfxWndProc;
  51. wndcls.hInstance = AfxGetInstanceHandle();
  52. wndcls.lpszClassName = g_pszClassName;
  53. if (!AfxRegisterClass(&wndcls))
  54. {
  55. return(false);
  56. }
  57. return(CWnd::CreateEx(0, g_pszClassName, g_pszClassName, 0, CRect(0, 0, 10, 10), NULL, 0) == TRUE);
  58. }
  59. //-----------------------------------------------------------------------------
  60. // Purpose: Attaches the block tool to this window before activating the context
  61. // menu.
  62. //-----------------------------------------------------------------------------
  63. void CToolBlockMessageWnd::PreMenu2D(CToolBlock *pToolBlock, CMapView2D *pView)
  64. {
  65. Assert(pToolBlock != NULL);
  66. m_pToolBlock = pToolBlock;
  67. m_pView2D = pView;
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Purpose:
  71. //-----------------------------------------------------------------------------
  72. void CToolBlockMessageWnd::OnCreateObject()
  73. {
  74. m_pToolBlock->CreateMapObject(m_pView2D);
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose: Constructor.
  78. //-----------------------------------------------------------------------------
  79. CToolBlock::CToolBlock(void)
  80. {
  81. // We always show our dimensions when we render.
  82. SetDrawFlags(GetDrawFlags() | Box3D::boundstext);
  83. SetDrawColors(Options.colors.clrToolHandle, Options.colors.clrToolBlock);
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: Destructor.
  87. //-----------------------------------------------------------------------------
  88. CToolBlock::~CToolBlock(void)
  89. {
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose: Handles key down events in the 2D view.
  93. // Input : Per CWnd::OnKeyDown.
  94. // Output : Returns true if the message was handled, false if not.
  95. //-----------------------------------------------------------------------------
  96. bool CToolBlock::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
  97. {
  98. switch (nChar)
  99. {
  100. case VK_RETURN:
  101. {
  102. if ( !IsEmpty() )
  103. {
  104. CreateMapObject(pView);
  105. }
  106. return true;
  107. }
  108. case VK_ESCAPE:
  109. {
  110. OnEscape();
  111. return true;
  112. }
  113. }
  114. return false;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Handles context menu events in the 2D view.
  118. // Input : Per CWnd::OnContextMenu.
  119. // Output : Returns true if the message was handled, false if not.
  120. //-----------------------------------------------------------------------------
  121. bool CToolBlock::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  122. {
  123. static CMenu menu, menuCreate;
  124. static bool bInit = false;
  125. if (!bInit)
  126. {
  127. bInit = true;
  128. // Create the menu.
  129. menu.LoadMenu(IDR_POPUPS);
  130. menuCreate.Attach(::GetSubMenu(menu.m_hMenu, 1));
  131. // Create the window that handles menu messages.
  132. s_wndToolMessage.Create();
  133. }
  134. if ( !pView->PointInClientRect(vPoint) )
  135. {
  136. return false;
  137. }
  138. if ( !IsEmpty() )
  139. {
  140. if ( HitTest(pView, vPoint, false) )
  141. {
  142. CPoint ptScreen( vPoint.x,vPoint.y);
  143. pView->ClientToScreen(&ptScreen);
  144. s_wndToolMessage.PreMenu2D(this, pView);
  145. menuCreate.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, &s_wndToolMessage);
  146. }
  147. }
  148. return true;
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: Handles left mouse button down events in the 2D view.
  152. // Input : Per CWnd::OnLButtonDown.
  153. // Output : Returns true if the message was handled, false if not.
  154. //-----------------------------------------------------------------------------
  155. bool CToolBlock::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  156. {
  157. Tool3D::OnLMouseDown3D(pView, nFlags, vPoint);
  158. //
  159. // If we have a box, see if they clicked on any part of it.
  160. //
  161. if ( !IsEmpty() )
  162. {
  163. if ( HitTest( pView, vPoint, true ) )
  164. {
  165. // just update current block
  166. StartTranslation( pView, vPoint, m_LastHitTestHandle );
  167. return true;
  168. }
  169. }
  170. return true;
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose: Handles left mouse button down events in the 2D view.
  174. // Input : Per CWnd::OnLButtonDown.
  175. // Output : Returns true if the message was handled, false if not.
  176. //-----------------------------------------------------------------------------
  177. bool CToolBlock::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  178. {
  179. Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);
  180. // If we have a box, see if they clicked on any part of it.
  181. if ( !IsEmpty() )
  182. {
  183. if ( HitTest( pView, vPoint, true ) )
  184. {
  185. // just update current block
  186. StartTranslation( pView, vPoint, m_LastHitTestHandle );
  187. return true;
  188. }
  189. }
  190. return true;
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose: Handles left mouse button up events in the 2D view.
  194. // Input : Per CWnd::OnLButtonUp.
  195. // Output : Returns true if the message was handled, false if not.
  196. //-----------------------------------------------------------------------------
  197. bool CToolBlock::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  198. {
  199. Tool3D::OnLMouseUp2D(pView, nFlags, vPoint);
  200. if (IsTranslating())
  201. {
  202. FinishTranslation(true);
  203. }
  204. m_pDocument->UpdateStatusbar();
  205. return true;
  206. }
  207. //-----------------------------------------------------------------------------
  208. // Purpose: Handles left mouse button up events in the 2D view.
  209. // Input : Per CWnd::OnLButtonUp.
  210. // Output : Returns true if the message was handled, false if not.
  211. //-----------------------------------------------------------------------------
  212. bool CToolBlock::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  213. {
  214. Tool3D::OnLMouseUp3D(pView, nFlags, vPoint);
  215. if (IsTranslating())
  216. {
  217. FinishTranslation(true);
  218. }
  219. m_pDocument->UpdateStatusbar();
  220. return true;
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose: Handles mouse move events in the 2D view.
  224. // Input : Per CWnd::OnMouseMove.
  225. // Output : Returns true if the message was handled, false if not.
  226. //-----------------------------------------------------------------------------
  227. bool CToolBlock::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  228. {
  229. vgui::HCursor hCursor = vgui::dc_arrow;
  230. // Snap it to the grid.
  231. unsigned int uConstraints = GetConstraints( nFlags );
  232. Tool3D::OnMouseMove2D(pView, nFlags, vPoint);
  233. //
  234. //
  235. // Convert to world coords.
  236. //
  237. Vector vecWorld;
  238. pView->ClientToWorld(vecWorld, vPoint);
  239. //
  240. // Update status bar position display.
  241. //
  242. char szBuf[128];
  243. if ( uConstraints & constrainSnap )
  244. m_pDocument->Snap(vecWorld,uConstraints);
  245. sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert]);
  246. SetStatusText(SBI_COORDS, szBuf);
  247. if ( IsTranslating() )
  248. {
  249. Tool3D::UpdateTranslation( pView, vPoint, uConstraints);
  250. hCursor = vgui::dc_none;
  251. }
  252. else if ( m_bMouseDragged[MOUSE_LEFT] )
  253. {
  254. // Starting a new box. Build a starting point for the drag.
  255. pView->SetCursor( "Resource/block.cur" );
  256. Vector vecStart;
  257. pView->ClientToWorld(vecStart, m_vMouseStart[MOUSE_LEFT] );
  258. // Snap it to the grid.
  259. if ( uConstraints & constrainSnap )
  260. m_pDocument->Snap( vecStart, uConstraints);
  261. // Start the new box with the extents of the last selected thing.
  262. m_pDocument->GetSelection()->GetLastValidBounds(bmins, bmaxs);
  263. vecStart[pView->axThird] = bmins[pView->axThird];
  264. StartNew(pView, vPoint, vecStart, pView->GetViewAxis() * (bmaxs-bmins) );
  265. }
  266. else if ( !IsEmpty() )
  267. {
  268. // If the cursor is on a handle, set it to a cross.
  269. if ( HitTest(pView, vPoint, true) )
  270. {
  271. hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
  272. }
  273. }
  274. if ( hCursor != vgui::dc_none )
  275. pView->SetCursor( hCursor );
  276. return true;
  277. }
  278. bool CToolBlock::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  279. {
  280. Tool3D::OnMouseMove3D(pView, nFlags, vPoint);
  281. if ( IsTranslating() )
  282. {
  283. unsigned int uConstraints = GetConstraints( nFlags );
  284. // If they are dragging with a valid handle, update the views.
  285. Tool3D::UpdateTranslation( pView, vPoint, uConstraints );
  286. }
  287. return true;
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose: Handles key down events in the 3D view.
  291. // Input : Per CWnd::OnKeyDown.
  292. // Output : Returns true if the message was handled, false if not.
  293. //-----------------------------------------------------------------------------
  294. bool CToolBlock::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
  295. {
  296. switch (nChar)
  297. {
  298. case VK_RETURN:
  299. {
  300. if ( !IsEmpty() )
  301. {
  302. CreateMapObject(pView);
  303. }
  304. return true;
  305. }
  306. case VK_ESCAPE:
  307. {
  308. OnEscape();
  309. return true;
  310. }
  311. }
  312. return false;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Handles the escape key in the 2D or 3D views.
  316. //-----------------------------------------------------------------------------
  317. void CToolBlock::OnEscape(void)
  318. {
  319. //
  320. // If we have nothing blocked, go back to the pointer tool.
  321. //
  322. if ( IsEmpty() )
  323. {
  324. ToolManager()->SetTool(TOOL_POINTER);
  325. }
  326. //
  327. // We're blocking out a region, so clear it.
  328. //
  329. else
  330. {
  331. SetEmpty();
  332. }
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: Creates a solid in the given view.
  336. //-----------------------------------------------------------------------------
  337. void CToolBlock::CreateMapObject(CMapView *pView)
  338. {
  339. CMapWorld *pWorld = m_pDocument->GetMapWorld();
  340. if (!(bmaxs[0] - bmins[0]) || !(bmaxs[1] - bmins[1]) || !(bmaxs[2] - bmins[2]))
  341. {
  342. AfxMessageBox("The box is empty.");
  343. SetEmpty();
  344. return;
  345. }
  346. BoundBox NewBox = *this;
  347. if (Options.view2d.bOrientPrimitives)
  348. {
  349. switch (pView->GetDrawType())
  350. {
  351. case VIEW2D_XY:
  352. {
  353. break;
  354. }
  355. case VIEW2D_YZ:
  356. {
  357. NewBox.Rotate90(AXIS_Y);
  358. break;
  359. }
  360. case VIEW2D_XZ:
  361. {
  362. NewBox.Rotate90(AXIS_X);
  363. break;
  364. }
  365. }
  366. }
  367. // Create the object within the given bounding box.
  368. CMapClass *pObject = GetMainWnd()->m_ObjectBar.CreateInBox(&NewBox, pView);
  369. if (pObject == NULL)
  370. {
  371. SetEmpty();
  372. return;
  373. }
  374. m_pDocument->ExpandObjectKeywords(pObject, pWorld);
  375. GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Object");
  376. m_pDocument->AddObjectToWorld(pObject);
  377. //
  378. // Do we need to rotate this object based on the view we created it in?
  379. //
  380. if (Options.view2d.bOrientPrimitives)
  381. {
  382. Vector center;
  383. pObject->GetBoundsCenter( center );
  384. QAngle angles(0, 0, 0);
  385. switch (pView->GetDrawType())
  386. {
  387. case VIEW2D_XY:
  388. {
  389. break;
  390. }
  391. case VIEW2D_YZ:
  392. {
  393. angles[1] = 90.f;
  394. pObject->TransRotate(center, angles);
  395. break;
  396. }
  397. case VIEW2D_XZ:
  398. {
  399. angles[0] = 90.f;
  400. pObject->TransRotate(center, angles);
  401. break;
  402. }
  403. }
  404. }
  405. GetHistory()->KeepNew(pObject);
  406. // Select the new object.
  407. m_pDocument->SelectObject(pObject, scClear|scSelect|scSaveChanges);
  408. m_pDocument->SetModifiedFlag();
  409. SetEmpty();
  410. ResetBounds();
  411. }