EM_Task/UnrealEd/Private/EditorSelectUtils.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

886 lines
30 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "Misc/FeedbackContext.h"
#include "Modules/ModuleManager.h"
#include "Components/ActorComponent.h"
#include "Components/SceneComponent.h"
#include "GameFramework/Actor.h"
#include "Components/PrimitiveComponent.h"
#include "Editor/UnrealEdEngine.h"
#include "Editor/GroupActor.h"
#include "Components/ChildActorComponent.h"
#include "Components/DecalComponent.h"
#include "Kismet2/ComponentEditorUtils.h"
#include "Engine/Selection.h"
#include "EdMode.h"
#include "EditorModeManager.h"
#include "EditorModes.h"
#include "Dialogs/Dialogs.h"
#include "UnrealEdGlobals.h"
#include "ScopedTransaction.h"
#include "Engine/LevelStreaming.h"
#include "LevelUtils.h"
#include "StatsViewerModule.h"
#include "SnappingUtils.h"
#include "Logging/MessageLog.h"
#include "ActorGroupingUtils.h"
#include "Subsystems/BrushEditingSubsystem.h"
#define LOCTEXT_NAMESPACE "EditorSelectUtils"
DEFINE_LOG_CATEGORY_STATIC(LogEditorSelectUtils, Log, All);
/*-----------------------------------------------------------------------------
Globals.
-----------------------------------------------------------------------------*/
// Click flags.
enum EViewportClick
{
CF_MOVE_ACTOR = 1, // Set if the actors have been moved since first click
CF_MOVE_TEXTURE = 2, // Set if textures have been adjusted since first click
CF_MOVE_ALL = (CF_MOVE_ACTOR | CF_MOVE_TEXTURE),
};
/*-----------------------------------------------------------------------------
Change transacting.
-----------------------------------------------------------------------------*/
void UUnrealEdEngine::NoteActorMovement()
{
if (!GUndo && !(GEditor->ClickFlags & CF_MOVE_ACTOR))
{
GEditor->ClickFlags |= CF_MOVE_ACTOR;
const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "ActorMovement", "Actor Movement"));
GLevelEditorModeTools().Snapping = 0;
AActor* SelectedActor = NULL;
for (FSelectionIterator It(GetSelectedActorIterator()); It; ++It)
{
AActor* Actor = static_cast<AActor*>(*It);
checkSlow(Actor->IsA(AActor::StaticClass()));
SelectedActor = Actor;
break;
}
if (SelectedActor == NULL)
{
USelection* SelectedActors = GetSelectedActors();
SelectedActors->Modify();
SelectActor(GWorld->GetDefaultBrush(), true, true);
}
// Look for an actor that requires snapping.
for (FSelectionIterator It(GetSelectedActorIterator()); It; ++It)
{
AActor* Actor = static_cast<AActor*>(*It);
checkSlow(Actor->IsA(AActor::StaticClass()));
GLevelEditorModeTools().Snapping = 1;
break;
}
TSet<AGroupActor*> GroupActors;
// Modify selected actors.
for (FSelectionIterator It(GetSelectedActorIterator()); It; ++It)
{
AActor* Actor = static_cast<AActor*>(*It);
checkSlow(Actor->IsA(AActor::StaticClass()));
Actor->Modify();
if (UActorGroupingUtils::IsGroupingActive())
{
// if this actor is in a group, add the GroupActor into a list to be modified shortly
AGroupActor* ActorLockedRootGroup = AGroupActor::GetRootForActor(Actor, true);
if (ActorLockedRootGroup != nullptr)
{
GroupActors.Add(ActorLockedRootGroup);
}
}
}
// Modify unique group actors
for (auto* GroupActor: GroupActors)
{
GroupActor->Modify();
}
}
}
void UUnrealEdEngine::FinishAllSnaps()
{
if (!IsRunningCommandlet())
{
if (ClickFlags & CF_MOVE_ACTOR)
{
ClickFlags &= ~CF_MOVE_ACTOR;
for (FSelectionIterator It(GetSelectedActorIterator()); It; ++It)
{
AActor* Actor = static_cast<AActor*>(*It);
checkSlow(Actor->IsA(AActor::StaticClass()));
Actor->Modify();
Actor->InvalidateLightingCache();
Actor->PostEditMove(true);
}
}
}
}
void UUnrealEdEngine::Cleanse(bool ClearSelection, bool Redraw, const FText& Reason)
{
if (GIsRunning)
{
FMessageLog("MapCheck").NewPage(LOCTEXT("MapCheck", "Map Check"));
FMessageLog("LightingResults").NewPage(LOCTEXT("LightingBuildNewLogPage", "Lighting Build"));
FStatsViewerModule& StatsViewerModule = FModuleManager::Get().LoadModuleChecked<FStatsViewerModule>(TEXT("StatsViewer"));
StatsViewerModule.Clear();
}
Super::Cleanse(ClearSelection, Redraw, Reason);
}
FVector UUnrealEdEngine::GetPivotLocation()
{
return GLevelEditorModeTools().PivotLocation;
}
void UUnrealEdEngine::SetPivot(FVector NewPivot, bool bSnapPivotToGrid, bool bIgnoreAxis, bool bAssignPivot /*=false*/)
{
FEditorModeTools& EditorModeTools = GLevelEditorModeTools();
if (!bIgnoreAxis)
{
// Don't stomp on orthonormal axis.
// TODO: this breaks if there is genuinely a need to set the pivot to a coordinate containing a zero component
if (NewPivot.X == 0)
NewPivot.X = EditorModeTools.PivotLocation.X;
if (NewPivot.Y == 0)
NewPivot.Y = EditorModeTools.PivotLocation.Y;
if (NewPivot.Z == 0)
NewPivot.Z = EditorModeTools.PivotLocation.Z;
}
// Set the pivot.
EditorModeTools.SetPivotLocation(NewPivot, false);
if (bSnapPivotToGrid)
{
FRotator DummyRotator(0, 0, 0);
FSnappingUtils::SnapToBSPVertex(EditorModeTools.SnappedLocation, EditorModeTools.GridBase, DummyRotator);
EditorModeTools.PivotLocation = EditorModeTools.SnappedLocation;
}
// Check all actors.
int32 Count = 0, SnapCount = 0;
// default to using the x axis for the translate rotate widget
EditorModeTools.TranslateRotateXAxisAngle = 0.0f;
EditorModeTools.TranslateRotate2DAngle = 0.0f;
FVector TranslateRotateWidgetWorldXAxis;
FVector Widget2DWorldXAxis;
AActor* LastSelectedActor = NULL;
for (FSelectionIterator It(GetSelectedActorIterator()); It; ++It)
{
AActor* Actor = static_cast<AActor*>(*It);
checkSlow(Actor->IsA(AActor::StaticClass()));
if (Count == 0)
{
TranslateRotateWidgetWorldXAxis = Actor->ActorToWorld().TransformVector(FVector(1.0f, 0.0f, 0.0f));
// get the xy plane project of this vector
TranslateRotateWidgetWorldXAxis.Z = 0.0f;
if (!TranslateRotateWidgetWorldXAxis.Normalize())
{
TranslateRotateWidgetWorldXAxis = FVector(1.0f, 0.0f, 0.0f);
}
Widget2DWorldXAxis = Actor->ActorToWorld().TransformVector(FVector(1, 0, 0));
Widget2DWorldXAxis.Y = 0;
if (!Widget2DWorldXAxis.Normalize())
{
Widget2DWorldXAxis = FVector(1, 0, 0);
}
}
LastSelectedActor = Actor;
++Count;
++SnapCount;
}
if (bAssignPivot && LastSelectedActor && UActorGroupingUtils::IsGroupingActive())
{
// set group pivot for the root-most group
AGroupActor* ActorGroupRoot = AGroupActor::GetRootForActor(LastSelectedActor, true, true);
if (ActorGroupRoot)
{
ActorGroupRoot->SetActorLocation(EditorModeTools.PivotLocation, false);
}
}
// if there are multiple actors selected, just use the x-axis for the "translate/rotate" or 2D widgets
if (Count == 1)
{
EditorModeTools.TranslateRotateXAxisAngle = TranslateRotateWidgetWorldXAxis.Rotation().Yaw;
EditorModeTools.TranslateRotate2DAngle = FMath::RadiansToDegrees(FMath::Atan2(Widget2DWorldXAxis.Z, Widget2DWorldXAxis.X));
}
// Update showing.
EditorModeTools.PivotShown = SnapCount > 0 || Count > 1;
}
void UUnrealEdEngine::ResetPivot()
{
GLevelEditorModeTools().PivotShown = 0;
GLevelEditorModeTools().Snapping = 0;
GLevelEditorModeTools().SnappedActor = 0;
}
/*-----------------------------------------------------------------------------
Selection.
-----------------------------------------------------------------------------*/
void UUnrealEdEngine::SetActorSelectionFlags(AActor* InActor)
{
TInlineComponentArray<UActorComponent*> Components;
InActor->GetComponents(Components);
// for every component in the actor
for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++)
{
UActorComponent* Component = Components[ComponentIndex];
if (Component->IsRegistered())
{
// If we have a 'child actor' component, want to update its visible selection state
UChildActorComponent* ChildActorComponent = Cast<UChildActorComponent>(Component);
if (ChildActorComponent != NULL && ChildActorComponent->GetChildActor() != NULL)
{
SetActorSelectionFlags(ChildActorComponent->GetChildActor());
}
UPrimitiveComponent* PrimComponent = Cast<UPrimitiveComponent>(Component);
if (PrimComponent != NULL && PrimComponent->IsRegistered())
{
PrimComponent->PushSelectionToProxy();
}
UDecalComponent* DecalComponent = Cast<UDecalComponent>(Component);
if (DecalComponent != NULL) // && DecalComponent->IsRegistered())
{
DecalComponent->PushSelectionToProxy();
}
}
}
}
void UUnrealEdEngine::SetPivotMovedIndependently(bool bMovedIndependently)
{
bPivotMovedIndependently = bMovedIndependently;
}
bool UUnrealEdEngine::IsPivotMovedIndependently() const
{
return bPivotMovedIndependently;
}
void UUnrealEdEngine::UpdatePivotLocationForSelection(bool bOnChange)
{
// Pick a new common pivot, or not.
AActor* SingleActor = nullptr;
USceneComponent* SingleComponent = nullptr;
if (GetSelectedComponentCount() > 0)
{
for (FSelectedEditableComponentIterator It(*GetSelectedComponents()); It; ++It)
{
UActorComponent* Component = CastChecked<UActorComponent>(*It);
AActor* ComponentOwner = Component->GetOwner();
if (ComponentOwner != nullptr)
{
USelection* SelectedActors = GetSelectedActors();
const bool bIsOwnerSelected = SelectedActors->IsSelected(ComponentOwner);
ensureMsgf(bIsOwnerSelected, TEXT("Owner(%s) of %s is not selected"), *ComponentOwner->GetFullName(), *Component->GetFullName());
if (ComponentOwner->GetWorld() == GWorld)
{
SingleActor = ComponentOwner;
if (Component->IsA<USceneComponent>())
{
SingleComponent = CastChecked<USceneComponent>(Component);
}
const bool IsTemplate = ComponentOwner->IsTemplate();
const bool LevelLocked = !FLevelUtils::IsLevelLocked(ComponentOwner->GetLevel());
check(IsTemplate || LevelLocked);
}
}
}
}
else
{
for (FSelectionIterator It(GetSelectedActorIterator()); It; ++It)
{
AActor* Actor = static_cast<AActor*>(*It);
checkSlow(Actor->IsA(AActor::StaticClass()));
const bool IsTemplate = Actor->IsTemplate();
const bool LevelLocked = !FLevelUtils::IsLevelLocked(Actor->GetLevel());
check(IsTemplate || LevelLocked);
SingleActor = Actor;
}
}
if (SingleComponent != NULL)
{
SetPivot(SingleComponent->GetComponentLocation(), false, true);
}
else if (SingleActor != NULL)
{
UBrushEditingSubsystem* BrushSubsystem = GEditor->GetEditorSubsystem<UBrushEditingSubsystem>();
const bool bGeometryMode = BrushSubsystem ? BrushSubsystem->IsGeometryEditorModeActive() : false;
// For geometry mode use current pivot location as it's set to selected face, not actor
if (!bGeometryMode || bOnChange == true)
{
// Set pivot point to the actor's location, accounting for any set pivot offset
FVector PivotPoint = SingleActor->GetTransform().TransformPosition(SingleActor->GetPivotOffset());
// If grouping is active, see if this actor is part of a locked group and use that pivot instead
if (UActorGroupingUtils::IsGroupingActive())
{
AGroupActor* ActorGroupRoot = AGroupActor::GetRootForActor(SingleActor, true, true);
if (ActorGroupRoot)
{
PivotPoint = ActorGroupRoot->GetActorLocation();
}
}
SetPivot(PivotPoint, false, true);
}
}
else
{
ResetPivot();
}
SetPivotMovedIndependently(false);
}
void UUnrealEdEngine::NoteSelectionChange(bool bNotify)
{
// The selection changed, so make sure the pivot (widget) is located in the right place
UpdatePivotLocationForSelection(true);
// Clear active editing visualizer on selection change
ComponentVisManager.ClearActiveComponentVis();
GLevelEditorModeTools().ActorSelectionChangeNotify();
const bool bComponentSelectionChanged = GetSelectedComponentCount() > 0;
if (bNotify)
{
USelection* Selection = bComponentSelectionChanged ? GetSelectedComponents() : GetSelectedActors();
USelection::SelectionChangedEvent.Broadcast(Selection);
}
if (!bComponentSelectionChanged)
{
// whenever selection changes, recompute whether the selection contains a locked actor
bCheckForLockActors = true;
// whenever selection changes, recompute whether the selection contains a world info actor
bCheckForWorldSettingsActors = true;
UpdateFloatingPropertyWindows();
}
RedrawLevelEditingViewports();
}
void UUnrealEdEngine::SelectGroup(AGroupActor* InGroupActor, bool bForceSelection /*=false*/, bool bInSelected /*=true*/, bool bNotify /*=true*/)
{
USelection* SelectedActors = GetSelectedActors();
bool bStartedBatchSelect = false;
if (!SelectedActors->IsBatchSelecting())
{
bStartedBatchSelect = true;
// These will have already been called when batch selecting
SelectedActors->BeginBatchSelectOperation();
SelectedActors->Modify();
}
static bool bIteratingGroups = false;
if (!bIteratingGroups)
{
bIteratingGroups = true;
// Select all actors within the group (if locked or forced)
if (bForceSelection || InGroupActor->IsLocked())
{
TArray<AActor*> GroupActors;
InGroupActor->GetGroupActors(GroupActors);
for (int32 ActorIndex = 0; ActorIndex < GroupActors.Num(); ++ActorIndex)
{
SelectActor(GroupActors[ActorIndex], bInSelected, false);
}
bForceSelection = true;
// Recursively select any subgroups
TArray<AGroupActor*> SubGroups;
InGroupActor->GetSubGroups(SubGroups);
for (int32 GroupIndex = 0; GroupIndex < SubGroups.Num(); ++GroupIndex)
{
SelectGroup(SubGroups[GroupIndex], bForceSelection, bInSelected, false);
}
}
if (bStartedBatchSelect)
{
SelectedActors->EndBatchSelectOperation(bNotify);
}
if (bNotify)
{
NoteSelectionChange();
}
// whenever selection changes, recompute whether the selection contains a locked actor
bCheckForLockActors = true;
// whenever selection changes, recompute whether the selection contains a world info actor
bCheckForWorldSettingsActors = true;
bIteratingGroups = false;
}
}
bool UUnrealEdEngine::CanSelectActor(AActor* Actor, bool bInSelected, bool bSelectEvenIfHidden, bool bWarnIfLevelLocked) const
{
// If selections are globally locked, leave.
if (!Actor || !Actor->GetLevel() || GEdSelectionLock || !Actor->IsEditable())
{
return false;
}
// Only abort from hidden actors if we are selecting. You can deselect hidden actors without a problem.
if (bInSelected)
{
// If the actor is NULL or hidden, leave.
if (!bSelectEvenIfHidden && (Actor->IsHiddenEd() || !FLevelUtils::IsLevelVisible(Actor->GetLevel())))
{
return false;
}
// If the actor explicitly makes itself unselectable, leave.
if (!Actor->IsSelectable())
{
return false;
}
// Ensure that neither the level nor the actor is being destroyed or is unreachable
const EObjectFlags InvalidSelectableFlags = RF_BeginDestroyed;
if (Actor->GetLevel()->HasAnyFlags(InvalidSelectableFlags) || Actor->GetLevel()->IsPendingKillOrUnreachable())
{
UE_LOG(LogEditorSelectUtils, Warning, TEXT("SelectActor: %s (%s)"), TEXT("The requested operation could not be completed because the level has invalid flags."), *Actor->GetActorLabel());
return false;
}
if (Actor->HasAnyFlags(InvalidSelectableFlags) || Actor->IsPendingKillOrUnreachable())
{
UE_LOG(LogEditorSelectUtils, Warning, TEXT("SelectActor: %s (%s)"), TEXT("The requested operation could not be completed because the actor has invalid flags."), *Actor->GetActorLabel());
return false;
}
if (!Actor->IsTemplate() && FLevelUtils::IsLevelLocked(Actor->GetLevel()))
{
if (bWarnIfLevelLocked)
{
UE_LOG(LogEditorSelectUtils, Warning, TEXT("SelectActor: %s (%s)"), TEXT("The requested operation could not be completed because the level is locked."), *Actor->GetActorLabel());
}
return false;
}
}
// If grouping operations are not currently allowed, don't select groups.
AGroupActor* SelectedGroupActor = Cast<AGroupActor>(Actor);
if (SelectedGroupActor && !UActorGroupingUtils::IsGroupingActive())
{
return false;
}
// Allow active modes to determine whether the selection is allowed.
return GLevelEditorModeTools().IsSelectionAllowed(Actor, bInSelected);
}
void UUnrealEdEngine::SelectActor(AActor* Actor, bool bInSelected, bool bNotify, bool bSelectEvenIfHidden, bool bForceRefresh)
{
const bool bWarnIfLevelLocked = true;
if (!CanSelectActor(Actor, bInSelected, bSelectEvenIfHidden, bWarnIfLevelLocked))
{
return;
}
bool bSelectionHandled = GLevelEditorModeTools().IsSelectionHandled(Actor, bInSelected);
// Select the actor and update its internals.
if (!bSelectionHandled)
{
if (bInSelected)
{
// If trying to select an Actor spawned by a ChildActorComponent, instead iterate up the hierarchy until we find a valid actor to select
while (Actor->IsChildActor())
{
Actor = Actor->GetParentComponent()->GetOwner();
}
}
if (UActorGroupingUtils::IsGroupingActive())
{
// if this actor is a group, do a group select/deselect
AGroupActor* SelectedGroupActor = Cast<AGroupActor>(Actor);
if (SelectedGroupActor)
{
SelectGroup(SelectedGroupActor, true, bInSelected, bNotify);
}
else
{
// Select/Deselect this actor's entire group, starting from the top locked group.
// If none is found, just use the actor.
AGroupActor* ActorLockedRootGroup = AGroupActor::GetRootForActor(Actor, true);
if (ActorLockedRootGroup)
{
SelectGroup(ActorLockedRootGroup, false, bInSelected, bNotify);
}
}
}
// Don't do any work if the actor's selection state is already the selected state.
const bool bActorSelected = Actor->IsSelected();
if (bActorSelected != bInSelected)
{
if (bInSelected)
{
UE_LOG(LogEditorSelectUtils, Verbose, TEXT("Selected Actor: %s"), *Actor->GetClass()->GetName());
}
else
{
UE_LOG(LogEditorSelectUtils, Verbose, TEXT("Deselected Actor: %s"), *Actor->GetClass()->GetName());
}
GetSelectedActors()->Select(Actor, bInSelected);
if (!bInSelected)
{
if (GetSelectedComponentCount() > 0)
{
GetSelectedComponents()->Modify();
}
GetSelectedComponents()->BeginBatchSelectOperation();
for (UActorComponent* Component: Actor->GetComponents())
{
if (Component)
{
GetSelectedComponents()->Deselect(Component);
// Remove the selection override delegates from the deselected components
if (USceneComponent* SceneComponent = Cast<USceneComponent>(Component))
{
FComponentEditorUtils::BindComponentSelectionOverride(SceneComponent, false);
}
}
}
GetSelectedComponents()->EndBatchSelectOperation(false);
}
else
{
// Bind the override delegates for the components in the selected actor
for (UActorComponent* Component: Actor->GetComponents())
{
if (USceneComponent* SceneComponent = Cast<USceneComponent>(Component))
{
FComponentEditorUtils::BindComponentSelectionOverride(SceneComponent, true);
}
}
}
if (bNotify)
{
NoteSelectionChange();
}
// whenever selection changes, recompute whether the selection contains a locked actor
bCheckForLockActors = true;
// whenever selection changes, recompute whether the selection contains a world info actor
bCheckForWorldSettingsActors = true;
}
else
{
if (bNotify || bForceRefresh)
{
// reset the property windows. In case something has changed since previous selection
UpdateFloatingPropertyWindows(bForceRefresh);
}
}
// A fast path to mark selection rather than reconnecting ALL components for ALL actors that have changed state
SetActorSelectionFlags(Actor);
}
}
void UUnrealEdEngine::SelectComponent(UActorComponent* Component, bool bInSelected, bool bNotify, bool bSelectEvenIfHidden)
{
// Don't do any work if the component's selection state matches the target selection state
const bool bComponentSelected = GetSelectedComponents()->IsSelected(Component);
if ((bComponentSelected && !bInSelected) || (!bComponentSelected && bInSelected))
{
if (bInSelected)
{
UE_LOG(LogEditorSelectUtils, Verbose, TEXT("Selected Component: %s"), *Component->GetClass()->GetName());
}
else
{
UE_LOG(LogEditorSelectUtils, Verbose, TEXT("Deselected Component: %s"), *Component->GetClass()->GetName());
}
GetSelectedComponents()->Select(Component, bInSelected);
// Make sure the override delegate is bound properly
USceneComponent* SceneComponent = Cast<USceneComponent>(Component);
if (SceneComponent)
{
FComponentEditorUtils::BindComponentSelectionOverride(SceneComponent, true);
}
// Update the selection visualization
AActor* ComponentOwner = Component->GetOwner();
if (ComponentOwner != nullptr)
{
TInlineComponentArray<UPrimitiveComponent*> PrimitiveComponents;
ComponentOwner->GetComponents(PrimitiveComponents);
for (int32 Idx = 0; Idx < PrimitiveComponents.Num(); ++Idx)
{
PrimitiveComponents[Idx]->PushSelectionToProxy();
}
}
if (bNotify)
{
NoteSelectionChange();
}
}
}
bool UUnrealEdEngine::IsComponentSelected(const UPrimitiveComponent* PrimComponent)
{
bool bIsSelected = false;
if (GetSelectedComponentCount() > 0)
{
const UActorComponent* PotentiallySelectedComponent = nullptr;
AActor* ComponentOwner = PrimComponent->GetOwner();
if (ComponentOwner->IsChildActor())
{
do
{
PotentiallySelectedComponent = ComponentOwner->GetParentComponent();
ComponentOwner = ComponentOwner->GetParentActor();
} while (ComponentOwner->IsChildActor());
}
else
{
PotentiallySelectedComponent = (PrimComponent->IsVisualizationComponent() ? PrimComponent->GetAttachParent() : PrimComponent);
}
bIsSelected = GetSelectedComponents()->IsSelected(PotentiallySelectedComponent);
}
return bIsSelected;
}
void UUnrealEdEngine::SelectBSPSurf(UModel* InModel, int32 iSurf, bool bSelected, bool bNoteSelectionChange)
{
if (GEdSelectionLock)
{
return;
}
FBspSurf& Surf = InModel->Surfs[iSurf];
InModel->ModifySurf(iSurf, false);
if (bSelected)
{
Surf.PolyFlags |= PF_Selected;
}
else
{
Surf.PolyFlags &= ~PF_Selected;
}
if (bNoteSelectionChange)
{
NoteSelectionChange();
}
// whenever selection changes, recompute whether the selection contains a locked actor
bCheckForLockActors = true;
// whenever selection changes, recompute whether the selection contains a world info actor
bCheckForWorldSettingsActors = true;
}
/**
* Deselects all BSP surfaces in the specified level.
*
* @param Level The level for which to deselect all levels.
* @return The number of surfaces that were deselected
*/
static uint32 DeselectAllSurfacesForLevel(ULevel* Level)
{
uint32 NumSurfacesDeselected = 0;
if (Level)
{
UModel* Model = Level->Model;
for (int32 SurfaceIndex = 0; SurfaceIndex < Model->Surfs.Num(); ++SurfaceIndex)
{
FBspSurf& Surf = Model->Surfs[SurfaceIndex];
if (Surf.PolyFlags & PF_Selected)
{
Model->ModifySurf(SurfaceIndex, false);
Surf.PolyFlags &= ~PF_Selected;
++NumSurfacesDeselected;
}
}
}
return NumSurfacesDeselected;
}
void UUnrealEdEngine::DeselectAllSurfaces()
{
UWorld* World = GWorld;
if (World)
{
DeselectAllSurfacesForLevel(World->PersistentLevel);
for (ULevelStreaming* StreamingLevel: World->GetStreamingLevels())
{
if (StreamingLevel)
{
if (ULevel* Level = StreamingLevel->GetLoadedLevel())
{
DeselectAllSurfacesForLevel(Level);
}
}
}
}
}
void UUnrealEdEngine::SelectNone(bool bNoteSelectionChange, bool bDeselectBSPSurfs, bool WarnAboutManyActors)
{
if (GEdSelectionLock)
{
return;
}
bool bShowProgress = false;
// If there are a lot of actors to process, pop up a warning "are you sure?" box
if (WarnAboutManyActors)
{
int32 NumSelectedActors = GEditor->GetSelectedActorCount();
if (NumSelectedActors >= EditorActorSelectionDefs::MaxActorsToSelectBeforeWarning)
{
bShowProgress = true;
const FText ConfirmText = FText::Format(NSLOCTEXT("UnrealEd", "Warning_ManyActorsForDeselect", "There are {0} selected actors. Are you sure you want to deselect them all?"), FText::AsNumber(NumSelectedActors));
FSuppressableWarningDialog::FSetupInfo Info(ConfirmText, NSLOCTEXT("UnrealEd", "Warning_ManyActors", "Warning: Many Actors"), "Warning_ManyActors");
Info.ConfirmText = NSLOCTEXT("ModalDialogs", "ManyActorsForDeselectConfirm", "Continue Deselection");
Info.CancelText = NSLOCTEXT("ModalDialogs", "ManyActorsForDeselectCancel", "Keep Current Selection");
FSuppressableWarningDialog ManyActorsWarning(Info);
if (ManyActorsWarning.ShowModal() == FSuppressableWarningDialog::Cancel)
{
return;
}
}
}
if (bShowProgress)
{
GWarn->BeginSlowTask(LOCTEXT("BeginDeselectingActorsTaskMessage", "Deselecting Actors"), true);
}
// Make a list of selected actors . . .
TArray<AActor*> ActorsToDeselect;
for (FSelectionIterator It(GetSelectedActorIterator()); It; ++It)
{
AActor* Actor = static_cast<AActor*>(*It);
checkSlow(Actor->IsA(AActor::StaticClass()));
ActorsToDeselect.Add(Actor);
}
USelection* SelectedActors = GetSelectedActors();
SelectedActors->BeginBatchSelectOperation();
SelectedActors->Modify();
// . . . and deselect them.
for (int32 ActorIndex = 0; ActorIndex < ActorsToDeselect.Num(); ++ActorIndex)
{
AActor* Actor = ActorsToDeselect[ActorIndex];
SelectActor(Actor, false, false);
}
uint32 NumDeselectSurfaces = 0;
UWorld* World = GWorld;
if (bDeselectBSPSurfs && World)
{
// Unselect all surfaces in all levels.
NumDeselectSurfaces += DeselectAllSurfacesForLevel(World->PersistentLevel);
for (ULevelStreaming* StreamingLevel: World->GetStreamingLevels())
{
if (StreamingLevel)
{
if (ULevel* Level = StreamingLevel->GetLoadedLevel())
{
NumDeselectSurfaces += DeselectAllSurfacesForLevel(Level);
}
}
}
}
SelectedActors->EndBatchSelectOperation(bNoteSelectionChange);
// prevents clicking on background multiple times spamming selection changes
if (ActorsToDeselect.Num() || NumDeselectSurfaces)
{
if (bNoteSelectionChange)
{
NoteSelectionChange();
}
// whenever selection changes, recompute whether the selection contains a locked actor
bCheckForLockActors = true;
// whenever selection changes, recompute whether the selection contains a world info actor
bCheckForWorldSettingsActors = true;
}
if (bShowProgress)
{
GWarn->EndSlowTask();
}
}
#undef LOCTEXT_NAMESPACE