229 lines
6.4 KiB
C++
229 lines
6.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SceneOutlinerHelpers.h"
|
|
#include "ActorTreeItem.h"
|
|
#include "ActorDescTreeItem.h"
|
|
#include "ActorFolderTreeItem.h"
|
|
#include "ComponentTreeItem.h"
|
|
#include "EditorActorFolders.h"
|
|
#include "EditorClassUtils.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "Misc/PackageName.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "WorldPartition/WorldPartitionActorDescInstance.h"
|
|
#include "UObject/Package.h"
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "SceneOutlinerHelpers"
|
|
|
|
namespace SceneOutliner
|
|
{
|
|
FString FSceneOutlinerHelpers::GetExternalPackageName(const ISceneOutlinerTreeItem& TreeItem)
|
|
{
|
|
if (const FActorTreeItem* ActorItem = TreeItem.CastTo<FActorTreeItem>())
|
|
{
|
|
if (AActor* Actor = ActorItem->Actor.Get())
|
|
{
|
|
if (Actor->IsPackageExternal())
|
|
{
|
|
return Actor->GetExternalPackage()->GetName();
|
|
}
|
|
|
|
}
|
|
}
|
|
else if (const FActorFolderTreeItem* ActorFolderItem = TreeItem.CastTo<FActorFolderTreeItem>())
|
|
{
|
|
if (const UActorFolder* ActorFolder = ActorFolderItem->GetActorFolder())
|
|
{
|
|
if (ActorFolder->IsPackageExternal())
|
|
{
|
|
return ActorFolder->GetExternalPackage()->GetName();
|
|
}
|
|
}
|
|
}
|
|
else if (const FActorDescTreeItem* ActorDescItem = TreeItem.CastTo<FActorDescTreeItem>())
|
|
{
|
|
if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle)
|
|
{
|
|
return ActorDescInstance->GetActorPackage().ToString();
|
|
}
|
|
}
|
|
|
|
return FString();
|
|
}
|
|
|
|
UPackage* FSceneOutlinerHelpers::GetExternalPackage(const ISceneOutlinerTreeItem& TreeItem)
|
|
{
|
|
if (const FActorTreeItem* ActorItem = TreeItem.CastTo<FActorTreeItem>())
|
|
{
|
|
if (AActor* Actor = ActorItem->Actor.Get())
|
|
{
|
|
if (Actor->IsPackageExternal())
|
|
{
|
|
return Actor->GetExternalPackage();
|
|
}
|
|
}
|
|
}
|
|
else if (const FActorFolderTreeItem* ActorFolderItem = TreeItem.CastTo<FActorFolderTreeItem>())
|
|
{
|
|
if (const UActorFolder* ActorFolder = ActorFolderItem->GetActorFolder())
|
|
{
|
|
if (ActorFolder->IsPackageExternal())
|
|
{
|
|
return ActorFolder->GetExternalPackage();
|
|
}
|
|
}
|
|
}
|
|
else if (const FActorDescTreeItem* ActorDescItem = TreeItem.CastTo<FActorDescTreeItem>())
|
|
{
|
|
if (const FWorldPartitionActorDescInstance* ActorDescInstance = *ActorDescItem->ActorDescHandle)
|
|
{
|
|
return FindPackage(nullptr, *ActorDescInstance->GetActorPackage().ToString());
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TSharedPtr<SWidget> FSceneOutlinerHelpers::GetClassHyperlink(UObject* InObject)
|
|
{
|
|
if (InObject)
|
|
{
|
|
if (UClass* Class = InObject->GetClass())
|
|
{
|
|
// Always show blueprints
|
|
const bool bIsBlueprintClass = UBlueprint::GetBlueprintFromClass(Class) != nullptr;
|
|
|
|
// Also show game or game plugin native classes (but not engine classes as that makes the scene outliner pretty noisy)
|
|
bool bIsGameClass = false;
|
|
if (!bIsBlueprintClass)
|
|
{
|
|
UPackage* Package = Class->GetOutermost();
|
|
const FString ModuleName = FPackageName::GetShortName(Package->GetFName());
|
|
|
|
FModuleStatus PackageModuleStatus;
|
|
if (FModuleManager::Get().QueryModule(*ModuleName, /*out*/ PackageModuleStatus))
|
|
{
|
|
bIsGameClass = PackageModuleStatus.bIsGameModule;
|
|
}
|
|
}
|
|
|
|
if (bIsBlueprintClass || bIsGameClass)
|
|
{
|
|
FEditorClassUtils::FSourceLinkParams SourceLinkParams;
|
|
SourceLinkParams.Object = InObject;
|
|
SourceLinkParams.bUseDefaultFormat = true;
|
|
|
|
return FEditorClassUtils::GetSourceLink(Class, SourceLinkParams);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void FSceneOutlinerHelpers::PopulateExtraSearchStrings(const ISceneOutlinerTreeItem& TreeItem, TArray< FString >& OutSearchStrings)
|
|
{
|
|
// For components, we want them to be searchable by the actor name if they request so. This is so you can search by actors in component
|
|
// pickers without the actual components themselves being filtered out.
|
|
if (const FComponentTreeItem* ComponentTreeItem = TreeItem.CastTo<FComponentTreeItem>())
|
|
{
|
|
if (ComponentTreeItem->GetSearchComponentByActorName())
|
|
{
|
|
if (const UActorComponent* Component = ComponentTreeItem->Component.Get())
|
|
{
|
|
if (const AActor* Owner = Component->GetOwner())
|
|
{
|
|
constexpr bool bCreateIfNone = false;
|
|
OutSearchStrings.Add(Owner->GetActorLabel(bCreateIfNone));
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FSceneOutlinerHelpers::RenameFolder(const FFolder& InFolder, const FText& NewFolderName, UWorld* World)
|
|
{
|
|
if (!World)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FName NewPath = InFolder.GetParent().GetPath();
|
|
if (NewPath.IsNone())
|
|
{
|
|
NewPath = FName(*NewFolderName.ToString());
|
|
}
|
|
else
|
|
{
|
|
NewPath = FName(*(NewPath.ToString() / NewFolderName.ToString()));
|
|
}
|
|
|
|
const FFolder TreeItemNewFolder(InFolder.GetRootObject(), NewPath);
|
|
FActorFolders::Get().RenameFolderInWorld(*World, InFolder, TreeItemNewFolder);
|
|
}
|
|
|
|
bool FSceneOutlinerHelpers::ValidateFolderName(const FFolder& InFolder, UWorld* World, const FText& InLabel, FText& OutErrorMessage)
|
|
{
|
|
const FText TrimmedLabel = FText::TrimPrecedingAndTrailing(InLabel);
|
|
|
|
if (TrimmedLabel.IsEmpty())
|
|
{
|
|
OutErrorMessage = LOCTEXT("RenameFailed_LeftBlank", "Names cannot be left blank");
|
|
return false;
|
|
}
|
|
|
|
if (TrimmedLabel.ToString().Len() >= NAME_SIZE)
|
|
{
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add(TEXT("CharCount"), NAME_SIZE);
|
|
OutErrorMessage = FText::Format(LOCTEXT("RenameFailed_TooLong", "Names must be less than {CharCount} characters long."), Arguments);
|
|
return false;
|
|
}
|
|
|
|
const FString LabelString = TrimmedLabel.ToString();
|
|
if (InFolder.GetLeafName().ToString() == LabelString)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int32 Dummy = 0;
|
|
if (LabelString.FindChar('/', Dummy) || LabelString.FindChar('\\', Dummy))
|
|
{
|
|
OutErrorMessage = LOCTEXT("RenameFailed_InvalidChar", "Folder names cannot contain / or \\.");
|
|
return false;
|
|
}
|
|
|
|
// Validate that this folder doesn't exist already
|
|
FName NewPath = InFolder.GetParent().GetPath();
|
|
if (NewPath.IsNone())
|
|
{
|
|
NewPath = FName(*LabelString);
|
|
}
|
|
else
|
|
{
|
|
NewPath = FName(*(NewPath.ToString() / LabelString));
|
|
}
|
|
FFolder NewFolder(InFolder.GetRootObject(), NewPath);
|
|
|
|
if (World && FActorFolders::Get().ContainsFolder(*World, NewFolder))
|
|
{
|
|
OutErrorMessage = LOCTEXT("RenameFailed_AlreadyExists", "A folder with this name already exists at this level");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FSceneOutlinerHelpers::IsFolderCurrent(const FFolder& InFolder, UWorld* World)
|
|
{
|
|
if (World)
|
|
{
|
|
return FActorFolders::Get().GetActorEditorContextFolder(*World) == InFolder;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |