Files
UnrealEngineUWP/Engine/Source/Editor/ContentBrowser/Private/AssetContextMenu.cpp
Josh Markiewicz c18a424666 Weekly Fort Dev -> Main integration from UE4-Fortnite-CL-2245134
MCP
 "app" : "fortnite",
  "moduleName" : "Fortnite-PublicService",
  "branch" : "TRUNK",
  "build" : "306",
  "cln" : "2245028",
  "version" : "UNKNOWN"

[CL 2247600 by Josh Markiewicz in Main branch]
2014-08-07 17:34:29 -04:00

1723 lines
51 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "ContentBrowserPCH.h"
#include "AssetViewTypes.h"
#include "AssetContextMenu.h"
#include "ContentBrowserModule.h"
#include "Editor/UnrealEd/Public/ObjectTools.h"
#include "Editor/UnrealEd/Public/PackageTools.h"
#include "Editor/UnrealEd/Public/FileHelpers.h"
#include "Editor/PropertyEditor/Public/PropertyEditorModule.h"
#include "Toolkits/AssetEditorManager.h"
#include "Toolkits/ToolkitManager.h"
#include "ConsolidateWindow.h"
#include "ReferenceViewer.h"
#include "ReferencedAssetsUtils.h"
#include "ISourceControlModule.h"
#include "ISourceControlRevision.h"
#include "SourceControlWindows.h"
#include "KismetEditorUtilities.h"
#include "AssetToolsModule.h"
#include "ComponentAssetBroker.h"
#include "SNumericEntryBox.h"
#include "SourceCodeNavigation.h"
#include "IDocumentation.h"
#include "EditorClassUtils.h"
#define LOCTEXT_NAMESPACE "ContentBrowser"
FAssetContextMenu::FAssetContextMenu(const TWeakPtr<SAssetView>& InAssetView)
: AssetView(InAssetView)
, bAtLeastOneNonRedirectorSelected(false)
, bCanExecuteSCCCheckOut(false)
, bCanExecuteSCCOpenForAdd(false)
, bCanExecuteSCCCheckIn(false)
, bCanExecuteSCCHistory(false)
, bCanExecuteSCCRevert(false)
, bCanExecuteSCCSync(false)
{
}
TSharedRef<SWidget> FAssetContextMenu::MakeContextMenu(const TArray<FAssetData>& InSelectedAssets, const FSourcesData& InSourcesData, TSharedPtr< FUICommandList > InCommandList)
{
SelectedAssets = InSelectedAssets;
SourcesData = InSourcesData;
// Cache any vars that are used in determining if you can execute any actions.
// Useful for actions whose "CanExecute" will not change or is expensive to calculate.
CacheCanExecuteVars();
// Get all menu extenders for this context menu from the content browser module
FContentBrowserModule& ContentBrowserModule = FModuleManager::GetModuleChecked<FContentBrowserModule>( TEXT("ContentBrowser") );
TArray<FContentBrowserMenuExtender_SelectedAssets> MenuExtenderDelegates = ContentBrowserModule.GetAllAssetViewContextMenuExtenders();
TArray<TSharedPtr<FExtender>> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute(SelectedAssets));
}
}
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, InCommandList, MenuExtender);
// Only add something if at least one asset is selected
if ( SelectedAssets.Num() )
{
// Add any type-specific context menu options
AddAssetTypeMenuOptions(MenuBuilder);
// Add quick access to common commands.
AddCommonMenuOptions(MenuBuilder);
// Add reference options
AddReferenceMenuOptions(MenuBuilder);
// Add documentation options
AddDocumentationMenuOptions(MenuBuilder);
// Add source control options
AddSourceControlMenuOptions(MenuBuilder);
// Add collection options
AddCollectionMenuOptions(MenuBuilder);
}
return MenuBuilder.MakeWidget();
}
void FAssetContextMenu::SetOnFindInAssetTreeRequested(const FOnFindInAssetTreeRequested& InOnFindInAssetTreeRequested)
{
OnFindInAssetTreeRequested = InOnFindInAssetTreeRequested;
}
void FAssetContextMenu::SetOnRenameRequested(const FOnRenameRequested& InOnRenameRequested)
{
OnRenameRequested = InOnRenameRequested;
}
void FAssetContextMenu::SetOnRenameFolderRequested(const FOnRenameFolderRequested& InOnRenameFolderRequested)
{
OnRenameFolderRequested = InOnRenameFolderRequested;
}
void FAssetContextMenu::SetOnDuplicateRequested(const FOnDuplicateRequested& InOnDuplicateRequested)
{
OnDuplicateRequested = InOnDuplicateRequested;
}
void FAssetContextMenu::SetOnAssetViewRefreshRequested(const FOnAssetViewRefreshRequested& InOnAssetViewRefreshRequested)
{
OnAssetViewRefreshRequested = InOnAssetViewRefreshRequested;
}
bool FAssetContextMenu::AddCommonMenuOptions(FMenuBuilder& MenuBuilder)
{
MenuBuilder.BeginSection("AssetContextActions", LOCTEXT("AssetActionsMenuHeading", "Asset Actions"));
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SyncToAssetTree", "Find in Asset Tree"),
LOCTEXT("SyncToAssetTreeTooltip", "Selects the folder in the asset tree containing this asset."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSyncToAssetTree ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSyncToAssetTree )
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("FindAssetInWorld", "Select Actors Using This Asset"),
LOCTEXT("FindAssetInWorldTooltip", "Selects all actors referencing this asset."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteFindAssetInWorld ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteFindAssetInWorld )
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("Properties", "Details..."),
LOCTEXT("PropertiesTooltip", "Opens the details for the selected assets."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteProperties ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteProperties )
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("PropertyMatrix", "Property Matrix..."),
LOCTEXT("PropertyMatrixTooltip", "Opens the property matrix editor for the selected assets."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecutePropertyMatrix ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteProperties )
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("Duplicate", "Create Copy"),
LOCTEXT("DuplicateTooltip", "Create a copy of the selected assets."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteDuplicate ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteDuplicate )
)
);
MenuBuilder.AddMenuEntry( FGenericCommands::Get().Rename, NAME_None,
LOCTEXT("Rename", "Rename"),
LOCTEXT("RenameTooltip", "Rename the selected asset.")
);
MenuBuilder.AddMenuEntry( FGenericCommands::Get().Delete, NAME_None,
LOCTEXT("Delete", "Delete"),
LOCTEXT("DeleteTooltip", "Delete the selected assets.")
);
if ( CanExecuteConsolidate() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("ReplaceReferences", "Replace References"),
LOCTEXT("ConsolidateTooltip", "Replace references to the selected assets."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteConsolidate )
)
);
}
MenuBuilder.AddMenuEntry(
LOCTEXT("Export", "Export..."),
LOCTEXT("ExportTooltip", "Export the selected assets to file."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteExport ) )
);
if (SelectedAssets.Num() > 1)
{
MenuBuilder.AddMenuEntry(
LOCTEXT("BulkExport", "Bulk Export..."),
LOCTEXT("BulkExportTooltip", "Export the selected assets to file in the selected directory"),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteBulkExport ) )
);
}
MenuBuilder.AddMenuEntry(
LOCTEXT("MigrateAsset", "Migrate..."),
LOCTEXT("MigrateAssetTooltip", "Copies all selected assets and their dependencies to another game"),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteMigrateAsset ) )
);
MenuBuilder.AddMenuEntry(
ContentBrowserUtils::GetExploreFolderText(),
LOCTEXT("FindInExplorerTooltip", "Finds this asset on disk"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteFindInExplorer ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteFindInExplorer )
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("CreateBlueprintUsing", "Create Blueprint Using..."),
LOCTEXT("CreateBlueprintUsingTooltip", "Create a new Blueprint and add this asset to it"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteCreateBlueprintUsing),
FCanExecuteAction::CreateSP(this, &FAssetContextMenu::CanExecuteCreateBlueprintUsing)
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("SaveAsset", "Save"),
LOCTEXT("SaveAssetTooltip", "Saves the asset to file."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSaveAsset ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSaveAsset )
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("AssignAssetChunk", "Assign to Chunk..."),
LOCTEXT("AssignAssetChunkTooltip", "Assign this asset to a specific Chunk"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteAssignChunkID)
)
);
MenuBuilder.AddSubMenu(
LOCTEXT("RemoveAssetFromChunk", "Remove from Chunk..."),
LOCTEXT("RemoveAssetFromChunkTooltip", "Removed an asset from a Chunk it's assigned to."),
FNewMenuDelegate::CreateRaw(this, &FAssetContextMenu::MakeChunkIDListMenu)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("RemoveAllChunkAssignments", "Remove from all Chunks"),
LOCTEXT("RemoveAllChunkAssignmentsTooltip", "Removed an asset from all Chunks it's assigned to."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteRemoveAllChunkID)
)
);
if (CanExecuteDiffSelected())
{
MenuBuilder.AddMenuEntry(
LOCTEXT("DiffSelected", "Diff Selected"),
LOCTEXT("DiffSelectedTooltip", "Diff the two assets that you have selected."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteDiffSelected)
)
);
}
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
if ( SelectedAssets.Num() == 1 && AssetToolsModule.Get().AssetUsesGenericThumbnail(SelectedAssets[0]) )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("CaptureThumbnail", "Capture Thumbnail"),
LOCTEXT("CaptureThumbnailTooltip", "Captures a thumbnail from the active viewport."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteCaptureThumbnail ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteCaptureThumbnail )
)
);
}
if ( CanClearCustomThumbnails() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("ClearCustomThumbnail", "Clear Thumbnail"),
LOCTEXT("ClearCustomThumbnailTooltip", "Clears all custom thumbnails for selected assets."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteClearThumbnail )
)
);
}
}
MenuBuilder.EndSection();
return true;
}
bool FAssetContextMenu::AddReferenceMenuOptions(FMenuBuilder& MenuBuilder)
{
MenuBuilder.BeginSection("AssetContextReferences", LOCTEXT("ReferencesMenuHeading", "References"));
{
MenuBuilder.AddMenuEntry(
LOCTEXT("CopyReference", "Copy Reference"),
LOCTEXT("CopyReferenceTooltip", "Copies reference paths for the selected assets to the clipboard."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteCopyReference ) )
);
MenuBuilder.AddMenuEntry(
LOCTEXT("ReferenceViewer", "Reference Viewer..."),
LOCTEXT("ReferenceViewerTooltip", "Shows a graph of references for this asset."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteShowReferenceViewer )
)
);
}
MenuBuilder.EndSection();
return true;
}
bool FAssetContextMenu::AddDocumentationMenuOptions(FMenuBuilder& MenuBuilder)
{
bool bAddedOption = false;
// Objects must be loaded for this operation... for now
UClass* SelectedClass = (SelectedAssets.Num() > 0 ? SelectedAssets[0].GetClass() : nullptr);
for (const FAssetData& AssetData : SelectedAssets)
{
if (SelectedClass != AssetData.GetClass())
{
SelectedClass = nullptr;
break;
}
}
// Go to C++ Code
if( SelectedClass != nullptr )
{
// Blueprints are special. We won't link to C++ and for documentation we'll use the class it is generated from
const bool bIsBlueprint = SelectedClass->IsChildOf<UBlueprint>();
if (bIsBlueprint)
{
FString* ParentClassPath = SelectedAssets[0].TagsAndValues.Find(GET_MEMBER_NAME_CHECKED(UBlueprint,ParentClass));
if (ParentClassPath)
{
SelectedClass = FindObject<UClass>(nullptr,**ParentClassPath);
}
}
if ( !bIsBlueprint && FSourceCodeNavigation::IsCompilerAvailable() )
{
FString ClassHeaderPath;
if( FSourceCodeNavigation::FindClassHeaderPath( SelectedClass, ClassHeaderPath ) && IFileManager::Get().FileSize( *ClassHeaderPath ) != INDEX_NONE )
{
bAddedOption = true;
const FString CodeFileName = FPaths::GetCleanFilename( *ClassHeaderPath );
MenuBuilder.BeginSection( "ActorCode", LOCTEXT("ActorCodeHeading", "C++") );
{
MenuBuilder.AddMenuEntry(
FText::Format( LOCTEXT("GoToCodeForActor", "Open {0}"), FText::FromString( CodeFileName ) ),
FText::Format( LOCTEXT("GoToCodeForActor_ToolTip", "Opens the header file for this actor ({0}) in a code editing program"), FText::FromString( CodeFileName ) ),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToCodeForAsset, SelectedClass ) )
);
}
MenuBuilder.EndSection();
}
}
const FString DocumentationLink = FEditorClassUtils::GetDocumentationLink(SelectedClass);
if (!DocumentationLink.IsEmpty())
{
bAddedOption = true;
MenuBuilder.BeginSection( "ActorDocumentation", LOCTEXT("ActorDocsHeading", "Documentation") );
{
MenuBuilder.AddMenuEntry(
LOCTEXT("GoToDocsForActor", "View Documentation"),
LOCTEXT("GoToDocsForActor_ToolTip", "Click to open documentation for this actor"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "HelpIcon.Hovered" ),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToDocsForAsset, SelectedClass ) )
);
}
MenuBuilder.EndSection();
}
}
return bAddedOption;
}
bool FAssetContextMenu::AddAssetTypeMenuOptions(FMenuBuilder& MenuBuilder)
{
bool bAnyTypeOptions = false;
// Objects must be loaded for this operation... for now
TArray<FString> ObjectPaths;
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num(); ++AssetIdx)
{
ObjectPaths.Add(SelectedAssets[AssetIdx].ObjectPath.ToString());
}
TArray<UObject*> SelectedObjects;
if ( ContentBrowserUtils::LoadAssetsIfNeeded(ObjectPaths, SelectedObjects) )
{
// Load the asset tools module
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
bAnyTypeOptions = AssetToolsModule.Get().GetAssetActions(SelectedObjects, MenuBuilder, /*bIncludeHeading=*/true);
}
return bAnyTypeOptions;
}
bool FAssetContextMenu::AddSourceControlMenuOptions(FMenuBuilder& MenuBuilder)
{
MenuBuilder.BeginSection("AssetContextSourceControl", LOCTEXT("AssetSCCOptionsMenuHeading", "Source Control"));
if ( ISourceControlModule::Get().IsEnabled() )
{
if( CanExecuteSCCSync() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCSync", "Sync"),
LOCTEXT("SCCSyncTooltip", "Updates the item to the latest version in source control."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCSync ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCSync )
)
);
}
if ( CanExecuteSCCCheckOut() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCCheckOut", "Check Out"),
LOCTEXT("SCCCheckOutTooltip", "Checks out the selected asset from source control."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCCheckOut ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCCheckOut )
)
);
}
if ( CanExecuteSCCOpenForAdd() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCOpenForAdd", "Mark For Add"),
LOCTEXT("SCCOpenForAddTooltip", "Adds the selected asset to source control."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCOpenForAdd ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCOpenForAdd )
)
);
}
if ( CanExecuteSCCCheckIn() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCCheckIn", "Check In"),
LOCTEXT("SCCCheckInTooltip", "Checks in the selected asset to source control."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCCheckIn ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCCheckIn )
)
);
}
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCRefresh", "Refresh"),
LOCTEXT("SCCRefreshTooltip", "Updates the source control status of the asset."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCRefresh ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCRefresh )
)
);
if( CanExecuteSCCHistory() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCHistory", "History"),
LOCTEXT("SCCHistoryTooltip", "Displays the source control revision history of the selected asset."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCHistory ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCHistory )
)
);
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCDiffAgainstDepot", "Diff Against Depot"),
LOCTEXT("SCCDiffAgainstDepotTooltip", "Look at differences between your version of the asset and that in source control."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCDiffAgainstDepot ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCDiffAgainstDepot )
)
);
}
if( CanExecuteSCCRevert() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCRevert", "Revert"),
LOCTEXT("SCCRevertTooltip", "Reverts the asset to the state it was before it was checked out."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSCCRevert ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSCCRevert )
)
);
}
}
else
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCConnectToSourceControl", "Connect To Source Control"),
LOCTEXT("SCCConnectToSourceControlTooltip", "Connect to source control to allow source control operations to be performed on content and levels."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteEnableSourceControl ),
FCanExecuteAction()
)
);
}
MenuBuilder.EndSection();
return true;
}
bool FAssetContextMenu::AddCollectionMenuOptions(FMenuBuilder& MenuBuilder)
{
// "Remove from collection" (only display option if exactly one collection is selected)
if ( SourcesData.Collections.Num() == 1 )
{
MenuBuilder.BeginSection("AssetContextCollections", LOCTEXT("AssetCollectionOptionsMenuHeading", "Collections"));
{
MenuBuilder.AddMenuEntry(
LOCTEXT("RemoveFromCollection", "Remove From Collection"),
LOCTEXT("RemoveFromCollection_ToolTip", "Removes the selected asset from the current collection."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteRemoveFromCollection ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteRemoveFromCollection )
)
);
}
MenuBuilder.EndSection();
return true;
}
return false;
}
void FAssetContextMenu::ExecuteSyncToAssetTree()
{
OnFindInAssetTreeRequested.ExecuteIfBound(SelectedAssets);
}
void FAssetContextMenu::ExecuteFindInExplorer()
{
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num(); ++AssetIdx)
{
const UObject* Asset = SelectedAssets[AssetIdx].GetAsset();
if (Asset)
{
FAssetData AssetData(Asset);
const bool bIsWorldAsset = (AssetData.AssetClass == UWorld::StaticClass()->GetFName());
const FString Extension = bIsWorldAsset ? FPackageName::GetMapPackageExtension() : FPackageName::GetAssetPackageExtension();
const FString FilePath = FPackageName::LongPackageNameToFilename(AssetData.PackageName.ToString(), Extension);
const FString FullFilePath = FPaths::ConvertRelativePathToFull(FilePath);
FPlatformProcess::ExploreFolder(*FullFilePath);
}
}
}
void FAssetContextMenu::ExecuteCreateBlueprintUsing()
{
if(SelectedAssets.Num() == 1)
{
UObject* Asset = SelectedAssets[0].GetAsset();
FKismetEditorUtilities::CreateBlueprintUsingAsset(Asset, true);
}
}
void FAssetContextMenu::GetSelectedAssets(TArray<UObject*>& Assets, bool SkipRedirectors)
{
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num(); ++AssetIdx)
{
if (SkipRedirectors && (SelectedAssets[AssetIdx].AssetClass == UObjectRedirector::StaticClass()->GetFName()))
{
// Don't operate on Redirectors
continue;
}
UObject* Object = SelectedAssets[AssetIdx].GetAsset();
if (Object)
{
Assets.Add(Object);
}
}
}
/** Generates a reference graph of the world and can then find actors referencing specified objects */
struct WorldReferenceGenerator : public FFindReferencedAssets
{
void BuildReferencingData()
{
MarkAllObjects();
const int32 MaxRecursionDepth = 0;
const bool bIncludeClasses = true;
const bool bIncludeDefaults = false;
const bool bReverseReferenceGraph = true;
UWorld* World = GWorld;
// Generate the reference graph for the world
FReferencedAssets* WorldReferencer = new(Referencers)FReferencedAssets(World);
FFindAssetsArchive(World, WorldReferencer->AssetList, &ReferenceGraph, MaxRecursionDepth, bIncludeClasses, bIncludeDefaults, bReverseReferenceGraph);
// Also include all the streaming levels in the results
for (int32 LevelIndex = 0; LevelIndex < World->StreamingLevels.Num(); ++LevelIndex)
{
ULevelStreaming* StreamingLevel = World->StreamingLevels[LevelIndex];
if( StreamingLevel != NULL )
{
ULevel* Level = StreamingLevel->GetLoadedLevel();
if( Level != NULL )
{
// Generate the reference graph for each streamed in level
FReferencedAssets* LevelReferencer = new(Referencers) FReferencedAssets(Level);
FFindAssetsArchive(Level, LevelReferencer->AssetList, &ReferenceGraph, MaxRecursionDepth, bIncludeClasses, bIncludeDefaults, bReverseReferenceGraph);
}
}
}
}
void MarkAllObjects()
{
// Mark all objects so we don't get into an endless recursion
for (FObjectIterator It; It; ++It)
{
It->Mark(OBJECTMARK_TagExp);
}
}
void Generate( const UObject* AssetToFind, TArray< TWeakObjectPtr<UObject> >& OutObjects )
{
// Don't examine visited objects
if (!AssetToFind->HasAnyMarks(OBJECTMARK_TagExp))
{
return;
}
AssetToFind->UnMark(OBJECTMARK_TagExp);
// Return once we find a parent object that is an actor
if (AssetToFind->IsA(AActor::StaticClass()))
{
OutObjects.Add(AssetToFind);
return;
}
// Transverse the reference graph looking for actor objects
TSet<UObject*>* Referencers = ReferenceGraph.Find(AssetToFind);
if (Referencers)
{
for(TSet<UObject*>::TConstIterator SetIt(*Referencers); SetIt; ++SetIt)
{
Generate(*SetIt, OutObjects);
}
}
}
};
void FAssetContextMenu::ExecuteFindAssetInWorld()
{
TArray<UObject*> AssetsToFind;
const bool SkipRedirectors = true;
GetSelectedAssets(AssetsToFind, SkipRedirectors);
const bool NoteSelectionChange = true;
const bool DeselectBSPSurfs = true;
const bool WarnAboutManyActors = false;
GEditor->SelectNone(NoteSelectionChange, DeselectBSPSurfs, WarnAboutManyActors);
if (AssetsToFind.Num() > 0)
{
const bool ShowProgressDialog = true;
GWarn->BeginSlowTask(NSLOCTEXT("AssetContextMenu", "FindAssetInWorld", "Finding actors that use this asset..."), ShowProgressDialog);
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
TArray< TWeakObjectPtr<UObject> > OutObjects;
WorldReferenceGenerator ObjRefGenerator;
ObjRefGenerator.BuildReferencingData();
for (int32 AssetIdx = 0; AssetIdx < AssetsToFind.Num(); ++AssetIdx)
{
ObjRefGenerator.MarkAllObjects();
ObjRefGenerator.Generate(AssetsToFind[AssetIdx], OutObjects);
}
if (OutObjects.Num() > 0)
{
const bool InSelected = true;
const bool Notify = false;
// Select referencing actors
for (int32 ActorIdx = 0; ActorIdx < OutObjects.Num(); ++ActorIdx)
{
GEditor->SelectActor(CastChecked<AActor>(OutObjects[ActorIdx].Get()), InSelected, Notify);
}
GEditor->NoteSelectionChange();
}
else
{
FNotificationInfo Info(LOCTEXT("NoReferencingActorsFound", "No actors found."));
Info.ExpireDuration = 3.0f;
FSlateNotificationManager::Get().AddNotification(Info);
}
GWarn->EndSlowTask();
}
}
void FAssetContextMenu::ExecuteProperties()
{
TArray<UObject*> ObjectsForPropertiesMenu;
const bool SkipRedirectors = true;
GetSelectedAssets(ObjectsForPropertiesMenu, SkipRedirectors);
if ( ObjectsForPropertiesMenu.Num() > 0 )
{
FAssetEditorManager::Get().OpenEditorForAssets( ObjectsForPropertiesMenu );
}
}
void FAssetContextMenu::ExecutePropertyMatrix()
{
TArray<UObject*> ObjectsForPropertiesMenu;
const bool SkipRedirectors = true;
GetSelectedAssets(ObjectsForPropertiesMenu, SkipRedirectors);
if ( ObjectsForPropertiesMenu.Num() > 0 )
{
FPropertyEditorModule& PropertyEditorModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>( "PropertyEditor" );
PropertyEditorModule.CreatePropertyEditorToolkit( EToolkitMode::Standalone, TSharedPtr<IToolkitHost>(), ObjectsForPropertiesMenu );
}
}
void FAssetContextMenu::ExecuteSaveAsset()
{
TArray<UPackage*> PackagesToSave;
GetSelectedPackages(PackagesToSave);
TArray< UPackage* > PackagesWithExternalRefs;
FString PackageNames;
if( PackageTools::CheckForReferencesToExternalPackages( &PackagesToSave, &PackagesWithExternalRefs ) )
{
for(int32 PkgIdx = 0; PkgIdx < PackagesWithExternalRefs.Num(); ++PkgIdx)
{
PackageNames += FString::Printf(TEXT("%s\n"), *PackagesWithExternalRefs[ PkgIdx ]->GetName());
}
bool bProceed = EAppReturnType::Yes == FMessageDialog::Open( EAppMsgType::YesNo, FText::Format( NSLOCTEXT("UnrealEd", "Warning_ExternalPackageRef", "The following assets have references to external assets: \n{0}\nExternal assets won't be found when in a game and all references will be broken. Proceed?"), FText::FromString(PackageNames) ) );
if(!bProceed)
{
return;
}
}
const bool bCheckDirty = false;
const bool bPromptToSave = false;
const FEditorFileUtils::EPromptReturnCode Return = FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptToSave);
//return Return == FEditorFileUtils::EPromptReturnCode::PR_Success;
}
void FAssetContextMenu::ExecuteDiffSelected() const
{
if (SelectedAssets.Num() >= 2)
{
UObject* FirstObjectSelected = SelectedAssets[0].GetAsset();
UObject* SecondObjectSelected = SelectedAssets[1].GetAsset();
if ((FirstObjectSelected != NULL) && (SecondObjectSelected != NULL))
{
// Load the asset registry module
FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
FRevisionInfo CurrentRevision;
CurrentRevision.Revision = -1;
AssetToolsModule.Get().DiffAssets(FirstObjectSelected, SecondObjectSelected, CurrentRevision, CurrentRevision);
}
}
}
void FAssetContextMenu::ExecuteDuplicate()
{
TArray<UObject*> ObjectsToDuplicate;
const bool SkipRedirectors = true;
GetSelectedAssets(ObjectsToDuplicate, SkipRedirectors);
if ( ObjectsToDuplicate.Num() == 1 )
{
OnDuplicateRequested.ExecuteIfBound(ObjectsToDuplicate[0]);
}
else if ( ObjectsToDuplicate.Num() > 1 )
{
TArray<UObject*> NewObjects;
ObjectTools::DuplicateObjects(ObjectsToDuplicate, TEXT(""), TEXT(""), /*bOpenDialog=*/false, &NewObjects);
TArray<FAssetData> AssetsToSync;
for ( auto ObjIt = NewObjects.CreateConstIterator(); ObjIt; ++ObjIt )
{
new(AssetsToSync) FAssetData(*ObjIt);
}
// Sync to asset tree
if ( NewObjects.Num() > 0 )
{
OnFindInAssetTreeRequested.ExecuteIfBound(AssetsToSync);
}
}
}
void FAssetContextMenu::ExecuteRename()
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
TArray< FString > SelectedFolders = AssetView.Pin()->GetSelectedFolders();
if ( AssetViewSelectedAssets.Num() == 1 && SelectedFolders.Num() == 0 )
{
// Don't operate on Redirectors
if ( AssetViewSelectedAssets[0].AssetClass != UObjectRedirector::StaticClass()->GetFName() )
{
OnRenameRequested.ExecuteIfBound(AssetViewSelectedAssets[0]);
}
}
if ( AssetViewSelectedAssets.Num() == 0 && SelectedFolders.Num() == 1 )
{
OnRenameFolderRequested.ExecuteIfBound(SelectedFolders[0]);
}
}
void FAssetContextMenu::ExecuteDelete()
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
if(AssetViewSelectedAssets.Num() > 0)
{
TArray<FAssetData> AssetsToDelete;
for( auto AssetIt = AssetViewSelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
const FAssetData& AssetData = *AssetIt;
if( AssetData.AssetClass == UObjectRedirector::StaticClass()->GetFName() )
{
// Don't operate on Redirectors
continue;
}
AssetsToDelete.Add( AssetData );
}
if ( AssetsToDelete.Num() > 0 )
{
ObjectTools::DeleteAssets( AssetsToDelete );
}
}
TArray< FString > SelectedFolders = AssetView.Pin()->GetSelectedFolders();
if(SelectedFolders.Num() > 0)
{
FText Prompt;
if ( SelectedFolders.Num() == 1 )
{
Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Single", "Delete folder '{0}'?"), FText::FromString(SelectedFolders[0]));
}
else
{
Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Multiple", "Delete {0} folders?"), FText::AsNumber(SelectedFolders.Num()));
}
// Spawn a confirmation dialog since this is potentially a highly destructive operation
ContentBrowserUtils::DisplayConfirmationPopup(
Prompt,
LOCTEXT("FolderDeleteConfirm_Yes", "Delete"),
LOCTEXT("FolderDeleteConfirm_No", "Cancel"),
AssetView.Pin().ToSharedRef(),
FOnClicked::CreateSP( this, &FAssetContextMenu::ExecuteDeleteFolderConfirmed ));
}
}
FReply FAssetContextMenu::ExecuteDeleteFolderConfirmed()
{
TArray< FString > SelectedFolders = AssetView.Pin()->GetSelectedFolders();
if(SelectedFolders.Num() > 0)
{
ContentBrowserUtils::DeleteFolders(SelectedFolders);
}
return FReply::Handled();
}
void FAssetContextMenu::ExecuteConsolidate()
{
TArray<UObject*> ObjectsToConsolidate;
const bool SkipRedirectors = true;
GetSelectedAssets(ObjectsToConsolidate, SkipRedirectors);
if ( ObjectsToConsolidate.Num() > 0 )
{
FConsolidateToolWindow::AddConsolidationObjects( ObjectsToConsolidate );
}
}
void FAssetContextMenu::ExecuteCaptureThumbnail()
{
FViewport* Viewport = GEditor->GetActiveViewport();
if ( ensure(GCurrentLevelEditingViewportClient) && ensure(Viewport) )
{
//have to re-render the requested viewport
FLevelEditorViewportClient* OldViewportClient = GCurrentLevelEditingViewportClient;
//remove selection box around client during render
GCurrentLevelEditingViewportClient = NULL;
Viewport->Draw();
ContentBrowserUtils::CaptureThumbnailFromViewport(Viewport, SelectedAssets);
//redraw viewport to have the yellow highlight again
GCurrentLevelEditingViewportClient = OldViewportClient;
Viewport->Draw();
}
}
void FAssetContextMenu::ExecuteClearThumbnail()
{
ContentBrowserUtils::ClearCustomThumbnails(SelectedAssets);
}
void FAssetContextMenu::ExecuteMigrateAsset()
{
// Get a list of package names for input into MigratePackages
TArray<FName> PackageNames;
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num(); ++AssetIdx)
{
PackageNames.Add(SelectedAssets[AssetIdx].PackageName);
}
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
AssetToolsModule.Get().MigratePackages( PackageNames );
}
void FAssetContextMenu::ExecuteShowReferenceViewer()
{
TArray<FName> PackageNames;
for ( auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
PackageNames.Add(AssetIt->PackageName);
}
if ( PackageNames.Num() > 0 )
{
IReferenceViewerModule::Get().InvokeReferenceViewerTab(PackageNames);
}
}
void FAssetContextMenu::ExecuteGoToCodeForAsset(UClass* SelectedClass)
{
if (SelectedClass)
{
FString ClassHeaderPath;
if( FSourceCodeNavigation::FindClassHeaderPath( SelectedClass, ClassHeaderPath ) && IFileManager::Get().FileSize( *ClassHeaderPath ) != INDEX_NONE )
{
const FString AbsoluteHeaderPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*ClassHeaderPath);
FSourceCodeNavigation::OpenSourceFile( AbsoluteHeaderPath );
}
}
}
void FAssetContextMenu::ExecuteGoToDocsForAsset(UClass* SelectedClass)
{
if (SelectedClass)
{
FString DocumentationLink = FEditorClassUtils::GetDocumentationLink(SelectedClass);
if (!DocumentationLink.IsEmpty())
{
IDocumentation::Get()->Open( DocumentationLink );
}
}
}
void FAssetContextMenu::ExecuteCopyReference()
{
ContentBrowserUtils::CopyAssetReferencesToClipboard(SelectedAssets);
}
void FAssetContextMenu::ExecuteExport()
{
TArray<UObject*> ObjectsToExport;
const bool SkipRedirectors = false;
GetSelectedAssets(ObjectsToExport, SkipRedirectors);
if ( ObjectsToExport.Num() > 0 )
{
ObjectTools::ExportObjects(ObjectsToExport, /*bPromptForEachFileName=*/true);
}
}
void FAssetContextMenu::ExecuteBulkExport()
{
TArray<UObject*> ObjectsToExport;
const bool SkipRedirectors = false;
GetSelectedAssets(ObjectsToExport, SkipRedirectors);
if ( ObjectsToExport.Num() > 0 )
{
ObjectTools::ExportObjects(ObjectsToExport, /*bPromptForEachFileName=*/false);
}
}
void FAssetContextMenu::ExecuteRemoveFromCollection()
{
if ( ensure(SourcesData.Collections.Num() == 1) )
{
TArray<FName> AssetsToRemove;
for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt)
{
AssetsToRemove.Add((*AssetIt).ObjectPath);
}
if ( AssetsToRemove.Num() > 0 )
{
FCollectionManagerModule& CollectionManagerModule = FModuleManager::LoadModuleChecked<FCollectionManagerModule>("CollectionManager");
FName CollectionName = SourcesData.Collections[0].Name;
ECollectionShareType::Type CollectionType = SourcesData.Collections[0].Type;
CollectionManagerModule.Get().RemoveFromCollection(CollectionName, CollectionType, AssetsToRemove);
OnAssetViewRefreshRequested.ExecuteIfBound();
}
}
}
void FAssetContextMenu::ExecuteSCCRefresh()
{
TArray<FString> PackageNames;
GetSelectedPackageNames(PackageNames);
ISourceControlModule::Get().GetProvider().Execute(ISourceControlOperation::Create<FUpdateStatus>(), SourceControlHelpers::PackageFilenames(PackageNames), EConcurrency::Asynchronous);
}
void FAssetContextMenu::ExecuteSCCCheckOut()
{
TArray<UPackage*> PackagesToCheckOut;
GetSelectedPackages(PackagesToCheckOut);
if ( PackagesToCheckOut.Num() > 0 )
{
// Update the source control status of all potentially relevant packages
ISourceControlModule::Get().GetProvider().Execute(ISourceControlOperation::Create<FUpdateStatus>(), PackagesToCheckOut);
// Now check them out
FEditorFileUtils::CheckoutPackages(PackagesToCheckOut);
}
}
void FAssetContextMenu::ExecuteSCCOpenForAdd()
{
TArray<FString> PackageNames;
GetSelectedPackageNames(PackageNames);
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
TArray<FString> PackagesToAdd;
TArray<UPackage*> PackagesToSave;
for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
{
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(*PackageIt), EStateCacheUsage::Use);
if ( SourceControlState.IsValid() && !SourceControlState->IsSourceControlled() )
{
PackagesToAdd.Add(*PackageIt);
// Make sure the file actually exists on disk before adding it
FString Filename;
if ( !FPackageName::DoesPackageExist(*PackageIt, NULL, &Filename) )
{
UPackage* Package = FindPackage(NULL, **PackageIt);
if ( Package )
{
PackagesToSave.Add(Package);
}
}
}
}
if ( PackagesToAdd.Num() > 0 )
{
// If any of the packages are new, save them now
if ( PackagesToSave.Num() > 0 )
{
const bool bCheckDirty = false;
const bool bPromptToSave = false;
TArray<UPackage*> FailedPackages;
const FEditorFileUtils::EPromptReturnCode Return = FEditorFileUtils::PromptForCheckoutAndSave(PackagesToSave, bCheckDirty, bPromptToSave, &FailedPackages);
if(FailedPackages.Num() > 0)
{
// don't try and add files that failed to save - remove them from the list
for(auto FailedPackageIt = FailedPackages.CreateConstIterator(); FailedPackageIt; FailedPackageIt++)
{
PackagesToAdd.Remove((*FailedPackageIt)->GetName());
}
}
}
SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), SourceControlHelpers::PackageFilenames(PackagesToAdd));
}
}
void FAssetContextMenu::ExecuteSCCCheckIn()
{
TArray<FString> PackageNames;
GetSelectedPackageNames(PackageNames);
TArray<UPackage*> Packages;
GetSelectedPackages(Packages);
// Prompt the user to ask if they would like to first save any dirty packages they are trying to check-in
const FEditorFileUtils::EPromptReturnCode UserResponse = FEditorFileUtils::PromptForCheckoutAndSave( Packages, true, true );
// If the user elected to save dirty packages, but one or more of the packages failed to save properly OR if the user
// canceled out of the prompt, don't follow through on the check-in process
const bool bShouldProceed = ( UserResponse == FEditorFileUtils::EPromptReturnCode::PR_Success || UserResponse == FEditorFileUtils::EPromptReturnCode::PR_Declined );
if ( bShouldProceed )
{
FSourceControlWindows::PromptForCheckin(PackageNames);
}
else
{
// If a failure occurred, alert the user that the check-in was aborted. This warning shouldn't be necessary if the user cancelled
// from the dialog, because they obviously intended to cancel the whole operation.
if ( UserResponse == FEditorFileUtils::EPromptReturnCode::PR_Failure )
{
FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "SCC_Checkin_Aborted", "Check-in aborted as a result of save failure.") );
}
}
}
void FAssetContextMenu::ExecuteSCCHistory()
{
TArray<FString> PackageNames;
GetSelectedPackageNames(PackageNames);
FSourceControlWindows::DisplayRevisionHistory(SourceControlHelpers::PackageFilenames(PackageNames));
}
void FAssetContextMenu::ExecuteSCCDiffAgainstDepot() const
{
// Load the asset registry module
FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
// Iterate over each selected asset
for(int32 AssetIdx=0; AssetIdx<SelectedAssets.Num(); AssetIdx++)
{
// Get the actual asset (will load it)
const FAssetData& AssetData = SelectedAssets[AssetIdx];
UObject* CurrentObject = AssetData.GetAsset();
if( CurrentObject )
{
const FString PackagePath = AssetData.PackageName.ToString();
const FString PackageName = AssetData.AssetName.ToString();
AssetToolsModule.Get().DiffAgainstDepot( CurrentObject, PackagePath, PackageName );
}
}
}
void FAssetContextMenu::ExecuteSCCRevert()
{
TArray<FString> PackageNames;
GetSelectedPackageNames(PackageNames);
FSourceControlWindows::PromptForRevert(PackageNames);
}
void FAssetContextMenu::ExecuteSCCSync()
{
TArray<FString> PackageNames;
GetSelectedPackageNames(PackageNames);
TArray<FString> PackageFileNames = SourceControlHelpers::PackageFilenames(PackageNames);
TArray<UPackage*> Packages;
GetSelectedPackages(Packages);
FText ErrorMessage;
PackageTools::UnloadPackages(Packages, ErrorMessage);
if(!ErrorMessage.IsEmpty())
{
FMessageDialog::Open( EAppMsgType::Ok, ErrorMessage );
}
else
{
ISourceControlModule::Get().GetProvider().Execute(ISourceControlOperation::Create<FSync>(), PackageFileNames);
for( TArray<FString>::TConstIterator PackageIter( PackageNames ); PackageIter; ++PackageIter )
{
PackageTools::LoadPackage(*PackageIter);
}
}
}
void FAssetContextMenu::ExecuteEnableSourceControl()
{
ISourceControlModule::Get().ShowLoginDialog(FSourceControlLoginClosed(), ELoginWindowMode::Modeless);
}
bool FAssetContextMenu::CanExecuteSyncToAssetTree() const
{
return SelectedAssets.Num() > 0;
}
bool FAssetContextMenu::CanExecuteFindInExplorer() const
{
return SelectedAssets.Num() > 0;
}
bool FAssetContextMenu::CanExecuteCreateBlueprintUsing() const
{
// Only work if you have a single asset selected
if(SelectedAssets.Num() == 1)
{
UObject* Asset = SelectedAssets[0].GetAsset();
// See if we know how to make a component from this asset
TArray< TSubclassOf<UActorComponent> > ComponentClassList = FComponentAssetBrokerage::GetComponentsForAsset(Asset);
return (ComponentClassList.Num() > 0);
}
return false;
}
bool FAssetContextMenu::CanExecuteFindAssetInWorld() const
{
return bAtLeastOneNonRedirectorSelected;
}
bool FAssetContextMenu::CanExecuteProperties() const
{
return bAtLeastOneNonRedirectorSelected;
}
bool FAssetContextMenu::CanExecutePropertyMatrix() const
{
return bAtLeastOneNonRedirectorSelected;
}
bool FAssetContextMenu::CanExecuteDuplicate() const
{
return bAtLeastOneNonRedirectorSelected;
}
bool FAssetContextMenu::CanExecuteRename() const
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
TArray< FString > SelectedFolders = AssetView.Pin()->GetSelectedFolders();
const bool bOneAssetSelected = AssetViewSelectedAssets.Num() == 1 && SelectedFolders.Num() == 0 && AssetViewSelectedAssets[0].AssetClass != UObjectRedirector::StaticClass()->GetFName();
const bool bOneFolderSelected = AssetViewSelectedAssets.Num() == 0 && SelectedFolders.Num() == 1;
return (bOneAssetSelected || bOneFolderSelected) && !AssetView.Pin()->IsThumbnailEditMode();
}
bool FAssetContextMenu::CanExecuteDelete() const
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
TArray< FString > SelectedFolders = AssetView.Pin()->GetSelectedFolders();
return AssetViewSelectedAssets.Num() > 0 || SelectedFolders.Num() > 0;
}
bool FAssetContextMenu::CanExecuteRemoveFromCollection() const
{
return SourcesData.Collections.Num() == 1;
}
bool FAssetContextMenu::CanExecuteSCCRefresh() const
{
return ISourceControlModule::Get().IsEnabled();
}
bool FAssetContextMenu::CanExecuteSCCCheckOut() const
{
return bCanExecuteSCCCheckOut;
}
bool FAssetContextMenu::CanExecuteSCCOpenForAdd() const
{
return bCanExecuteSCCOpenForAdd;
}
bool FAssetContextMenu::CanExecuteSCCCheckIn() const
{
return bCanExecuteSCCCheckIn;
}
bool FAssetContextMenu::CanExecuteSCCHistory() const
{
return bCanExecuteSCCHistory;
}
bool FAssetContextMenu::CanExecuteSCCDiffAgainstDepot() const
{
return bCanExecuteSCCHistory;
}
bool FAssetContextMenu::CanExecuteSCCRevert() const
{
return bCanExecuteSCCRevert;
}
bool FAssetContextMenu::CanExecuteSCCSync() const
{
return bCanExecuteSCCSync;
}
bool FAssetContextMenu::CanExecuteConsolidate() const
{
TArray<UObject*> ProposedObjects;
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num(); ++AssetIdx)
{
// Don't load assets here. Only operate on already loaded assets.
if ( SelectedAssets[AssetIdx].IsAssetLoaded() )
{
UObject* Object = SelectedAssets[AssetIdx].GetAsset();
if ( Object )
{
ProposedObjects.Add(Object);
}
}
}
if ( ProposedObjects.Num() > 0 )
{
TArray<UObject*> CompatibleObjects;
return FConsolidateToolWindow::DetermineAssetCompatibility(ProposedObjects, CompatibleObjects);
}
return false;
}
bool FAssetContextMenu::CanExecuteSaveAsset() const
{
TArray<UPackage*> Packages;
GetSelectedPackages(Packages);
// only enabled if at least one selected package is loaded at all
for (int32 PackageIdx = 0; PackageIdx < Packages.Num(); ++PackageIdx)
{
if ( Packages[PackageIdx] != NULL )
{
return true;
}
}
return false;
}
bool FAssetContextMenu::CanExecuteDiffSelected() const
{
bool bCanDiffSelected = false;
if (SelectedAssets.Num() == 2)
{
FAssetData const& FirstSelection = SelectedAssets[0];
FAssetData const& SecondSelection = SelectedAssets[1];
if (FirstSelection.AssetClass != SecondSelection.AssetClass)
{
bCanDiffSelected = false;
}
else
{
bCanDiffSelected = (FirstSelection.AssetClass == UBlueprint::StaticClass()->GetFName());
}
}
return bCanDiffSelected;
}
bool FAssetContextMenu::CanExecuteCaptureThumbnail() const
{
return GCurrentLevelEditingViewportClient != NULL;
}
bool FAssetContextMenu::CanClearCustomThumbnails() const
{
for ( auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
if ( ContentBrowserUtils::AssetHasCustomThumbnail(*AssetIt) )
{
return true;
}
}
return false;
}
void FAssetContextMenu::CacheCanExecuteVars()
{
bAtLeastOneNonRedirectorSelected = false;
bCanExecuteSCCCheckOut = false;
bCanExecuteSCCOpenForAdd = false;
bCanExecuteSCCCheckIn = false;
bCanExecuteSCCHistory = false;
bCanExecuteSCCRevert = false;
bCanExecuteSCCSync = false;
for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt)
{
const FAssetData& AssetData = *AssetIt;
if ( !AssetData.IsValid() )
{
continue;
}
if ( !bAtLeastOneNonRedirectorSelected && AssetData.AssetClass != UObjectRedirector::StaticClass()->GetFName() )
{
bAtLeastOneNonRedirectorSelected = true;
}
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
if ( ISourceControlModule::Get().IsEnabled() )
{
// Check the SCC state for each package in the selected paths
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(AssetData.PackageName.ToString()), EStateCacheUsage::Use);
if(SourceControlState.IsValid())
{
if ( SourceControlState->CanCheckout() )
{
bCanExecuteSCCCheckOut = true;
}
if ( !SourceControlState->IsSourceControlled() && SourceControlState->CanAdd() )
{
bCanExecuteSCCOpenForAdd = true;
}
else if( SourceControlState->IsSourceControlled() && !SourceControlState->IsAdded() )
{
bCanExecuteSCCHistory = true;
}
if(!SourceControlState->IsCurrent())
{
bCanExecuteSCCSync = true;
}
if ( SourceControlState->CanCheckIn() )
{
bCanExecuteSCCCheckIn = true;
bCanExecuteSCCRevert = true;
}
}
}
if ( bAtLeastOneNonRedirectorSelected
&& bCanExecuteSCCCheckOut
&& bCanExecuteSCCOpenForAdd
&& bCanExecuteSCCCheckIn
&& bCanExecuteSCCHistory
&& bCanExecuteSCCRevert
&& bCanExecuteSCCSync
)
{
// All options are available, no need to keep iterating
break;
}
}
}
void FAssetContextMenu::GetSelectedPackageNames(TArray<FString>& OutPackageNames) const
{
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num(); ++AssetIdx)
{
OutPackageNames.Add(SelectedAssets[AssetIdx].PackageName.ToString());
}
}
void FAssetContextMenu::GetSelectedPackages(TArray<UPackage*>& OutPackages) const
{
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num(); ++AssetIdx)
{
UPackage* Package = FindPackage(NULL, *SelectedAssets[AssetIdx].PackageName.ToString());
if ( Package )
{
OutPackages.Add(Package);
}
}
}
void FAssetContextMenu::MakeChunkIDListMenu(FMenuBuilder& MenuBuilder)
{
TArray<int32> FoundChunks;
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
for (const auto& SelectedAsset : AssetViewSelectedAssets)
{
UPackage* Package = FindPackage(NULL, *SelectedAsset.PackageName.ToString());
if (Package)
{
for (auto ChunkID : Package->GetChunkIDs())
{
FoundChunks.AddUnique(ChunkID);
}
}
}
for (auto ChunkID : FoundChunks)
{
MenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("PackageChunk", "Chunk {0}"), FText::AsNumber(ChunkID)),
FText(),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteRemoveChunkID, ChunkID)
)
);
}
}
void FAssetContextMenu::ExecuteAssignChunkID()
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
auto AssetViewPtr = AssetView.Pin();
if (AssetViewSelectedAssets.Num() > 0 && AssetViewPtr.IsValid())
{
// Determine the position of the window so that it will spawn near the mouse, but not go off the screen.
const FVector2D CursorPos = FSlateApplication::Get().GetCursorPos();
FSlateRect Anchor(CursorPos.X, CursorPos.Y, CursorPos.X, CursorPos.Y);
FVector2D AdjustedSummonLocation = FSlateApplication::Get().CalculatePopupWindowPosition(Anchor, SColorPicker::DEFAULT_WINDOW_SIZE, Orient_Horizontal);
TSharedPtr<SWindow> Window = SNew(SWindow)
.AutoCenter(EAutoCenter::None)
.ScreenPosition(AdjustedSummonLocation)
.SupportsMaximize(false)
.SupportsMinimize(false)
.SizingRule(ESizingRule::Autosized)
.Title(LOCTEXT("WindowHeader", "Enter Chunk ID"));
Window->SetContent(
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Top)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("MeshPaint_LabelStrength", "Chunk ID"))
]
+ SHorizontalBox::Slot()
.FillWidth(2.0f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
[
SNew(SNumericEntryBox<int32>)
.AllowSpin(true)
.MinSliderValue(0)
.MaxSliderValue(300)
.MinValue(0)
.MaxValue(300)
.Value(this, &FAssetContextMenu::GetChunkIDSelection)
.OnValueChanged(this, &FAssetContextMenu::OnChunkIDAssignChanged)
]
]
+ SVerticalBox::Slot()
.FillHeight(1.0f)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Bottom)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
[
SNew(SButton)
.Text(LOCTEXT("ChunkIDAssign_Yes", "OK"))
.OnClicked(this, &FAssetContextMenu::OnChunkIDAssignCommit, Window)
]
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
[
SNew(SButton)
.Text(LOCTEXT("ChunkIDAssign_No", "Cancel"))
.OnClicked(this, &FAssetContextMenu::OnChunkIDAssignCancel, Window)
]
]
);
ChunkIDSelected = 0;
FSlateApplication::Get().AddModalWindow(Window.ToSharedRef(), AssetViewPtr);
}
}
void FAssetContextMenu::ExecuteRemoveAllChunkID()
{
TArray<int32> EmptyChunks;
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
for (const auto& SelectedAsset : AssetViewSelectedAssets)
{
UPackage* Package = FindPackage(NULL, *SelectedAsset.PackageName.ToString());
if (Package)
{
Package->SetChunkIDs(EmptyChunks);
Package->SetDirtyFlag(true);
}
}
}
TOptional<int32> FAssetContextMenu::GetChunkIDSelection() const
{
return ChunkIDSelected;
}
void FAssetContextMenu::OnChunkIDAssignChanged(int32 NewChunkID)
{
ChunkIDSelected = NewChunkID;
}
FReply FAssetContextMenu::OnChunkIDAssignCommit(TSharedPtr<SWindow> Window)
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
for (const auto& SelectedAsset : AssetViewSelectedAssets)
{
UPackage* Package = FindPackage(NULL, *SelectedAsset.PackageName.ToString());
if (Package)
{
TArray<int32> CurrentChunks = Package->GetChunkIDs();
CurrentChunks.AddUnique(ChunkIDSelected);
Package->SetChunkIDs(CurrentChunks);
Package->SetDirtyFlag(true);
}
}
Window->RequestDestroyWindow();
return FReply::Handled();
}
FReply FAssetContextMenu::OnChunkIDAssignCancel(TSharedPtr<SWindow> Window)
{
Window->RequestDestroyWindow();
return FReply::Handled();
}
void FAssetContextMenu::ExecuteRemoveChunkID(int32 ChunkID)
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
for (const auto& SelectedAsset : AssetViewSelectedAssets)
{
UPackage* Package = FindPackage(NULL, *SelectedAsset.PackageName.ToString());
if (Package)
{
int32 FoundIndex;
TArray<int32> CurrentChunks = Package->GetChunkIDs();
CurrentChunks.Find(ChunkID, FoundIndex);
if (FoundIndex != INDEX_NONE)
{
CurrentChunks.RemoveAt(FoundIndex);
Package->SetChunkIDs(CurrentChunks);
Package->SetDirtyFlag(true);
}
}
}
}
#undef LOCTEXT_NAMESPACE