diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/Selection.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'hammer/Selection.cpp')
| -rw-r--r-- | hammer/Selection.cpp | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/hammer/Selection.cpp b/hammer/Selection.cpp new file mode 100644 index 0000000..7a4fe47 --- /dev/null +++ b/hammer/Selection.cpp @@ -0,0 +1,612 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The document. Exposes functions for object creation, deletion, and +// manipulation. Holds the current tool. Handles GUI messages that are +// view-independent. +// +//=============================================================================// + + +#include "stdafx.h" +#include "Selection.h" +#include "mapdoc.h" +#include "MapHelper.h" +#include "MapSolid.h" +#include "Manifest.h" +#include "mapdefs.h" +#include "globalfunctions.h" +#include "mainfrm.h" +#include "objectproperties.h" + +CSelection::CSelection(void) +{ + m_pDocument = NULL; +} + +CSelection::~CSelection(void) +{ + +} + +void CSelection::Init( CMapDoc *pDocument ) +{ + m_pDocument = pDocument; + m_eSelectMode = selectGroups; + m_SelectionList.Purge(); + ClearHitList(); + + m_LastValidBounds.bmins = Vector(0, 0, 0); + m_LastValidBounds.bmaxs = Vector(64, 64, 64); + + UpdateSelectionBounds(); +} + +bool CSelection::IsSelected(CMapClass *pobj) +{ + return (m_SelectionList.Find(pobj) != m_SelectionList.InvalidIndex()); +} + + +void CSelection::GetLastValidBounds(Vector &vecMins, Vector &vecMaxs) +{ + vecMins = m_LastValidBounds.bmins; + vecMaxs = m_LastValidBounds.bmaxs; +} + +bool CSelection::GetBounds(Vector &vecMins, Vector &vecMaxs) +{ + if ( m_bBoundsDirty ) + UpdateSelectionBounds(); + + if ( m_SelectionList.Count() == 0) + return false; + + vecMins = m_Bounds.bmins; + vecMaxs = m_Bounds.bmaxs; + + return true;; +} + +bool CSelection::GetLogicalBounds(Vector2D &vecMins, Vector2D &vecMaxs) +{ + if ( m_bBoundsDirty ) + UpdateSelectionBounds(); + + if ( m_SelectionList.Count() == 0) + return false; + + vecMins = m_vecLogicalMins; + vecMaxs = m_vecLogicalMaxs; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Used for translations. Uses entity origins and brush bounds. +// That way, when moving stuff, the entity origins will stay on the grid. +//----------------------------------------------------------------------------- +void CSelection::GetBoundsForTranslation( Vector &vecMins, Vector &vecMaxs ) +{ + vecMins.Init( COORD_NOTINIT, COORD_NOTINIT, 0 ); + vecMaxs.Init( -COORD_NOTINIT, -COORD_NOTINIT, 0 ); + + for (int i = 0; i < m_SelectionList.Count(); i++) + { + CMapClass *pobj = m_SelectionList[i]; + + // update physical bounds + Vector mins, maxs; + + CEditGameClass *pEdit = dynamic_cast< CEditGameClass* >( pobj ); + if ( (pEdit && pEdit->IsSolidClass()) || dynamic_cast<CMapSolid *>(pobj) ) + { + pobj->GetRender2DBox(mins, maxs); + } + else + { + pobj->GetOrigin( mins ); + maxs = mins; + } + + VectorMin( mins, vecMins, vecMins ); + VectorMax( maxs, vecMaxs, vecMaxs ); + } +} + +void CSelection::UpdateSelectionBounds( void ) +{ + m_Bounds.ResetBounds(); + + m_vecLogicalMins[0] = m_vecLogicalMins[1] = COORD_NOTINIT; + m_vecLogicalMaxs[0] = m_vecLogicalMaxs[1] = -COORD_NOTINIT; + + for (int i = 0; i < m_SelectionList.Count(); i++) + { + CMapClass *pobj = m_SelectionList[i]; + + // update physical bounds + Vector mins,maxs; + pobj->GetRender2DBox(mins, maxs); + m_Bounds.UpdateBounds(mins, maxs); + + // update logical bounds + Vector2D logicalMins,logicalMaxs; + pobj->GetRenderLogicalBox( logicalMins, logicalMaxs ); + Vector2DMin( logicalMins, m_vecLogicalMins, m_vecLogicalMins ); + Vector2DMax( logicalMaxs, m_vecLogicalMaxs, m_vecLogicalMaxs ); + } + + // remeber bounds if valid + if ( m_Bounds.IsValidBox() ) + { + m_LastValidBounds = m_Bounds; + } + + m_bBoundsDirty = false; +} + +bool CSelection::GetBoundsCenter(Vector &vecCenter) +{ + if ( m_bBoundsDirty ) + UpdateSelectionBounds(); + + if ( m_SelectionList.Count() == 0 ) + return false; + + m_Bounds.GetBoundsCenter( vecCenter ); + + return true; +} + +bool CSelection::GetLogicalBoundsCenter( Vector2D &vecCenter ) +{ + if ( m_bBoundsDirty ) + UpdateSelectionBounds(); + + if ( m_SelectionList.Count() == 0 ) + return false; + + vecCenter = (m_vecLogicalMins+m_vecLogicalMaxs)/2; + + return true; +} + +bool CSelection::IsEmpty() +{ + return m_SelectionList.Count() == 0; +} + +const CMapObjectList *CSelection::GetList() +{ + return &m_SelectionList; +} + +const CMapObjectList* CSelection::GetHitList() +{ + return &m_HitList; +} + +int CSelection::GetCount() +{ + return m_SelectionList.Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns the current selection mode. The selection mode determines +// what gets selected when the user clicks on something - the group, +// the entity, or the solid. +//----------------------------------------------------------------------------- +SelectMode_t CSelection::GetMode() +{ + return m_eSelectMode; +} + +void CSelection::SetSelectionState(SelectionState_t eSelectionState) +{ + for ( int i=0; i<m_SelectionList.Count(); i++ ) + { + CMapEntity *pObject = (CMapEntity *)m_SelectionList.Element(i); + pObject->SetSelectionState( eSelectionState ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CSelection::IsAnEntitySelected(void) +{ + if (m_SelectionList.Count() > 0) + { + int nSelCount = m_SelectionList.Count(); + for (int i = 0; i < nSelCount; i++) + { + CMapClass *pObject = m_SelectionList.Element(i); + CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject); + if (pEntity != NULL) + { + return true; + } + } + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the selection is editable. Every object must be +// individually editable for this routine to return true. +//----------------------------------------------------------------------------- +bool CSelection::IsEditable() +{ + if ( m_SelectionList.Count() > 0 ) + { + int nSelCount = m_SelectionList.Count(); + for (int i = 0; i < nSelCount; i++) + { + CMapClass *pObject = m_SelectionList.Element(i); + + if ( pObject->IsEditable() == false ) + { + return false; + } + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the selection is copyable. CManifestInstance classes +// are not copyable. +//----------------------------------------------------------------------------- +bool CSelection::IsCopyable() +{ + if ( m_SelectionList.Count() > 0 ) + { + int nSelCount = m_SelectionList.Count(); + for (int i = 0; i < nSelCount; i++) + { + CMapClass *pObject = m_SelectionList.Element(i); + + if ( pObject->IsMapClass( MAPCLASS_TYPE( CManifestInstance ) ) ) + { + return false; + } + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Sets the current selection mode, which determines which objects +// are selected when the user clicks on things. +//----------------------------------------------------------------------------- +void CSelection::SetMode(SelectMode_t eNewSelectMode) +{ + SelectMode_t eOldSelectMode = m_eSelectMode; + m_eSelectMode = eNewSelectMode; + + if ((eOldSelectMode == selectSolids) || + ((eOldSelectMode == selectObjects) && (eNewSelectMode == selectGroups))) + { + // + // If we are going from a more specific selection mode to a less specific one, + // clear the selection. This avoids unexpectedly selecting new things. + // + SelectObject(NULL, scClear|scSaveChanges); + } + else + { + // + // Put all the children of the selected objects in a list, along with their children. + // + CMapObjectList NewList; + int nSelCount = m_SelectionList.Count(); + for (int i = 0; i < nSelCount; i++) + { + CMapClass *pObject = m_SelectionList[i]; + AddLeavesToListCallback(pObject, &NewList); + pObject->EnumChildren((ENUMMAPCHILDRENPROC)AddLeavesToListCallback, (DWORD)&NewList); + } + + SelectObject(NULL, scClear|scSaveChanges); + + // + // Add child objects to selection. + // + for (int pos=0;pos<NewList.Count();pos++) + { + CMapClass *pObject = NewList[pos]; + CMapClass *pSelObject = pObject->PrepareSelection(eNewSelectMode); + if (pSelObject) + { + SelectObject(pSelObject, scSelect); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pObject - +//----------------------------------------------------------------------------- +void CSelection::AddHit(CMapClass *pObject) +{ + if ( m_HitList.Find(pObject) == -1 ) + { + m_HitList.AddToTail(pObject); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSelection::ClearHitList(void) +{ + m_HitList.RemoveAll(); + m_iCurHit = -1; +} + +bool CSelection::RemoveAll(void) +{ + for ( int i=0;i<m_SelectionList.Count(); i++ ) + { + CMapClass *pObject = m_SelectionList.Element(i); + pObject->SetSelectionState(SELECT_NONE); + } + + m_SelectionList.RemoveAll(); + SetBoundsDirty(); + + return true; +} + +bool CSelection::RemoveDead(void) +{ + bool bFoundOne = false; + + for ( int i=m_SelectionList.Count()-1; i>=0; i-- ) + { + CMapClass *pObject = m_SelectionList.Element(i); + if (!pObject->GetParent()) + { + m_SelectionList.FastRemove(i); + pObject->SetSelectionState(SELECT_NONE); + bFoundOne = true; + } + } + + // TODO check if we do the same as in SelectObject + SetBoundsDirty(); + + return bFoundOne; +} + +//----------------------------------------------------------------------------- +// Purpose: Removes objects that are not visible from the selection set. +//----------------------------------------------------------------------------- +bool CSelection::RemoveInvisibles(void) +{ + bool bFoundOne = false; + + for ( int i=m_SelectionList.Count()-1; i>=0; i-- ) + { + CMapClass *pObject = m_SelectionList.Element(i); + if ( !pObject->IsVisible() ) + { + m_SelectionList.FastRemove(i); + pObject->SetSelectionState(SELECT_NONE); + bFoundOne = true; + } + } + + SetBoundsDirty(); + + return bFoundOne; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : iIndex - +// bUpdateViews - +//----------------------------------------------------------------------------- +void CSelection::SetCurrentHit(int iIndex, bool bCascading) +{ + if ( m_HitList.Count() == 0) + { + Assert( m_iCurHit == -1); + return; + } + + // save & toggle old selection off + if (m_iCurHit != -1) + { + CMapClass *pObject = m_HitList[m_iCurHit]; + SelectObject(pObject, scToggle|scSaveChanges); + } + + if (iIndex == hitNext) + { + // hit next object + m_iCurHit++; + } + else if (iIndex == hitPrev) + { + // hit prev object + m_iCurHit--; + } + else + { + m_iCurHit = iIndex; + } + + // make sure curhit is valid + if (m_iCurHit >= m_HitList.Count()) + { + m_iCurHit = 0; + } + else if (m_iCurHit < 0) + { + m_iCurHit = m_HitList.Count() - 1; + } + + CMapClass *pObject = m_HitList[m_iCurHit]; + + if ( bCascading ) + { + // Build actual selection list based on cascading... + CUtlRBTree< CMapClass*, unsigned short > tree( 0, 0, DefLessFunc( CMapClass* ) ); + + bool bRecursive = false; // not used yet + m_pDocument->BuildCascadingSelectionList( pObject, tree, bRecursive ); + + CMapObjectList list; + list.AddToTail( pObject ); + bool bRootIsSelected = IsSelected(pObject); + bool bUniformSelectionState = true; + + for ( unsigned short h = tree.FirstInorder(); h != tree.InvalidIndex(); h = tree.NextInorder(h) ) + { + list.AddToTail( list[h] ); + + if ( IsSelected( list[h] ) != bRootIsSelected ) + { + bUniformSelectionState = false; + } + } + + /* Change toggle to select or unselect if we're toggling and cascading + // but the root + children have different selection state + if ( ( !bUniformSelectionState ) && ( cmd == scToggle ) ) + { + cmd = bRootIsSelected ? scSelect : scUnselect; + }*/ + + SelectObjectList( &list, scSelect ); + } + else + { + SelectObject(pObject, scToggle ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : pobj - +// cmd - +//----------------------------------------------------------------------------- +bool CSelection::SelectObject(CMapClass *pObj, int cmd) +{ + // if no object is given we only can execute the clear command + if ( pObj == NULL ) + { + // check if selection is already empty + if (m_SelectionList.Count() == 0) + return false; // nothing to do + + if ( cmd & scClear ) + { + RemoveAll(); + } + } + else // object oriented operation + { + int iIndex = m_SelectionList.Find(pObj); + bool bAlreadySelected = iIndex != -1; + + if ( cmd & scToggle ) + { + if ( bAlreadySelected ) + cmd |= scUnselect; + else + cmd |= scSelect; + } + + if ( cmd & scSelect ) + { + if ( cmd & scClear ) + { + // if we re-selected the only selected element, nothing changes + if ( bAlreadySelected && m_SelectionList.Count() == 1 ) + return false; + + RemoveAll(); + bAlreadySelected = false; // reset that flag + } + + if ( bAlreadySelected ) + return false; + + m_SelectionList.AddToTail(pObj); + pObj->SetSelectionState(SELECT_NORMAL); + } + else if ( (cmd & scUnselect) && bAlreadySelected ) + { + // ok unselect an yet selected object + m_SelectionList.Remove(iIndex); + pObj->SetSelectionState(SELECT_NONE); + } + else + { + return false; // nothing was changed + } + } + + // ok something in the selection was changed, set dirty flags + SetBoundsDirty(); + + if ( cmd & scSaveChanges ) + { + // changing the selection automatically saves changes made to the properties dialog + GetMainWnd()->pObjectProperties->SaveData(); + } + + // always mark data dirty + GetMainWnd()->pObjectProperties->MarkDataDirty(); + + // uddate all views + m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_SELECTION ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Clears the current selection and selects everything in the given list. +// Input : pList - Objects to select. +//----------------------------------------------------------------------------- +void CSelection::SelectObjectList( const CMapObjectList *pList, int cmd ) +{ + // Clear the current selection. + + // Clear the current selection. + if ( cmd & scSaveChanges ) + { + GetMainWnd()->pObjectProperties->SaveData(); + cmd &= ~scSaveChanges; + } + + if ( cmd & scClear ) + { + RemoveAll(); + cmd &= ~scClear; + } + + if ( pList != NULL ) + { + for (int pos=0;pos<pList->Count();pos++) + { + CMapClass *pObject = pList->Element(pos); + CMapClass *pSelObject = pObject->PrepareSelection( m_eSelectMode ); + if (pSelObject) + { + SelectObject( pSelObject, cmd ); + } + } + } +} |