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.

1984 lines
52 KiB

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