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.

1987 lines
53 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "stdafx.h"
  7. #include "Gizmo.h"
  8. #include "GlobalFunctions.h" // FIXME: For NotifyDuplicates
  9. #include "History.h"
  10. #include "MainFrm.h"
  11. #include "MapDoc.h"
  12. #include "MapDefs.h"
  13. #include "MapEntity.h"
  14. #include "MapPointHandle.h"
  15. #include "MapSolid.h"
  16. #include "MapView2D.h"
  17. #include "MapViewLogical.h"
  18. #include "MapView3D.h"
  19. #include "ObjectProperties.h"
  20. #include "Options.h"
  21. #include "Render2D.h"
  22. #include "ToolSelection.h"
  23. #include "StatusBarIDs.h"
  24. #include "ToolManager.h"
  25. #include "hammer.h"
  26. #include "vgui/Cursor.h"
  27. #include "mapdecal.h"
  28. #include "RenderUtils.h"
  29. #include "tier0/icommandline.h"
  30. #include "Manifest.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include <tier0/memdbgon.h>
  33. #pragma warning(disable:4244)
  34. // For debugging mouse messages
  35. //static int _nMouseMove = 0;
  36. //-----------------------------------------------------------------------------
  37. // Purpose:
  38. //-----------------------------------------------------------------------------
  39. Selection3D::Selection3D(void)
  40. {
  41. // The block tool uses our bounds as the default size when starting a new
  42. // box. Set to reasonable defaults to begin with.
  43. m_bIsLogicalTranslating = false;
  44. m_bInLogicalBoxSelection = false;
  45. m_bBoxSelection = false;
  46. m_bEyedropper = false;
  47. m_b3DEditMode = false;
  48. m_bSelected = false;
  49. m_bLButtonDown = false;
  50. m_bLeftDragged = false;
  51. m_bDrawAsSolidBox = false;
  52. SetDrawFlags(Box3D::expandbox | Box3D::boundstext);
  53. SetDrawColors(Options.colors.clrToolHandle, Options.colors.clrToolSelection);
  54. m_clrLogicalBox = Options.colors.clrToolSelection;
  55. m_vLDownLogicalClient.Init();
  56. m_pSelection = NULL;
  57. }
  58. void Selection3D::Init( CMapDoc *pDocument )
  59. {
  60. Box3D::Init( pDocument );
  61. m_pSelection = pDocument->GetSelection();
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose:
  65. //-----------------------------------------------------------------------------
  66. Selection3D::~Selection3D(void)
  67. {
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Purpose: Called when the tool is activated.
  71. // Input : eOldTool - The ID of the previously active tool.
  72. //-----------------------------------------------------------------------------
  73. void Selection3D::OnActivate()
  74. {
  75. EnableHandles(true);
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Purpose: Called when the tool is deactivated.
  79. // Input : eNewTool - The ID of the tool that is being activated.
  80. //-----------------------------------------------------------------------------
  81. void Selection3D::OnDeactivate()
  82. {
  83. EnableHandles(false);
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: Enables or disables the selection handles based on the current
  87. // state of the tool.
  88. //-----------------------------------------------------------------------------
  89. void Selection3D::UpdateHandleState(void)
  90. {
  91. if ( !IsActiveTool() || m_pSelection->IsEditable() == false )
  92. {
  93. EnableHandles(false);
  94. }
  95. else
  96. {
  97. EnableHandles(true);
  98. }
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose:
  102. // Input : pView - The view that invoked the eyedropper.
  103. // VarList -
  104. // Output :
  105. //-----------------------------------------------------------------------------
  106. GDinputvariable *Selection3D::ChooseEyedropperVar(CMapView *pView, CUtlVector<GDinputvariable *> &VarList)
  107. {
  108. //
  109. // Build a popup menu containing all the variable names.
  110. //
  111. CMenu menu;
  112. menu.CreatePopupMenu();
  113. int nVarCount = VarList.Count();
  114. for (int nVar = 0; nVar < nVarCount; nVar++)
  115. {
  116. GDinputvariable *pVar = VarList.Element(nVar);
  117. menu.AppendMenu(MF_STRING, nVar + 1, pVar->GetLongName());
  118. }
  119. //
  120. // Invoke the popup menu.
  121. //
  122. CPoint point;
  123. GetCursorPos(&point);
  124. int nID = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, NULL, NULL);
  125. if (nID == 0)
  126. {
  127. return NULL;
  128. }
  129. return VarList.Element(nID - 1);
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Purpose:
  133. // Input : pt -
  134. // bValidOnly -
  135. // Output : Returns the handle under the given point, -1 if there is none.
  136. //-----------------------------------------------------------------------------
  137. int Selection3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles)
  138. {
  139. if (!IsEmpty())
  140. {
  141. return Box3D::HitTest(pView, ptClient, bTestHandles);
  142. }
  143. return FALSE;
  144. }
  145. bool Selection3D::HitTestLogical( CMapView *pView, const Vector2D &ptClient )
  146. {
  147. Vector2D vecLogicalMins, vecLogicalMaxs;
  148. if ( !m_pSelection->GetLogicalBounds(vecLogicalMins, vecLogicalMaxs) )
  149. return false;
  150. // Build a rect from our bounds to hit test against.
  151. Vector2D vecMinClient, vecMaxClient;
  152. Vector vecMins( vecLogicalMins.x, vecLogicalMins.y, 0.0f );
  153. Vector vecMaxs( vecLogicalMaxs.x, vecLogicalMaxs.y, 0.0f );
  154. pView->WorldToClient( vecMinClient, vecMins );
  155. pView->WorldToClient( vecMaxClient, vecMaxs );
  156. CRect rect(vecMinClient.x, vecMinClient.y, vecMaxClient.x, vecMaxClient.y);
  157. rect.NormalizeRect();
  158. // See if the point lies within the main rect.
  159. return rect.PtInRect( CPoint( ptClient.x, ptClient.y ) ) ? true : false;
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose:
  163. //-----------------------------------------------------------------------------
  164. void Selection3D::SetEmpty(void)
  165. {
  166. m_vTranslation.Init();
  167. m_bIsTranslating = false;
  168. m_pSelection->SelectObject(NULL,scClear);
  169. UpdateSelectionBounds();
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. //-----------------------------------------------------------------------------
  174. bool Selection3D::IsEmpty(void)
  175. {
  176. return (m_bBoxSelection || m_pSelection->GetCount()) ? false : true;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose:
  180. // Input :
  181. //-----------------------------------------------------------------------------
  182. void Selection3D::UpdateSelectionBounds( void )
  183. {
  184. if ( !m_pSelection->GetBounds( bmins, bmaxs ) )
  185. {
  186. ResetBounds();
  187. }
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose:
  191. // Input : pt3 -
  192. // Output : Returns TRUE on success, FALSE on failure.
  193. //-----------------------------------------------------------------------------
  194. bool Selection3D::StartBoxSelection( CMapView *pView, const Vector2D &vPoint, const Vector &vStart)
  195. {
  196. m_bBoxSelection = true;
  197. Box3D::StartNew( pView, vPoint, vStart, Vector(0,0,0) );
  198. return true;
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose:
  202. //-----------------------------------------------------------------------------
  203. void Selection3D::EndBoxSelection()
  204. {
  205. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL | MAPVIEW_UPDATE_SELECTION );
  206. m_bBoxSelection = false;
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Start, end logical selection
  210. //-----------------------------------------------------------------------------
  211. void Selection3D::StartLogicalBoxSelection( CMapViewLogical *pView, const Vector &vStart )
  212. {
  213. m_bInLogicalBoxSelection = true;
  214. m_clrLogicalBox = RGB( 50, 255, 255 );
  215. m_vecLogicalSelBoxMins = m_vecLogicalSelBoxMaxs = vStart.AsVector2D();
  216. }
  217. void Selection3D::EndLogicalBoxSelection( )
  218. {
  219. m_clrLogicalBox = Options.colors.clrToolSelection;
  220. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL | MAPVIEW_UPDATE_SELECTION );
  221. m_bInLogicalBoxSelection = false;
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose:
  225. //-----------------------------------------------------------------------------
  226. void Selection3D::TransformSelection(void)
  227. {
  228. // Transform the selected objects.
  229. const CMapObjectList *pSelList = m_pSelection->GetList();
  230. for (int i = 0; i < pSelList->Count(); i++)
  231. {
  232. CUtlReference< CMapClass > ref = pSelList->Element(i);
  233. CMapClass *pobj = ref;
  234. pobj->Transform( GetTransformMatrix() );
  235. }
  236. m_pDocument->SetModifiedFlag();
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. //-----------------------------------------------------------------------------
  241. void Selection3D::TransformLogicalSelection( const Vector2D &vecTranslation )
  242. {
  243. // Transform the selected objects.
  244. const CMapObjectList *pSelList = m_pSelection->GetList();
  245. for (int i = 0; i < pSelList->Count(); i++)
  246. {
  247. CUtlReference< CMapClass > ref = pSelList->Element(i);
  248. CMapClass *pObj = ref;
  249. Vector2D vecNewPosition;
  250. Vector2DAdd( pObj->GetLogicalPosition(), vecTranslation, vecNewPosition );
  251. pObj->SetLogicalPosition( vecNewPosition );
  252. }
  253. // The transformation may have changed some entity properties (such as the "angles" key),
  254. // so we must refresh the Object Properties dialog.
  255. GetMainWnd()->pObjectProperties->MarkDataDirty();
  256. }
  257. //-----------------------------------------------------------------------------
  258. // Purpose: Draws objects when they are selected. Odd, how this code is stuck
  259. // in this obscure place, away from all the other 2D rendering code.
  260. // Input : pobj - Object to draw.
  261. // pSel -
  262. // Output : Returns TRUE to keep enumerating.
  263. //-----------------------------------------------------------------------------
  264. static BOOL DrawObject(CMapClass *pobj, CRender *pRender)
  265. {
  266. if ( !pobj->IsVisible() )
  267. return true;
  268. // switch selection mode so transformed object is drawn normal
  269. pobj->SetSelectionState( SELECT_NONE );
  270. CRender2D *pRender2D = dynamic_cast<CRender2D*>(pRender);
  271. if ( pRender2D )
  272. pobj->Render2D(pRender2D);
  273. CRender3D *pRender3D = dynamic_cast<CRender3D*>(pRender);
  274. if ( pRender3D )
  275. pobj->Render3D(pRender3D);
  276. pobj->SetSelectionState( SELECT_MODIFY );
  277. return TRUE;
  278. }
  279. static BOOL DrawObjectLogical( CMapClass *pObj, CRender2D *pRender2D )
  280. {
  281. if ( !pObj->IsVisibleLogical() )
  282. return true;
  283. // switch selection mode so transformed object is drawn normal
  284. pObj->SetSelectionState( SELECT_NONE );
  285. if ( pRender2D )
  286. {
  287. pObj->RenderLogical( pRender2D );
  288. }
  289. pObj->SetSelectionState( SELECT_MODIFY );
  290. return TRUE;
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Purpose:
  294. // Input : *pRender -
  295. //-----------------------------------------------------------------------------
  296. void Selection3D::RenderTool2D(CRender2D *pRender)
  297. {
  298. if ( !m_pSelection->IsEmpty() && IsTranslating() && !IsBoxSelecting() )
  299. {
  300. //
  301. // Even if this is not the active tool, selected objects should be rendered
  302. // with the selection color.
  303. //
  304. COLORREF clr = Options.colors.clrSelection;
  305. pRender->SetDrawColor( GetRValue(clr), GetGValue(clr), GetBValue(clr) );
  306. VMatrix matrix = GetTransformMatrix();
  307. pRender->BeginLocalTransfrom( matrix );
  308. const CMapObjectList *pSelList = m_pSelection->GetList();
  309. for (int i = 0; i < pSelList->Count(); i++)
  310. {
  311. CMapClass *pobj = (CUtlReference< CMapClass >)pSelList->Element(i);
  312. DrawObject(pobj, pRender);
  313. pobj->EnumChildren((ENUMMAPCHILDRENPROC)DrawObject, (DWORD)pRender);
  314. }
  315. pRender->EndLocalTransfrom();
  316. }
  317. else if ( !IsBoxSelecting() )
  318. {
  319. UpdateSelectionBounds();
  320. }
  321. Box3D::RenderTool2D(pRender);
  322. }
  323. //-----------------------------------------------------------------------------
  324. // Render tool in visio view
  325. //-----------------------------------------------------------------------------
  326. void Selection3D::RenderToolLogical( CRender2D *pRender )
  327. {
  328. if ( !m_pSelection->IsEmpty() && m_bIsLogicalTranslating && !IsLogicalBoxSelecting() )
  329. {
  330. // Even if this is not the active tool, selected objects should be rendered
  331. // with the selection color.
  332. COLORREF clr = Options.colors.clrSelection;
  333. pRender->SetDrawColor( GetRValue(clr), GetGValue(clr), GetBValue(clr) );
  334. VMatrix matrix = GetTransformMatrix();
  335. MatrixBuildTranslation( matrix, m_vLogicalTranslation.x, m_vLogicalTranslation.y, 0.0f );
  336. pRender->BeginLocalTransfrom( matrix );
  337. const CMapObjectList *pSelList = m_pSelection->GetList();
  338. for (int i = 0; i < pSelList->Count(); i++)
  339. {
  340. CMapClass *pobj = (CUtlReference< CMapClass>)pSelList->Element(i);
  341. DrawObjectLogical(pobj, pRender);
  342. pobj->EnumChildren((ENUMMAPCHILDRENPROC)DrawObjectLogical, (DWORD)pRender);
  343. }
  344. pRender->EndLocalTransfrom();
  345. }
  346. Vector2D vecLogicalMins, vecLogicalMaxs;
  347. if ( IsLogicalBoxSelecting() )
  348. {
  349. vecLogicalMins = m_vecLogicalSelBoxMins;
  350. vecLogicalMaxs = m_vecLogicalSelBoxMaxs;
  351. }
  352. else if ( !m_pSelection->GetLogicalBounds( vecLogicalMins, vecLogicalMaxs ) )
  353. return;
  354. Vector mins( vecLogicalMins.x, vecLogicalMins.y, 0.0f );
  355. Vector maxs( vecLogicalMaxs.x, vecLogicalMaxs.y, 0.0f );
  356. Assert( pRender );
  357. pRender->PushRenderMode( RENDER_MODE_DOTTED );
  358. pRender->SetDrawColor( GetRValue(Options.colors.clrToolDrag), GetGValue(Options.colors.clrToolDrag), GetBValue(Options.colors.clrToolDrag) );
  359. pRender->DrawRectangle( mins, maxs, false, 2 );
  360. pRender->PopRenderMode();
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose: Renders a selection gizmo at our bounds center.
  364. // Input : pRender -
  365. //-----------------------------------------------------------------------------
  366. void Selection3D::RenderTool3D(CRender3D *pRender)
  367. {
  368. const CMapObjectList *pSelList = m_pSelection->GetList();
  369. if ( m_bDrawAsSolidBox )
  370. {
  371. // while picking draw Selection tool as solid box
  372. // so we cant pick stuff behind it
  373. if ( pSelList->Count() )
  374. {
  375. pRender->PushRenderMode( RENDER_MODE_FLAT );
  376. pRender->BeginRenderHitTarget( (CUtlReference< CMapClass>)pSelList->Element(0) );
  377. pRender->RenderBox( bmins, bmaxs, 255,255,255, SELECT_NONE );
  378. pRender->EndRenderHitTarget();
  379. pRender->PopRenderMode();
  380. }
  381. return;
  382. }
  383. else if ( !m_pSelection->IsEmpty() && IsTranslating() && !IsBoxSelecting() )
  384. {
  385. //
  386. // Even if this is not the active tool, selected objects should be rendered
  387. // with the selection color.
  388. //
  389. COLORREF clr = Options.colors.clrSelection;
  390. pRender->SetDrawColor( GetRValue(clr), GetGValue(clr), GetBValue(clr) );
  391. VMatrix matrix = GetTransformMatrix();
  392. pRender->BeginLocalTransfrom( matrix );
  393. for (int i = 0; i < pSelList->Count(); i++)
  394. {
  395. CMapClass *pobj = (CUtlReference< CMapClass>)pSelList->Element(i);
  396. DrawObject(pobj, pRender);
  397. pobj->EnumChildren((ENUMMAPCHILDRENPROC)DrawObject, (DWORD)pRender);
  398. }
  399. pRender->EndLocalTransfrom();
  400. if ( m_pDocument->m_bShowGrid && m_b3DEditMode )
  401. RenderTranslationPlane( pRender );
  402. }
  403. else if ( !IsBoxSelecting() )
  404. {
  405. UpdateSelectionBounds();
  406. }
  407. if ( m_b3DEditMode )
  408. {
  409. Box3D::RenderTool3D(pRender);
  410. }
  411. }
  412. CBaseTool *Selection3D::GetToolObject( CMapView2D *pView, const Vector2D &vPoint, bool bAttach )
  413. {
  414. const CMapObjectList *pSelList = m_pSelection->GetList();
  415. for (int i = 0; i < pSelList->Count(); i++)
  416. {
  417. CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i);
  418. //
  419. // Hit test against the object. nHitData will return with object-specific
  420. // information about what was clicked on.
  421. //
  422. HitInfo_t HitData;
  423. if ( pObject->HitTest2D(pView, vPoint, HitData) )
  424. {
  425. //
  426. // They clicked on some part of the object. See if there is a
  427. // tool associated with what we clicked on.
  428. //
  429. CBaseTool *pToolHit = HitData.pObject->GetToolObject(HitData.uData, bAttach );
  430. if ( pToolHit != NULL )
  431. {
  432. return pToolHit;
  433. }
  434. }
  435. }
  436. return NULL;
  437. }
  438. CBaseTool *Selection3D::GetToolObjectLogical( CMapViewLogical *pView, const Vector2D &vPoint, bool bAttach )
  439. {
  440. const CMapObjectList *pSelList = m_pSelection->GetList();
  441. for (int i = 0; i < pSelList->Count(); i++)
  442. {
  443. CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i);
  444. //
  445. // Hit test against the object. nHitData will return with object-specific
  446. // information about what was clicked on.
  447. //
  448. HitInfo_t HitData;
  449. if ( pObject->HitTestLogical(pView, vPoint, HitData) )
  450. {
  451. //
  452. // They clicked on some part of the object. See if there is a
  453. // tool associated with what we clicked on.
  454. //
  455. CBaseTool *pToolHit = HitData.pObject->GetToolObject(HitData.uData, bAttach );
  456. if ( pToolHit != NULL )
  457. {
  458. return pToolHit;
  459. }
  460. }
  461. }
  462. return NULL;
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Purpose:
  466. // Input : pView -
  467. // point -
  468. //-----------------------------------------------------------------------------
  469. bool Selection3D::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  470. {
  471. // First give any selected tool helpers a chance to handle the message.
  472. // Don't hit test against tool helpers when shift is held down
  473. // (beginning a Clone operation).
  474. CBaseTool *pToolHit = GetToolObject( pView, vPoint, true );
  475. if ( pToolHit )
  476. {
  477. return pToolHit->OnContextMenu2D(pView, nFlags, vPoint);
  478. }
  479. static CMenu menu, menuSelection;
  480. static bool bInit = false;
  481. if (!bInit)
  482. {
  483. bInit = true;
  484. menu.LoadMenu(IDR_POPUPS);
  485. menuSelection.Attach(::GetSubMenu(menu.m_hMenu, 0));
  486. }
  487. if ( !pView->PointInClientRect( vPoint ) )
  488. return false;
  489. if (!IsEmpty() && !IsBoxSelecting())
  490. {
  491. if ( HitTest(pView, vPoint, false) )
  492. {
  493. CPoint ptScreen( vPoint.x,vPoint.y);
  494. pView->ClientToScreen(&ptScreen);
  495. menuSelection.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, pView);
  496. return true;
  497. }
  498. }
  499. return false;
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Purpose:
  503. // Input : pView -
  504. // point -
  505. //-----------------------------------------------------------------------------
  506. bool Selection3D::OnContextMenuLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint)
  507. {
  508. // First give any selected tool helpers a chance to handle the message.
  509. // Don't hit test against tool helpers when shift is held down
  510. // (beginning a Clone operation).
  511. CBaseTool *pToolHit = GetToolObjectLogical( pView, vPoint, true );
  512. if ( pToolHit )
  513. return pToolHit->OnContextMenuLogical(pView, nFlags, vPoint);
  514. static CMenu menu, menuSelection;
  515. static bool bInit = false;
  516. if (!bInit)
  517. {
  518. bInit = true;
  519. menu.LoadMenu(IDR_POPUPS);
  520. menuSelection.Attach(::GetSubMenu(menu.m_hMenu, 8));
  521. }
  522. if ( !pView->PointInClientRect( vPoint ) )
  523. return false;
  524. if (!IsEmpty() && !IsLogicalBoxSelecting())
  525. {
  526. if ( HitTestLogical( pView, vPoint ) )
  527. {
  528. CPoint ptScreen( vPoint.x, vPoint.y );
  529. pView->ClientToScreen(&ptScreen);
  530. menuSelection.TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, pView);
  531. return true;
  532. }
  533. }
  534. return false;
  535. }
  536. //-----------------------------------------------------------------------------
  537. // Purpose:
  538. //-----------------------------------------------------------------------------
  539. void Selection3D::SelectInBox(CMapDoc *pDoc, bool bInsideOnly)
  540. {
  541. BoundBox box(*this);
  542. EndBoxSelection();
  543. //
  544. // Make selection box "infinite" in 0-depth axes, of which there
  545. // should not be more than 1.
  546. //
  547. int countzero = 0;
  548. for(int i = 0; i < 3; i++)
  549. {
  550. if (box.bmaxs[i] == box.bmins[i])
  551. {
  552. box.bmins[i] = -COORD_NOTINIT;
  553. box.bmaxs[i] = COORD_NOTINIT;
  554. ++countzero;
  555. }
  556. }
  557. if (countzero <= 1)
  558. {
  559. pDoc->SelectRegion(&box, bInsideOnly);
  560. }
  561. UpdateSelectionBounds();
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose:
  565. //-----------------------------------------------------------------------------
  566. void Selection3D::SelectInLogicalBox(CMapDoc *pDoc, bool bInsideOnly)
  567. {
  568. Vector2D mins = m_vecLogicalSelBoxMins;
  569. Vector2D maxs = m_vecLogicalSelBoxMaxs;
  570. // Make selection box "infinite" in 0-depth axes, of which there
  571. // should not be more than 1.
  572. int countzero = 0;
  573. for (int i = 0; i < 2; i++)
  574. {
  575. if (maxs[i] == mins[i])
  576. {
  577. mins[i] = -COORD_NOTINIT;
  578. maxs[i] = COORD_NOTINIT;
  579. ++countzero;
  580. }
  581. }
  582. if (countzero <= 1)
  583. {
  584. pDoc->SelectLogicalRegion( mins, maxs, bInsideOnly );
  585. }
  586. UpdateSelectionBounds();
  587. }
  588. //-----------------------------------------------------------------------------
  589. // Purpose:
  590. //-----------------------------------------------------------------------------
  591. void Selection3D::NudgeObjects(CMapView *pView, int nChar, bool bSnap, bool bClone)
  592. {
  593. Vector vecDelta, vVert, vHorz, vThrd;
  594. pView->GetBestTransformPlane( vHorz, vVert, vThrd );
  595. m_pDocument->GetNudgeVector( vHorz, vVert, nChar, bSnap, vecDelta);
  596. m_pDocument->NudgeObjects(vecDelta, bClone);
  597. CMapView2DBase *pView2D = dynamic_cast<CMapView2DBase*>(pView);
  598. if ( !pView2D )
  599. return;
  600. // Try to keep the selection fully in the view if it started that way.
  601. bool bFullyVisible = pView2D->IsBoxFullyVisible(bmins, bmaxs);
  602. // Make sure it can still fit entirely in the view after nudging and don't scroll the
  603. // view if it can't. This second check is probably unnecessary, but it can't hurt,
  604. // and there might be cases where the selection changes size after a nudge operation.
  605. if (bFullyVisible && pView2D->CanBoxFitInView(bmins, bmaxs))
  606. {
  607. pView2D->LockWindowUpdate();
  608. pView2D->EnsureVisible(bmins, 25);
  609. pView2D->EnsureVisible(bmaxs, 25);
  610. pView2D->UnlockWindowUpdate();
  611. }
  612. }
  613. //-----------------------------------------------------------------------------
  614. // Purpose: Handles key down events in the 2D view.
  615. // Input : Per CWnd::OnKeyDown.
  616. // Output : Returns true on success, false on failure.
  617. //-----------------------------------------------------------------------------
  618. bool Selection3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
  619. {
  620. bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0);
  621. bool bCtrl = ((GetKeyState(VK_CONTROL) & 0x8000) != 0);
  622. if (Options.view2d.bNudge && (nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT))
  623. {
  624. if (!IsEmpty())
  625. {
  626. bool bSnap = m_pDocument->IsSnapEnabled() && !bCtrl;
  627. NudgeObjects(pView, nChar, bSnap, bShift);
  628. return true;
  629. }
  630. }
  631. switch (nChar)
  632. {
  633. // TODO: do we want this here or in the view?
  634. case VK_DELETE:
  635. {
  636. m_pDocument->OnCmdMsg(ID_EDIT_DELETE, CN_COMMAND, NULL, NULL);
  637. break;
  638. }
  639. case VK_NEXT:
  640. {
  641. m_pDocument->OnCmdMsg(ID_EDIT_SELNEXT, CN_COMMAND, NULL, NULL);
  642. break;
  643. }
  644. case VK_PRIOR:
  645. {
  646. m_pDocument->OnCmdMsg(ID_EDIT_SELPREV, CN_COMMAND, NULL, NULL);
  647. break;
  648. }
  649. case VK_ESCAPE:
  650. {
  651. OnEscape(m_pDocument);
  652. break;
  653. }
  654. case VK_RETURN:
  655. {
  656. if (IsBoxSelecting())
  657. {
  658. SelectInBox(m_pDocument, bShift);
  659. UpdateHandleState();
  660. }
  661. break;
  662. }
  663. default:
  664. {
  665. return false;
  666. }
  667. }
  668. return true;
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose: Handles key down events in the logical view.
  672. // Input : Per CWnd::OnKeyDown.
  673. // Output : Returns true on success, false on failure.
  674. //-----------------------------------------------------------------------------
  675. bool Selection3D::OnKeyDownLogical(CMapViewLogical *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
  676. {
  677. bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0);
  678. bool bAlt = GetKeyState(VK_MENU) < 0;
  679. /* FIXME
  680. if ( Options.view2d.bNudge && ( nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT ) )
  681. {
  682. if (!IsEmpty())
  683. {
  684. NudgeObjects2D(pView, nChar, !bCtrl, bShift);
  685. return true;
  686. }
  687. }
  688. */
  689. switch (nChar)
  690. {
  691. // TODO: do we want this here or in the view?
  692. case VK_DELETE:
  693. m_pDocument->OnCmdMsg(ID_EDIT_DELETE, CN_COMMAND, NULL, NULL);
  694. break;
  695. case VK_NEXT:
  696. m_pDocument->OnCmdMsg( bAlt ? ID_EDIT_SELNEXT_CASCADING : ID_EDIT_SELNEXT, CN_COMMAND, NULL, NULL);
  697. break;
  698. case VK_PRIOR:
  699. m_pDocument->OnCmdMsg( bAlt ? ID_EDIT_SELPREV_CASCADING : ID_EDIT_SELPREV, CN_COMMAND, NULL, NULL);
  700. break;
  701. case VK_ESCAPE:
  702. OnEscape( m_pDocument );
  703. break;
  704. case VK_RETURN:
  705. if ( m_bInLogicalBoxSelection )
  706. {
  707. EndLogicalBoxSelection( );
  708. SelectInLogicalBox( m_pDocument, bShift );
  709. }
  710. break;
  711. default:
  712. return false;
  713. }
  714. return true;
  715. }
  716. //-----------------------------------------------------------------------------
  717. // Purpose: Handles left button down events in the 2D view.
  718. // Input : Per CWnd::OnLButtonDown.
  719. // Output : Returns true if the message was handled, false if not.
  720. //-----------------------------------------------------------------------------
  721. bool Selection3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  722. {
  723. // First give any selected tool helpers a chance to handle the message.
  724. // Don't hit test against tool helpers when shift is held down
  725. // (beginning a Clone operation).
  726. if (!(nFlags & MK_SHIFT))
  727. {
  728. CBaseTool *pToolHit = GetToolObject( pView, vPoint, true );
  729. if (pToolHit)
  730. {
  731. // There is a tool. Attach the object to the tool and forward
  732. // the message to the tool.
  733. return pToolHit->OnLMouseDown2D(pView, nFlags, vPoint);
  734. }
  735. }
  736. Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);
  737. m_bSelected = false;
  738. if ( IsBoxSelecting() )
  739. {
  740. // if we click outside of the current selection box, remove old box
  741. if ( !HitTest(pView, vPoint, true) )
  742. {
  743. EndBoxSelection();
  744. }
  745. }
  746. if (nFlags & MK_CONTROL)
  747. {
  748. // add object under cursor to selection
  749. m_bSelected = pView->SelectAt(vPoint, false, false);
  750. UpdateHandleState();
  751. }
  752. else if ( IsEmpty() || !HitTest(pView,vPoint, true) )
  753. {
  754. // start new selection
  755. m_TranslateMode = modeScale;
  756. m_bSelected = pView->SelectAt(vPoint, true, false);
  757. UpdateHandleState();
  758. }
  759. return true;
  760. }
  761. //-----------------------------------------------------------------------------
  762. // Purpose: Returns the constraints flags for the translation.
  763. // Input : bDisableSnap -
  764. // nKeyFlags -
  765. //-----------------------------------------------------------------------------
  766. unsigned int Selection3D::GetConstraints(unsigned int nKeyFlags)
  767. {
  768. unsigned int uConstraints = Tool3D::GetConstraints( nKeyFlags );
  769. if ( m_TranslateMode==modeRotate )
  770. {
  771. // backwards capability, SHIFT turns snapping off during rotation
  772. if ( (nKeyFlags & MK_SHIFT) || !Options.view2d.bRotateConstrain )
  773. {
  774. uConstraints = 0;
  775. }
  776. }
  777. if ( uConstraints & constrainSnap )
  778. {
  779. if ( m_pSelection->GetCount() == 1)
  780. {
  781. CMapClass *pObject = (CUtlReference< CMapClass>)m_pSelection->GetList()->Element(0);
  782. if (pObject->ShouldSnapToHalfGrid())
  783. {
  784. uConstraints |= constrainHalfSnap;
  785. }
  786. }
  787. }
  788. return uConstraints;
  789. }
  790. //-----------------------------------------------------------------------------
  791. // Purpose: Handles mouse move events in the 2D view.
  792. // Input : Per CWnd::OnMouseMove.
  793. // Output : Returns true if the message was handled, false if not.
  794. //-----------------------------------------------------------------------------
  795. bool Selection3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  796. {
  797. Tool3D::OnMouseMove2D(pView, nFlags, vPoint);
  798. bool IsEditable = m_pSelection->IsEditable();
  799. vgui::HCursor hCursor = vgui::dc_arrow;
  800. bool bCtrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? true : false;
  801. unsigned int uConstraints = GetConstraints( nFlags);
  802. // Convert to world coords.
  803. Vector vecWorld;
  804. pView->ClientToWorld(vecWorld, vPoint);
  805. //
  806. // Update status bar position display.
  807. //
  808. char szBuf[128];
  809. sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert]);
  810. SetStatusText(SBI_COORDS, szBuf);
  811. //
  812. // If we are currently dragging the selection (moving, scaling, rotating, or shearing)
  813. // update that operation based on the current cursor position and keyboard state.
  814. //
  815. if ( IsTranslating() )
  816. {
  817. Tool3D::UpdateTranslation( pView, vPoint, uConstraints );
  818. hCursor = vgui::dc_none;
  819. }
  820. //
  821. // Else if we have just started dragging the selection, begin a new translation
  822. //
  823. else if ( m_bMouseDragged[MOUSE_LEFT] )
  824. {
  825. pView->SetCapture();
  826. if ( IsEditable && !bCtrl && HitTest( pView, m_vMouseStart[MOUSE_LEFT], true) )
  827. {
  828. // we selected a handle - start translation the selection
  829. StartTranslation( pView, m_vMouseStart[MOUSE_LEFT], m_LastHitTestHandle );
  830. hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
  831. }
  832. else if ( !m_bSelected )
  833. {
  834. // start new box selection if we didnt select an addition object
  835. Vector ptOrg;
  836. pView->ClientToWorld(ptOrg, m_vMouseStart[MOUSE_LEFT] );
  837. // set best third axis value
  838. ptOrg[pView->axThird] = COORD_NOTINIT;
  839. m_pDocument->GetBestVisiblePoint(ptOrg);
  840. if ( uConstraints & constrainSnap )
  841. m_pDocument->Snap(ptOrg,uConstraints);
  842. StartBoxSelection( pView, m_vMouseStart[MOUSE_LEFT], ptOrg );
  843. EnableHandles(true);
  844. }
  845. }
  846. else if (!IsEmpty())
  847. {
  848. //DBG("(%d) OnMouseMove2D: Selection NOT empty, update cursor\n", _nMouseMove);
  849. //
  850. // Just in case the selection set is not empty and "selection" hasn't received a left mouse click.
  851. // (NOTE: this is gross, but unfortunately necessary (cab))
  852. //
  853. UpdateHandleState();
  854. //
  855. // If the cursor is on a handle, the cursor will be set by the HitTest code.
  856. //
  857. bool bFoundTool = false;
  858. if ( GetToolObject( pView, vPoint, false ) )
  859. {
  860. // If they moused over an interactive handle, it should have set the cursor.
  861. hCursor = vgui::dc_crosshair;
  862. bFoundTool = true;
  863. }
  864. // If we haven't moused over any interactive handles contained in the object, see if the
  865. // mouse is over one of the selection handles.
  866. if ( IsEditable && !bFoundTool && HitTest(pView, vPoint, true) )
  867. {
  868. hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
  869. }
  870. }
  871. if ( hCursor != vgui::dc_none )
  872. {
  873. pView->SetCursor( hCursor );
  874. }
  875. return true;
  876. }
  877. void Selection3D::FinishTranslation(bool bSave, bool bClone )
  878. {
  879. const CMapObjectList *pSelList = m_pSelection->GetList();
  880. // keep copy of current objects?
  881. if ( bClone && (GetTranslateMode() == modeMove))
  882. {
  883. GetHistory()->MarkUndoPosition(pSelList, "Clone Objects");
  884. m_pDocument->CloneObjects(*pSelList);
  885. GetHistory()->KeepNew(pSelList);
  886. }
  887. else
  888. {
  889. GetHistory()->MarkUndoPosition(pSelList, "Translation");
  890. GetHistory()->Keep(pSelList);
  891. }
  892. if ( bSave )
  893. {
  894. // transform selected objects
  895. TransformSelection();
  896. }
  897. // finish the tool translation
  898. Box3D::FinishTranslation( bSave );
  899. if ( bSave )
  900. {
  901. // update selection bounds
  902. UpdateSelectionBounds();
  903. NotifyDuplicates(pSelList);
  904. }
  905. m_pSelection->SetSelectionState( SELECT_NORMAL );
  906. }
  907. void Selection3D::StartTranslation(CMapView *pView, const Vector2D &vPoint, const Vector &vHandleOrigin )
  908. {
  909. Vector refPoint;
  910. Vector *pRefPoint = NULL;
  911. // use single object origin as translation origin
  912. if (m_pSelection->GetCount() == 1)
  913. {
  914. if ( vHandleOrigin.IsZero() || m_TranslateMode == modeRotate )
  915. {
  916. CMapClass *pMapClassObj = (CUtlReference< CMapClass>)m_pSelection->GetList()->Element(0);
  917. CMapEntity *pObject = (CMapEntity *)pMapClassObj;
  918. if ( pObject->IsMapClass(MAPCLASS_TYPE(CMapEntity)) && pObject->IsPlaceholder() )
  919. {
  920. // set entity origin as translation center
  921. pObject->GetOrigin( refPoint );
  922. pRefPoint = &refPoint;
  923. }
  924. }
  925. }
  926. // we selected a handle - start translation the selection
  927. // If translating, redo our bounds temporarily to use the entity origins rather than their bounds
  928. // so things will stay on the grid correctly.
  929. Vector vCustomHandleBox[2];
  930. Vector *pCustomHandleBox = NULL;
  931. if ( vHandleOrigin.IsZero() )
  932. {
  933. pCustomHandleBox = vCustomHandleBox;
  934. m_pSelection->GetBoundsForTranslation( vCustomHandleBox[0], vCustomHandleBox[1] );
  935. }
  936. Box3D::StartTranslation( pView, vPoint, vHandleOrigin, pRefPoint, pCustomHandleBox );
  937. if ( !m_pSelection->IsEmpty() )
  938. UpdateSelectionBounds();
  939. m_pSelection->SetSelectionState( SELECT_MODIFY );
  940. }
  941. //-----------------------------------------------------------------------------
  942. // Purpose: Handles left button up events in the 2D view.
  943. // Input : Per CWnd::OnLButtonUp.
  944. // Output : Returns true if the message was handled, false if not.
  945. //-----------------------------------------------------------------------------
  946. bool Selection3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
  947. {
  948. bool bShift = ( nFlags & MK_SHIFT ) ? true : false;
  949. Tool3D::OnLMouseUp2D(pView, nFlags, vPoint);
  950. bool IsEditable = m_pSelection->IsEditable();
  951. if ( IsTranslating() )
  952. {
  953. // selecting stuff in box
  954. if ( IsBoxSelecting() )
  955. {
  956. Box3D::FinishTranslation(true);
  957. if (Options.view2d.bAutoSelect)
  958. {
  959. SelectInBox(m_pDocument, bShift);
  960. UpdateHandleState();
  961. }
  962. }
  963. else
  964. {
  965. FinishTranslation( true, bShift );
  966. }
  967. }
  968. else if ( !m_bSelected && !m_pSelection->IsEmpty() )
  969. {
  970. if ( IsEditable && HitTest(pView, vPoint, false) )
  971. {
  972. ToggleTranslateMode();
  973. UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
  974. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
  975. }
  976. }
  977. // we might have removed some stuff that was relevant:
  978. m_pDocument->UpdateStatusbar();
  979. return true;
  980. }
  981. //-----------------------------------------------------------------------------
  982. // Purpose: Handles left button down events in the 2D view.
  983. // Input : Per CWnd::OnLButtonDown.
  984. // Output : Returns true if the message was handled, false if not.
  985. //-----------------------------------------------------------------------------
  986. bool Selection3D::OnLMouseDownLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint)
  987. {
  988. // First give any selected tool helpers a chance to handle the message.
  989. // Don't hit test against tool helpers when shift is held down
  990. // (beginning a Clone operation).
  991. if (!(nFlags & MK_SHIFT))
  992. {
  993. CBaseTool *pToolHit = GetToolObjectLogical( pView, vPoint, true );
  994. if (pToolHit)
  995. {
  996. // There is a tool. Attach the object to the tool and forward
  997. // the message to the tool.
  998. return pToolHit->OnLMouseDownLogical(pView, nFlags, vPoint);
  999. }
  1000. }
  1001. m_bLButtonDown = true;
  1002. m_vLDownLogicalClient = vPoint;
  1003. pView->SetCapture();
  1004. m_bLeftDragged = false;
  1005. m_bSelected = false;
  1006. if ( m_bInLogicalBoxSelection )
  1007. {
  1008. EndLogicalBoxSelection( );
  1009. }
  1010. // If they weren't alt- or ctrl-clicking and we have a selection, if they clicked
  1011. // in the selection rectangle, maintain what we got.
  1012. bool bCtrlClick = (nFlags & MK_CONTROL) != 0;
  1013. bool bAltClick = GetKeyState(VK_MENU) < 0;
  1014. if ( !bAltClick && !bCtrlClick && !IsEmpty() )
  1015. {
  1016. if ( HitTestLogical( pView, vPoint ) )
  1017. return true;
  1018. }
  1019. if ( bAltClick )
  1020. {
  1021. m_bSelected = ( pView->SelectAtCascading( vPoint, !bCtrlClick ) == true );
  1022. return true;
  1023. }
  1024. m_bSelected = ( pView->SelectAt( vPoint, !bCtrlClick, false ) == true );
  1025. return true;
  1026. }
  1027. //-----------------------------------------------------------------------------
  1028. // Purpose: Handles mouse move events in the 2D visio view.
  1029. // Input : Per CWnd::OnMouseMove.
  1030. // Output : Returns true if the message was handled, false if not.
  1031. //-----------------------------------------------------------------------------
  1032. bool Selection3D::OnMouseMoveLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint)
  1033. {
  1034. if ( m_bLButtonDown )
  1035. {
  1036. if ( !m_bLeftDragged )
  1037. {
  1038. // check if mouse was dragged if button is pressed down
  1039. Vector2D sizeDragged = vPoint - m_vLDownLogicalClient;
  1040. if ((abs(sizeDragged.x) > DRAG_THRESHHOLD) || (abs(sizeDragged.y) > DRAG_THRESHHOLD))
  1041. {
  1042. // If here, means we've dragged the mouse
  1043. m_bLeftDragged = true;
  1044. }
  1045. }
  1046. // Make sure the point is visible.
  1047. pView->ToolScrollToPoint( vPoint );
  1048. }
  1049. // Convert to world coords.
  1050. Vector2D vecWorld;
  1051. pView->ClientToWorld( vecWorld, vPoint );
  1052. // Update status bar position display.
  1053. char szBuf[128];
  1054. sprintf(szBuf, " @%.0f, %.0f ", vecWorld.x, vecWorld.y);
  1055. SetStatusText( SBI_COORDS, szBuf );
  1056. // If we are currently dragging the selection (moving)
  1057. // update that operation based on the current cursor position and keyboard state.
  1058. if ( m_bIsLogicalTranslating )
  1059. {
  1060. Vector2D vecTranslation;
  1061. Vector2DSubtract( vecWorld, m_vLastLogicalDragPoint, vecTranslation );
  1062. m_vLastLogicalDragPoint = vecWorld;
  1063. m_vLogicalTranslation += vecTranslation;
  1064. pView->UpdateView( MAPVIEW_UPDATE_TOOL );
  1065. return true;
  1066. }
  1067. if ( m_bInLogicalBoxSelection && (nFlags & MK_LBUTTON) )
  1068. {
  1069. Vector vecStartWorld;
  1070. pView->ClientToWorld( vecStartWorld, m_vLDownLogicalClient );
  1071. if ( vecWorld.x < vecStartWorld.x )
  1072. {
  1073. m_vecLogicalSelBoxMins.x = vecWorld.x;
  1074. m_vecLogicalSelBoxMaxs.x = vecStartWorld.x;
  1075. }
  1076. else
  1077. {
  1078. m_vecLogicalSelBoxMins.x = vecStartWorld.x;
  1079. m_vecLogicalSelBoxMaxs.x = vecWorld.x;
  1080. }
  1081. if ( vecWorld.y < vecStartWorld.y )
  1082. {
  1083. m_vecLogicalSelBoxMins.y = vecWorld.y;
  1084. m_vecLogicalSelBoxMaxs.y = vecStartWorld.y;
  1085. }
  1086. else
  1087. {
  1088. m_vecLogicalSelBoxMins.y = vecStartWorld.y;
  1089. m_vecLogicalSelBoxMaxs.y = vecWorld.y;
  1090. }
  1091. pView->UpdateView( MAPVIEW_UPDATE_TOOL );
  1092. return true;
  1093. }
  1094. // If we have just started dragging the selection, begin a new translation
  1095. if ( m_bLButtonDown && (nFlags & MK_LBUTTON) && m_bLeftDragged )
  1096. {
  1097. pView->SetCapture();
  1098. // Check to see if the point at which we started clicking lies within the selection region
  1099. if ( HitTestLogical( pView, m_vLDownLogicalClient ) )
  1100. {
  1101. pView->ClientToWorld( m_vLastLogicalDragPoint, m_vLDownLogicalClient );
  1102. m_vLogicalTranslation.Init();
  1103. m_bIsLogicalTranslating = true;
  1104. pView->SetCursor( vgui::dc_sizeall );
  1105. m_pSelection->SetSelectionState( SELECT_MODIFY );
  1106. }
  1107. else if ( !m_bSelected )
  1108. {
  1109. // We're doing a drag with the mouse down, and nothing is selected.
  1110. // Start a logical box selection
  1111. Vector ptOrg;
  1112. pView->ClientToWorld( ptOrg, m_vLDownLogicalClient );
  1113. StartLogicalBoxSelection( pView, ptOrg );
  1114. }
  1115. return true;
  1116. }
  1117. // If we are simply hovering over an object but the mouse isn't down, update the cursor.
  1118. vgui::HCursor hCursor = vgui::dc_arrow;
  1119. if ( !IsEmpty() )
  1120. {
  1121. // If the cursor is on a handle, the cursor will be set by the HitTest code.
  1122. bool bFoundTool = false;
  1123. if ( GetToolObjectLogical( pView, vPoint, false ) )
  1124. {
  1125. // If they moused over an interactive handle, it should have set the cursor.
  1126. hCursor = vgui::dc_crosshair;
  1127. bFoundTool = true;
  1128. }
  1129. // If we haven't moused over any interactive handles contained in the object, see if the
  1130. // mouse is over one of the selection handles.
  1131. if ( !bFoundTool && HitTestLogical(pView, vPoint) )
  1132. {
  1133. hCursor = vgui::dc_sizeall;
  1134. }
  1135. }
  1136. if ( hCursor != vgui::dc_none )
  1137. {
  1138. pView->SetCursor( hCursor );
  1139. }
  1140. return true;
  1141. }
  1142. //-----------------------------------------------------------------------------
  1143. // Purpose: Handles left button up events in the 2D view.
  1144. // Input : Per CWnd::OnLButtonUp.
  1145. // Output : Returns true if the message was handled, false if not.
  1146. //-----------------------------------------------------------------------------
  1147. bool Selection3D::OnLMouseUpLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint)
  1148. {
  1149. bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0);
  1150. ReleaseCapture();
  1151. m_bLButtonDown = false;
  1152. const CMapObjectList *pSelList = m_pSelection->GetList();
  1153. // selecting stuff in box
  1154. if ( m_bInLogicalBoxSelection )
  1155. {
  1156. if ( Options.view2d.bAutoSelect )
  1157. {
  1158. EndLogicalBoxSelection( );
  1159. SelectInLogicalBox( m_pDocument, bShift );
  1160. }
  1161. m_pSelection->SetSelectionState( SELECT_NORMAL );
  1162. goto updateStatusBar;
  1163. }
  1164. if ( m_bIsLogicalTranslating )
  1165. {
  1166. // keep copy of current objects?
  1167. if ( nFlags & MK_SHIFT )
  1168. {
  1169. GetHistory()->MarkUndoPosition(pSelList, "Clone Objects");
  1170. m_pDocument->CloneObjects(*pSelList);
  1171. GetHistory()->KeepNew(pSelList);
  1172. }
  1173. else
  1174. {
  1175. GetHistory()->MarkUndoPosition(pSelList, "Logical Translation");
  1176. GetHistory()->Keep( pSelList );
  1177. }
  1178. TransformLogicalSelection( m_vLogicalTranslation );
  1179. // finish the tool translation
  1180. m_bIsLogicalTranslating = false;
  1181. // update selection bounds
  1182. UpdateSelectionBounds();
  1183. m_pDocument->SetModifiedFlag();
  1184. NotifyDuplicates( pSelList );
  1185. m_pSelection->SetSelectionState( SELECT_NORMAL );
  1186. goto updateStatusBar;
  1187. }
  1188. updateStatusBar:
  1189. // we might have removed some stuff that was relevant:
  1190. m_pDocument->UpdateStatusbar();
  1191. return true;
  1192. }
  1193. //-----------------------------------------------------------------------------
  1194. // Purpose: Handles key down events in the 3D view.
  1195. // Input : Per CWnd::OnKeyDown.
  1196. // Output : Returns true if the message was handled, false if not.
  1197. //-----------------------------------------------------------------------------
  1198. bool Selection3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
  1199. {
  1200. bool bShift = ((GetKeyState(VK_SHIFT) & 0x8000) != 0);
  1201. bool bCtrl = ((GetKeyState(VK_CONTROL) & 0x8000) != 0);
  1202. switch (nChar)
  1203. {
  1204. /*
  1205. dvs: The eyedropper is a somewhat failed experiment, an attempt to create a way to
  1206. quickly hook entities together. I think a dedicated connection tool with a more
  1207. rubber-band style UI might be more successful. Either that or relegate that work
  1208. to a Logical-style view.
  1209. case 'e':
  1210. case 'E':
  1211. {
  1212. m_bEyedropper = !m_bEyedropper;
  1213. if (m_bEyedropper)
  1214. {
  1215. SetEyedropperCursor();
  1216. }
  1217. else
  1218. {
  1219. SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1220. }
  1221. return true;
  1222. }
  1223. */
  1224. #ifndef SDK_BUILD
  1225. case 'x':
  1226. case 'X':
  1227. {
  1228. m_b3DEditMode = !m_b3DEditMode;
  1229. pView->UpdateView( MAPVIEW_UPDATE_TOOL );
  1230. return true;
  1231. }
  1232. #endif
  1233. case VK_DELETE:
  1234. {
  1235. m_pDocument->OnCmdMsg(ID_EDIT_DELETE, CN_COMMAND, NULL, NULL);
  1236. return true;
  1237. }
  1238. case VK_ESCAPE:
  1239. {
  1240. OnEscape(m_pDocument);
  1241. return true;
  1242. }
  1243. }
  1244. if (Options.view2d.bNudge && (nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT))
  1245. {
  1246. if (!IsEmpty())
  1247. {
  1248. bool bSnap = m_pDocument->IsSnapEnabled() && !bCtrl;
  1249. NudgeObjects(pView, nChar, bSnap, bShift);
  1250. return true;
  1251. }
  1252. }
  1253. return false;
  1254. }
  1255. //-----------------------------------------------------------------------------
  1256. // Purpose: Handles double click events in the 3D view.
  1257. // Input : Per CWnd::OnLButtonDblClk.
  1258. // Output : Returns true if the message was handled, false if not.
  1259. //-----------------------------------------------------------------------------
  1260. bool Selection3D::OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  1261. {
  1262. if ( !m_pSelection->IsEmpty() )
  1263. {
  1264. if ( m_pSelection->GetCount() == 1 )
  1265. {
  1266. CMapClass *pObject = (CUtlReference< CMapClass>)m_pSelection->GetList()->Element( 0 );
  1267. CManifestInstance *pManifestInstance = dynamic_cast< CManifestInstance * >( pObject );
  1268. if ( pManifestInstance )
  1269. {
  1270. CManifest *pManifest = CMapDoc::GetManifest();
  1271. if ( pManifest )
  1272. {
  1273. pManifest->SetPrimaryMap( pManifestInstance->GetManifestMap() );
  1274. return true;
  1275. }
  1276. }
  1277. }
  1278. GetMainWnd()->pObjectProperties->ShowWindow(SW_SHOW);
  1279. }
  1280. return true;
  1281. }
  1282. bool Selection3D::OnLMouseDblClkLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint)
  1283. {
  1284. if ( !m_pSelection->IsEmpty() )
  1285. {
  1286. GetMainWnd()->pObjectProperties->ShowWindow(SW_SHOW);
  1287. }
  1288. return true;
  1289. }
  1290. //-----------------------------------------------------------------------------
  1291. // Purpose:
  1292. // Input : pView -
  1293. // point -
  1294. //-----------------------------------------------------------------------------
  1295. void Selection3D::EyedropperPick2D(CMapView2D *pView, const Vector2D &vPoint)
  1296. {
  1297. }
  1298. //-----------------------------------------------------------------------------
  1299. // Purpose:
  1300. // Input : *pView -
  1301. // point -
  1302. //-----------------------------------------------------------------------------
  1303. void Selection3D::EyedropperPick3D(CMapView3D *pView, const Vector2D &vPoint)
  1304. {
  1305. //
  1306. // We only want to do this if we have at least one entity selected.
  1307. //
  1308. if ( !m_pSelection->IsAnEntitySelected() )
  1309. {
  1310. MessageBox( NULL, "No entities are selected, so the eyedropper has nothing to assign to.", "No selected entities", MB_OK);
  1311. return;
  1312. }
  1313. //
  1314. // If they clicked on an entity, get the name of the entity they clicked on.
  1315. //
  1316. ULONG ulFace;
  1317. CMapClass *pClickObject = pView->NearestObjectAt( vPoint, ulFace);
  1318. if (pClickObject != NULL)
  1319. {
  1320. EyedropperPick(pView, pClickObject);
  1321. }
  1322. }
  1323. //-----------------------------------------------------------------------------
  1324. // Purpose:
  1325. // Input : pObject -
  1326. //-----------------------------------------------------------------------------
  1327. void Selection3D::EyedropperPick(CMapView *pView, CMapClass *pObject)
  1328. {
  1329. //
  1330. // The eyedropper currently only supports clicking on entities.
  1331. // TODO: consider using this to fill out face lists if they click on a solid
  1332. //
  1333. CMapEntity *pEntity = FindEntityInTree(pObject);
  1334. if (pEntity == NULL)
  1335. {
  1336. // They clicked on something that is not an entity.
  1337. return;
  1338. }
  1339. //
  1340. // Get the name of the clicked on entity.
  1341. //
  1342. const char *pszClickName = NULL;
  1343. pszClickName = pEntity->GetKeyValue("targetname");
  1344. if (pszClickName == NULL)
  1345. {
  1346. //
  1347. // They clicked on an entity with no name.
  1348. //
  1349. MessageBox( NULL, "The chosen entity has no name.", "No name to pick", MB_OK );
  1350. return;
  1351. }
  1352. //
  1353. // Build a list of all the keyvalues in the selected entities that support the eyedropper.
  1354. //
  1355. CUtlVector<GDinputvariable *> VarList;
  1356. int nEntityCount = 0;
  1357. const CMapObjectList *pSelList = m_pSelection->GetList();
  1358. for (int i = 0; i < pSelList->Count(); i++)
  1359. {
  1360. CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i);
  1361. CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject);
  1362. if (pEntity != NULL)
  1363. {
  1364. nEntityCount++;
  1365. GDclass *pClass = pEntity->GetClass();
  1366. int nVarCount = pClass->GetVariableCount();
  1367. for (int nVar = 0; nVar < nVarCount; nVar++)
  1368. {
  1369. GDinputvariable *pVar = pClass->GetVariableAt(nVar);
  1370. if (pVar && ((pVar->GetType() == ivTargetDest) || (pVar->GetType() == ivTargetNameOrClass)))
  1371. {
  1372. VarList.AddToTail(pVar);
  1373. }
  1374. }
  1375. }
  1376. }
  1377. //
  1378. // Prompt for what keyvalue in the selected entities we are filling out.
  1379. //
  1380. int nCount = VarList.Count();
  1381. if (nCount <= 0)
  1382. {
  1383. //
  1384. // No selected entities have keys of the appropriate type, so there's nothing we can do.
  1385. //
  1386. MessageBox( NULL, "No selected entities have keyvalues that accept an entity name, so the eyedropper has nothing to assign to.", "No eligible keyvalues", MB_OK );
  1387. return;
  1388. }
  1389. //
  1390. // Determine the name of the key that we are filling out.
  1391. //
  1392. GDinputvariable *pVar = ChooseEyedropperVar(pView, VarList);
  1393. if (!pVar)
  1394. {
  1395. return;
  1396. }
  1397. const char *pszVarName = pVar->GetName();
  1398. if (!pszVarName)
  1399. {
  1400. return;
  1401. }
  1402. GetHistory()->MarkUndoPosition( pSelList, "Set Keyvalue");
  1403. //
  1404. // Apply the key to all selected entities with the chosen keyvalue.
  1405. //
  1406. for (int i = 0; i < pSelList->Count(); i++)
  1407. {
  1408. CMapClass *pObject = (CUtlReference< CMapClass>)pSelList->Element(i);
  1409. CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject);
  1410. if (pEntity != NULL)
  1411. {
  1412. GDclass *pClass = pEntity->GetClass();
  1413. GDinputvariable *pVar = pClass->VarForName(pszVarName);
  1414. if (pVar && ((pVar->GetType() == ivTargetDest) || (pVar->GetType() == ivTargetNameOrClass)))
  1415. {
  1416. GetHistory()->Keep(pEntity);
  1417. pEntity->SetKeyValue(pszVarName, pszClickName);
  1418. }
  1419. }
  1420. }
  1421. CMapDoc *pDoc = pView->GetMapDoc();
  1422. if (pDoc != NULL)
  1423. {
  1424. pDoc->SetModifiedFlag();
  1425. }
  1426. }
  1427. //-----------------------------------------------------------------------------
  1428. // Purpose: Returns the nearest CMapEntity object up the hierarchy from the
  1429. // given object.
  1430. // Input : pObject - Object to start from.
  1431. //-----------------------------------------------------------------------------
  1432. CMapEntity *Selection3D::FindEntityInTree(CMapClass *pObject)
  1433. {
  1434. do
  1435. {
  1436. CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject);
  1437. if (pEntity != NULL)
  1438. {
  1439. return pEntity;
  1440. }
  1441. pObject = pObject->GetParent();
  1442. } while (pObject != NULL);
  1443. // No entity in this branch of the object tree.
  1444. return NULL;
  1445. }
  1446. //-----------------------------------------------------------------------------
  1447. // Purpose: Handles left button down events in the 3D view.
  1448. // Input : Per CWnd::OnLButtonDown.
  1449. // Output : Returns true if the message was handled, false if not.
  1450. //-----------------------------------------------------------------------------
  1451. bool Selection3D::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  1452. {
  1453. Tool3D::OnLMouseDown3D(pView, nFlags, vPoint);
  1454. m_bSelected = false;
  1455. //
  1456. // If they are holding down the eyedropper hotkey, do an eyedropper pick. The eyedropper fills out
  1457. // keyvalues in selected entities based on the object they clicked on.
  1458. //
  1459. if (m_bEyedropper)
  1460. {
  1461. EyedropperPick3D(pView, vPoint);
  1462. m_bEyedropper = false;
  1463. SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1464. return true;
  1465. }
  1466. if (nFlags & MK_CONTROL)
  1467. {
  1468. m_bSelected = pView->SelectAt(vPoint, false, false);;
  1469. UpdateHandleState();
  1470. }
  1471. else if ( m_b3DEditMode && HitTest(pView,vPoint, true) )
  1472. {
  1473. // if clicked on handles, never change selection
  1474. if ( !IsBoxSelecting() && m_LastHitTestHandle == vec3_origin )
  1475. {
  1476. // clicked somewhere on our selection tool but maybe something else is inbetween
  1477. HitInfo_t HitData;
  1478. m_bDrawAsSolidBox = true;
  1479. pView->ObjectsAt( vPoint, &HitData, 1 );
  1480. if ( HitData.pObject && !HitData.pObject->IsSelected() )
  1481. {
  1482. m_bSelected = pView->SelectAt(vPoint, true, false);
  1483. UpdateHandleState();
  1484. }
  1485. m_bDrawAsSolidBox = false;
  1486. pView->SetCursor( UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode ) );
  1487. }
  1488. }
  1489. else
  1490. {
  1491. m_TranslateMode = modeScale;
  1492. m_bSelected = pView->SelectAt(vPoint, true, false);
  1493. UpdateHandleState();
  1494. }
  1495. if ( m_bSelected && !m_b3DEditMode )
  1496. {
  1497. pView->BeginPick();
  1498. }
  1499. return true;
  1500. }
  1501. //-----------------------------------------------------------------------------
  1502. // Purpose: Handles left button up events in the 3D view.
  1503. // Input : Per CWnd::OnLButtonUp.
  1504. // Output : Returns true if the message was handled, false if not.
  1505. //-----------------------------------------------------------------------------
  1506. bool Selection3D::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  1507. {
  1508. bool bShift = ( nFlags & MK_SHIFT ) ? true : false;
  1509. Tool3D::OnLMouseUp3D(pView, nFlags, vPoint) ;
  1510. bool IsEditable = m_pSelection->IsEditable();
  1511. if ( IsTranslating() )
  1512. {
  1513. // selecting stuff in box
  1514. if ( IsBoxSelecting() )
  1515. {
  1516. Box3D::FinishTranslation(true);
  1517. if (Options.view2d.bAutoSelect)
  1518. {
  1519. SelectInBox(m_pDocument, bShift);
  1520. UpdateHandleState();
  1521. }
  1522. }
  1523. else
  1524. {
  1525. FinishTranslation( true, bShift );
  1526. }
  1527. }
  1528. else if ( m_b3DEditMode && !m_bSelected && !m_pSelection->IsEmpty() )
  1529. {
  1530. if ( IsEditable && HitTest(pView, vPoint, false) )
  1531. {
  1532. ToggleTranslateMode();
  1533. UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
  1534. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
  1535. }
  1536. }
  1537. pView->EndPick();
  1538. // we might have removed some stuff that was relevant:
  1539. m_pDocument->UpdateStatusbar();
  1540. return true;
  1541. }
  1542. //-----------------------------------------------------------------------------
  1543. // Purpose: Handles mouse move events in the 3D view.
  1544. // Input : Per CWnd::OnMouseMove.
  1545. // Output : Returns true if the message was handled, false if not.
  1546. //-----------------------------------------------------------------------------
  1547. bool Selection3D::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
  1548. {
  1549. Tool3D::OnMouseMove3D(pView, nFlags, vPoint);
  1550. bool IsEditable = m_pSelection->IsEditable();
  1551. vgui::HCursor hCursor = vgui::dc_arrow;
  1552. if ( m_bEyedropper )
  1553. {
  1554. SetEyedropperCursor();
  1555. }
  1556. //
  1557. // If we are currently dragging the selection (moving, scaling, rotating, or shearing)
  1558. // update that operation based on the current cursor position and keyboard state.
  1559. //
  1560. else if ( IsTranslating() )
  1561. {
  1562. unsigned int uConstraints = GetConstraints(nFlags);
  1563. //
  1564. // If they are dragging with a valid handle, update the views.
  1565. //
  1566. Tool3D::UpdateTranslation( pView, vPoint, uConstraints );
  1567. hCursor = vgui::dc_none;
  1568. }
  1569. //
  1570. // Else if we have just started dragging the selection, begin a new translation
  1571. //
  1572. else if ( m_b3DEditMode && m_bMouseDragged[MOUSE_LEFT] )
  1573. {
  1574. if ( IsEditable && HitTest( pView, m_vMouseStart[MOUSE_LEFT], true) )
  1575. {
  1576. // we selected a handle - start translation the selection
  1577. StartTranslation( pView, vPoint, m_LastHitTestHandle );
  1578. hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
  1579. }
  1580. }
  1581. else if ( IsEditable && m_b3DEditMode && !IsEmpty() )
  1582. {
  1583. UpdateHandleState();
  1584. if ( HitTest(pView, vPoint, true) )
  1585. {
  1586. hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
  1587. }
  1588. }
  1589. if ( hCursor != vgui::dc_none )
  1590. {
  1591. pView->SetCursor( hCursor );
  1592. }
  1593. return true;
  1594. }
  1595. //-----------------------------------------------------------------------------
  1596. // Purpose: Sets the cursor to the eyedropper cursor.
  1597. //-----------------------------------------------------------------------------
  1598. void Selection3D::SetEyedropperCursor(void)
  1599. {
  1600. static HCURSOR hcurEyedropper = NULL;
  1601. if (!hcurEyedropper)
  1602. {
  1603. hcurEyedropper = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_EYEDROPPER));
  1604. }
  1605. SetCursor(hcurEyedropper);
  1606. }
  1607. //-----------------------------------------------------------------------------
  1608. // Purpose: Handles the escape key in the 2D or 3D views.
  1609. //-----------------------------------------------------------------------------
  1610. void Selection3D::OnEscape(CMapDoc *pDoc)
  1611. {
  1612. //
  1613. // If we're in eyedropper mode, go back to selection mode.
  1614. //
  1615. if (m_bEyedropper)
  1616. {
  1617. m_bEyedropper = false;
  1618. SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  1619. }
  1620. //
  1621. // If we're box selecting, clear the box.
  1622. //
  1623. else if (IsBoxSelecting())
  1624. {
  1625. EndBoxSelection();
  1626. UpdateSelectionBounds();
  1627. }
  1628. //
  1629. // If we're logical box selecting, clear the box.
  1630. //
  1631. else if ( m_bInLogicalBoxSelection )
  1632. {
  1633. EndLogicalBoxSelection();
  1634. }
  1635. //
  1636. // If we're moving a brush, put it back.
  1637. //
  1638. else if (IsTranslating())
  1639. {
  1640. FinishTranslation(false,false);
  1641. }
  1642. //
  1643. // If we have a selection, deselect it.
  1644. //
  1645. else if (!IsEmpty())
  1646. {
  1647. SetEmpty();
  1648. }
  1649. }