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.

626 lines
15 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: The document. Exposes functions for object creation, deletion, and
  4. // manipulation. Holds the current tool. Handles GUI messages that are
  5. // view-independent.
  6. //
  7. //=============================================================================//
  8. #include "stdafx.h"
  9. #include "Selection.h"
  10. #include "mapdoc.h"
  11. #include "MapHelper.h"
  12. #include "MapSolid.h"
  13. #include "Manifest.h"
  14. #include "mapdefs.h"
  15. #include "globalfunctions.h"
  16. #include "mainfrm.h"
  17. #include "objectproperties.h"
  18. CSelection::CSelection(void)
  19. {
  20. m_pDocument = NULL;
  21. }
  22. CSelection::~CSelection(void)
  23. {
  24. }
  25. void CSelection::Init( CMapDoc *pDocument )
  26. {
  27. m_pDocument = pDocument;
  28. m_eSelectMode = selectGroups;
  29. m_SelectionList.RemoveAll();
  30. ClearHitList();
  31. m_LastValidBounds.bmins = Vector(0, 0, 0);
  32. m_LastValidBounds.bmaxs = Vector(64, 64, 64);
  33. UpdateSelectionBounds();
  34. }
  35. bool CSelection::IsSelected(CMapClass *pobj)
  36. {
  37. return (m_SelectionList.Find(pobj) != m_SelectionList.InvalidIndex());
  38. }
  39. void CSelection::GetLastValidBounds(Vector &vecMins, Vector &vecMaxs)
  40. {
  41. vecMins = m_LastValidBounds.bmins;
  42. vecMaxs = m_LastValidBounds.bmaxs;
  43. }
  44. bool CSelection::GetBounds(Vector &vecMins, Vector &vecMaxs)
  45. {
  46. if ( m_bBoundsDirty )
  47. UpdateSelectionBounds();
  48. if ( m_SelectionList.Count() == 0)
  49. return false;
  50. vecMins = m_Bounds.bmins;
  51. vecMaxs = m_Bounds.bmaxs;
  52. return true;;
  53. }
  54. bool CSelection::GetLogicalBounds(Vector2D &vecMins, Vector2D &vecMaxs)
  55. {
  56. if ( m_bBoundsDirty )
  57. UpdateSelectionBounds();
  58. if ( m_SelectionList.Count() == 0)
  59. return false;
  60. vecMins = m_vecLogicalMins;
  61. vecMaxs = m_vecLogicalMaxs;
  62. return true;
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose: Used for translations. Uses entity origins and brush bounds.
  66. // That way, when moving stuff, the entity origins will stay on the grid.
  67. //-----------------------------------------------------------------------------
  68. void CSelection::GetBoundsForTranslation( Vector &vecMins, Vector &vecMaxs )
  69. {
  70. vecMins.Init( COORD_NOTINIT, COORD_NOTINIT, 0 );
  71. vecMaxs.Init( -COORD_NOTINIT, -COORD_NOTINIT, 0 );
  72. // If there are any solids, then only use the bounds for those. Otherwise,
  73. // an entity that is off the grid can pull all the solids off the grid and you never want that.
  74. int nSolids = 0;
  75. for (int i = 0; i < m_SelectionList.Count(); i++)
  76. {
  77. CMapClass *pobj = m_SelectionList[i];
  78. CEditGameClass *pEdit = dynamic_cast< CEditGameClass* >( pobj );
  79. if ( (pEdit && pEdit->IsSolidClass()) || dynamic_cast<CMapSolid *>(pobj) )
  80. {
  81. ++nSolids;
  82. }
  83. }
  84. for (int i = 0; i < m_SelectionList.Count(); i++)
  85. {
  86. CMapClass *pobj = m_SelectionList[i];
  87. // update physical bounds
  88. Vector mins, maxs;
  89. CEditGameClass *pEdit = dynamic_cast< CEditGameClass* >( pobj );
  90. if ( (pEdit && pEdit->IsSolidClass()) || dynamic_cast<CMapSolid *>(pobj) )
  91. {
  92. pobj->GetRender2DBox(mins, maxs);
  93. }
  94. else if ( nSolids == 0 )
  95. {
  96. pobj->GetOrigin( mins );
  97. maxs = mins;
  98. }
  99. VectorMin( mins, vecMins, vecMins );
  100. VectorMax( maxs, vecMaxs, vecMaxs );
  101. }
  102. }
  103. void CSelection::UpdateSelectionBounds( void )
  104. {
  105. m_Bounds.ResetBounds();
  106. m_vecLogicalMins[0] = m_vecLogicalMins[1] = COORD_NOTINIT;
  107. m_vecLogicalMaxs[0] = m_vecLogicalMaxs[1] = -COORD_NOTINIT;
  108. for (int i = 0; i < m_SelectionList.Count(); i++)
  109. {
  110. CMapClass *pobj = m_SelectionList[i];
  111. // update physical bounds
  112. Vector mins,maxs;
  113. pobj->GetRender2DBox(mins, maxs);
  114. m_Bounds.UpdateBounds(mins, maxs);
  115. // update logical bounds
  116. Vector2D logicalMins,logicalMaxs;
  117. pobj->GetRenderLogicalBox( logicalMins, logicalMaxs );
  118. Vector2DMin( logicalMins, m_vecLogicalMins, m_vecLogicalMins );
  119. Vector2DMax( logicalMaxs, m_vecLogicalMaxs, m_vecLogicalMaxs );
  120. }
  121. // remeber bounds if valid
  122. if ( m_Bounds.IsValidBox() )
  123. {
  124. m_LastValidBounds = m_Bounds;
  125. }
  126. m_bBoundsDirty = false;
  127. }
  128. bool CSelection::GetBoundsCenter(Vector &vecCenter)
  129. {
  130. if ( m_bBoundsDirty )
  131. UpdateSelectionBounds();
  132. if ( m_SelectionList.Count() == 0 )
  133. return false;
  134. m_Bounds.GetBoundsCenter( vecCenter );
  135. return true;
  136. }
  137. bool CSelection::GetLogicalBoundsCenter( Vector2D &vecCenter )
  138. {
  139. if ( m_bBoundsDirty )
  140. UpdateSelectionBounds();
  141. if ( m_SelectionList.Count() == 0 )
  142. return false;
  143. vecCenter = (m_vecLogicalMins+m_vecLogicalMaxs)/2;
  144. return true;
  145. }
  146. bool CSelection::IsEmpty()
  147. {
  148. return m_SelectionList.Count() == 0;
  149. }
  150. const CMapObjectList *CSelection::GetList()
  151. {
  152. return &m_SelectionList;
  153. }
  154. const CMapObjectList* CSelection::GetHitList()
  155. {
  156. return &m_HitList;
  157. }
  158. int CSelection::GetCount()
  159. {
  160. return m_SelectionList.Count();
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Purpose: Returns the current selection mode. The selection mode determines
  164. // what gets selected when the user clicks on something - the group,
  165. // the entity, or the solid.
  166. //-----------------------------------------------------------------------------
  167. SelectMode_t CSelection::GetMode()
  168. {
  169. return m_eSelectMode;
  170. }
  171. void CSelection::SetSelectionState(SelectionState_t eSelectionState)
  172. {
  173. for ( int i=0; i<m_SelectionList.Count(); i++ )
  174. {
  175. CMapClass *pMapClass = (CUtlReference< CMapClass >)m_SelectionList.Element(i);
  176. CMapEntity *pObject = (CMapEntity *)pMapClass;
  177. pObject->SetSelectionState( eSelectionState );
  178. }
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose:
  182. //-----------------------------------------------------------------------------
  183. bool CSelection::IsAnEntitySelected(void)
  184. {
  185. if (m_SelectionList.Count() > 0)
  186. {
  187. int nSelCount = m_SelectionList.Count();
  188. for (int i = 0; i < nSelCount; i++)
  189. {
  190. CMapClass *pObject = m_SelectionList.Element(i);
  191. CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject);
  192. if (pEntity != NULL)
  193. {
  194. return true;
  195. }
  196. }
  197. }
  198. return false;
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose: Returns true if the selection is editable. Every object must be
  202. // individually editable for this routine to return true.
  203. //-----------------------------------------------------------------------------
  204. bool CSelection::IsEditable()
  205. {
  206. if ( m_SelectionList.Count() > 0 )
  207. {
  208. int nSelCount = m_SelectionList.Count();
  209. for (int i = 0; i < nSelCount; i++)
  210. {
  211. CMapClass *pObject = m_SelectionList.Element(i);
  212. if ( pObject->IsEditable() == false )
  213. {
  214. return false;
  215. }
  216. }
  217. }
  218. return true;
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Returns true if the selection is copyable. CManifestInstance classes
  222. // are not copyable.
  223. //-----------------------------------------------------------------------------
  224. bool CSelection::IsCopyable()
  225. {
  226. if ( m_SelectionList.Count() > 0 )
  227. {
  228. int nSelCount = m_SelectionList.Count();
  229. for (int i = 0; i < nSelCount; i++)
  230. {
  231. CMapClass *pObject = m_SelectionList.Element(i);
  232. if ( pObject->IsMapClass( MAPCLASS_TYPE( CManifestInstance ) ) )
  233. {
  234. return false;
  235. }
  236. }
  237. }
  238. return true;
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Sets the current selection mode, which determines which objects
  242. // are selected when the user clicks on things.
  243. //-----------------------------------------------------------------------------
  244. void CSelection::SetMode(SelectMode_t eNewSelectMode)
  245. {
  246. SelectMode_t eOldSelectMode = m_eSelectMode;
  247. m_eSelectMode = eNewSelectMode;
  248. if ((eOldSelectMode == selectSolids) ||
  249. ((eOldSelectMode == selectObjects) && (eNewSelectMode == selectGroups)))
  250. {
  251. //
  252. // If we are going from a more specific selection mode to a less specific one,
  253. // clear the selection. This avoids unexpectedly selecting new things.
  254. //
  255. SelectObject(NULL, scClear|scSaveChanges);
  256. }
  257. else
  258. {
  259. //
  260. // Put all the children of the selected objects in a list, along with their children.
  261. //
  262. CMapObjectList NewList;
  263. int nSelCount = m_SelectionList.Count();
  264. for (int i = 0; i < nSelCount; i++)
  265. {
  266. CMapClass *pObject = m_SelectionList[i];
  267. AddLeavesToListCallback(pObject, &NewList);
  268. pObject->EnumChildren((ENUMMAPCHILDRENPROC)AddLeavesToListCallback, (DWORD)&NewList);
  269. }
  270. SelectObject(NULL, scClear|scSaveChanges);
  271. //
  272. // Add child objects to selection.
  273. //
  274. for (int pos=0;pos<NewList.Count();pos++)
  275. {
  276. CMapClass *pObject = NewList[pos];
  277. CMapClass *pSelObject = pObject->PrepareSelection(eNewSelectMode);
  278. if (pSelObject)
  279. {
  280. SelectObject(pSelObject, scSelect);
  281. }
  282. }
  283. }
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Purpose:
  287. // Input : *pObject -
  288. //-----------------------------------------------------------------------------
  289. void CSelection::AddHit(CMapClass *pObject)
  290. {
  291. if ( m_HitList.Find(pObject) == -1 )
  292. {
  293. m_HitList.AddToTail(pObject);
  294. }
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose:
  298. //-----------------------------------------------------------------------------
  299. void CSelection::ClearHitList(void)
  300. {
  301. m_HitList.RemoveAll();
  302. m_iCurHit = -1;
  303. }
  304. bool CSelection::RemoveAll(void)
  305. {
  306. for ( int i=0;i<m_SelectionList.Count(); i++ )
  307. {
  308. CMapClass *pObject = m_SelectionList.Element(i);
  309. pObject->SetSelectionState(SELECT_NONE);
  310. }
  311. m_SelectionList.RemoveAll();
  312. SetBoundsDirty();
  313. return true;
  314. }
  315. bool CSelection::RemoveDead(void)
  316. {
  317. bool bFoundOne = false;
  318. for ( int i=m_SelectionList.Count()-1; i>=0; i-- )
  319. {
  320. CMapClass *pObject = m_SelectionList.Element(i);
  321. if (!pObject->GetParent())
  322. {
  323. m_SelectionList.FastRemove(i);
  324. pObject->SetSelectionState(SELECT_NONE);
  325. bFoundOne = true;
  326. }
  327. }
  328. // TODO check if we do the same as in SelectObject
  329. SetBoundsDirty();
  330. return bFoundOne;
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose: Removes objects that are not visible from the selection set.
  334. //-----------------------------------------------------------------------------
  335. bool CSelection::RemoveInvisibles(void)
  336. {
  337. bool bFoundOne = false;
  338. for ( int i=m_SelectionList.Count()-1; i>=0; i-- )
  339. {
  340. CMapClass *pObject = m_SelectionList.Element(i);
  341. if ( !pObject->IsVisible() )
  342. {
  343. m_SelectionList.FastRemove(i);
  344. pObject->SetSelectionState(SELECT_NONE);
  345. bFoundOne = true;
  346. }
  347. }
  348. SetBoundsDirty();
  349. return bFoundOne;
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Purpose:
  353. // Input : iIndex -
  354. // bUpdateViews -
  355. //-----------------------------------------------------------------------------
  356. void CSelection::SetCurrentHit(int iIndex, bool bCascading)
  357. {
  358. if ( m_HitList.Count() == 0)
  359. {
  360. Assert( m_iCurHit == -1);
  361. return;
  362. }
  363. // save & toggle old selection off
  364. if (m_iCurHit != -1)
  365. {
  366. CMapClass *pObject = m_HitList[m_iCurHit];
  367. SelectObject(pObject, scToggle|scSaveChanges);
  368. }
  369. if (iIndex == hitNext)
  370. {
  371. // hit next object
  372. m_iCurHit++;
  373. }
  374. else if (iIndex == hitPrev)
  375. {
  376. // hit prev object
  377. m_iCurHit--;
  378. }
  379. else
  380. {
  381. m_iCurHit = iIndex;
  382. }
  383. // make sure curhit is valid
  384. if (m_iCurHit >= m_HitList.Count())
  385. {
  386. m_iCurHit = 0;
  387. }
  388. else if (m_iCurHit < 0)
  389. {
  390. m_iCurHit = m_HitList.Count() - 1;
  391. }
  392. CMapClass *pObject = m_HitList[m_iCurHit];
  393. if ( bCascading )
  394. {
  395. // Build actual selection list based on cascading...
  396. CUtlRBTree< CMapClass*, unsigned short > tree( 0, 0, DefLessFunc( CMapClass* ) );
  397. bool bRecursive = false; // not used yet
  398. m_pDocument->BuildCascadingSelectionList( pObject, tree, bRecursive );
  399. CMapObjectList list;
  400. list.AddToTail( pObject );
  401. bool bRootIsSelected = IsSelected(pObject);
  402. bool bUniformSelectionState = true;
  403. for ( unsigned short h = tree.FirstInorder(); h != tree.InvalidIndex(); h = tree.NextInorder(h) )
  404. {
  405. list.AddToTail( list[h] );
  406. if ( IsSelected( list[h] ) != bRootIsSelected )
  407. {
  408. bUniformSelectionState = false;
  409. }
  410. }
  411. /* Change toggle to select or unselect if we're toggling and cascading
  412. // but the root + children have different selection state
  413. if ( ( !bUniformSelectionState ) && ( cmd == scToggle ) )
  414. {
  415. cmd = bRootIsSelected ? scSelect : scUnselect;
  416. }*/
  417. SelectObjectList( &list, scSelect );
  418. }
  419. else
  420. {
  421. SelectObject(pObject, scToggle );
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose:
  426. // Input : pobj -
  427. // cmd -
  428. //-----------------------------------------------------------------------------
  429. bool CSelection::SelectObject(CMapClass *pObj, int cmd)
  430. {
  431. // if no object is given we only can execute the clear command
  432. if ( pObj == NULL )
  433. {
  434. // check if selection is already empty
  435. if (m_SelectionList.Count() == 0)
  436. return false; // nothing to do
  437. if ( cmd & scClear )
  438. {
  439. RemoveAll();
  440. }
  441. }
  442. else // object oriented operation
  443. {
  444. int iIndex = m_SelectionList.Find(pObj);
  445. bool bAlreadySelected = iIndex != -1;
  446. if ( cmd & scToggle )
  447. {
  448. if ( bAlreadySelected )
  449. cmd |= scUnselect;
  450. else
  451. cmd |= scSelect;
  452. }
  453. if ( cmd & scSelect )
  454. {
  455. if ( cmd & scClear )
  456. {
  457. // if we re-selected the only selected element, nothing changes
  458. if ( bAlreadySelected && m_SelectionList.Count() == 1 )
  459. return false;
  460. RemoveAll();
  461. bAlreadySelected = false; // reset that flag
  462. }
  463. if ( bAlreadySelected )
  464. return false;
  465. m_SelectionList.AddToTail(pObj);
  466. pObj->SetSelectionState(SELECT_NORMAL);
  467. }
  468. else if ( (cmd & scUnselect) && bAlreadySelected )
  469. {
  470. // ok unselect an yet selected object
  471. m_SelectionList.FastRemove(iIndex);
  472. pObj->SetSelectionState(SELECT_NONE);
  473. }
  474. else
  475. {
  476. return false; // nothing was changed
  477. }
  478. }
  479. // ok something in the selection was changed, set dirty flags
  480. SetBoundsDirty();
  481. if ( cmd & scSaveChanges )
  482. {
  483. // changing the selection automatically saves changes made to the properties dialog
  484. GetMainWnd()->pObjectProperties->SaveData( SAVEDATA_SELECTION_CHANGED );
  485. }
  486. // always mark data dirty
  487. GetMainWnd()->pObjectProperties->MarkDataDirty();
  488. // uddate all views
  489. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_SELECTION );
  490. return true;
  491. }
  492. //-----------------------------------------------------------------------------
  493. // Purpose: Clears the current selection and selects everything in the given list.
  494. // Input : pList - Objects to select.
  495. //-----------------------------------------------------------------------------
  496. void CSelection::SelectObjectList( const CMapObjectList *pList, int cmd )
  497. {
  498. // Clear the current selection.
  499. // Clear the current selection.
  500. if ( cmd & scSaveChanges )
  501. {
  502. GetMainWnd()->pObjectProperties->SaveData( SAVEDATA_SELECTION_CHANGED );
  503. cmd &= ~scSaveChanges;
  504. }
  505. if ( cmd & scClear )
  506. {
  507. RemoveAll();
  508. cmd &= ~scClear;
  509. }
  510. if ( pList != NULL )
  511. {
  512. for (int pos=0;pos<pList->Count();pos++)
  513. {
  514. CMapClass *pObject = (CUtlReference< CMapClass >)pList->Element(pos);
  515. CMapClass *pSelObject = pObject->PrepareSelection( m_eSelectMode );
  516. if (pSelObject)
  517. {
  518. SelectObject( pSelObject, cmd );
  519. }
  520. }
  521. }
  522. }