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.

612 lines
14 KiB

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