Files
UnrealEngineUWP/Engine/Source/Editor/ContentBrowser/Private/AssetContextMenu.cpp
Daniel Wright b3000a2491 Fixed MaterialInstanceConstantName compile error
#lockdown Zachary.EdgertonJones

[CL 2521894 by Daniel Wright in Main branch]
2015-04-22 17:46:00 -04:00

2191 lines
67 KiB
C++

// Copyright 1998-2015 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 "ISizeMapModule.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"
#include "SColorPicker.h"
#include "GenericCommands.h"
#include "SNotificationList.h"
#include "NotificationManager.h"
#include "Engine/LevelStreaming.h"
#define LOCTEXT_NAMESPACE "ContentBrowser"
FAssetContextMenu::FAssetContextMenu(const TWeakPtr<SAssetView>& InAssetView)
: AssetView(InAssetView)
, bAtLeastOneNonRedirectorSelected(false)
, bAtLeastOneClassSelected(false)
, bCanExecuteSCCMerge(false)
, bCanExecuteSCCCheckOut(false)
, bCanExecuteSCCOpenForAdd(false)
, bCanExecuteSCCCheckIn(false)
, bCanExecuteSCCHistory(false)
, bCanExecuteSCCRevert(false)
, bCanExecuteSCCSync(false)
{
}
void FAssetContextMenu::BindCommands(TSharedPtr< FUICommandList >& Commands)
{
Commands->MapAction(FGenericCommands::Get().Duplicate, FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteDuplicate),
FCanExecuteAction::CreateSP(this, &FAssetContextMenu::CanExecuteDuplicate),
FIsActionChecked(),
FIsActionButtonVisible::CreateSP(this, &FAssetContextMenu::CanExecuteDuplicate)
));
}
TSharedRef<SWidget> FAssetContextMenu::MakeContextMenu(const TArray<FAssetData>& InSelectedAssets, const FSourcesData& InSourcesData, TSharedPtr< FUICommandList > InCommandList)
{
SetSelectedAssets(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 imported asset context menu options
AddImportedAssetMenuOptions(MenuBuilder);
// Add quick access to common commands.
AddCommonMenuOptions(MenuBuilder);
// Add reference options
AddReferenceMenuOptions(MenuBuilder);
// Add documentation options
AddDocumentationMenuOptions(MenuBuilder);
// Add collection options
AddCollectionMenuOptions(MenuBuilder);
// Add source control options
AddSourceControlMenuOptions(MenuBuilder);
}
return MenuBuilder.MakeWidget();
}
void FAssetContextMenu::SetSelectedAssets(const TArray<FAssetData>& InSelectedAssets)
{
SelectedAssets = InSelectedAssets;
}
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::AddImportedAssetMenuOptions(FMenuBuilder& MenuBuilder)
{
if (AreImportedAssetActionsVisible())
{
TArray<FString> ResolvedFilePaths;
GetSelectedAssetSourceFilePaths(ResolvedFilePaths);
MenuBuilder.BeginSection("ImportedAssetActions", LOCTEXT("ImportedAssetActionsMenuHeading", "Imported Asset"));
{
// Reimport
MenuBuilder.AddMenuEntry(
LOCTEXT("Reimport", "Reimport"),
LOCTEXT("ReimportTooltip", "Reimport the selected asset(s) from the source file on disk."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteReimport),
FCanExecuteAction()
)
);
// Show Source In Explorer
MenuBuilder.AddMenuEntry(
LOCTEXT("FindSourceFile", "Open Source Location"),
LOCTEXT("FindSourceFileTooltip", "Opens the folder containing the source of the selected asset(s)."),
FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.AssetActions.OpenSourceLocation"),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteFindSourceInExplorer, ResolvedFilePaths),
FCanExecuteAction::CreateSP(this, &FAssetContextMenu::CanExecuteImportedAssetActions, ResolvedFilePaths)
)
);
// Open In External Editor
MenuBuilder.AddMenuEntry(
LOCTEXT("OpenInExternalEditor", "Open In External Editor"),
LOCTEXT("OpenInExternalEditorTooltip", "Open the selected asset(s) in the default external editor."),
FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.AssetActions.OpenInExternalEditor"),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteOpenInExternalEditor, ResolvedFilePaths),
FCanExecuteAction::CreateSP(this, &FAssetContextMenu::CanExecuteImportedAssetActions, ResolvedFilePaths)
)
);
}
MenuBuilder.EndSection();
return true;
}
return false;
}
bool FAssetContextMenu::AddCommonMenuOptions(FMenuBuilder& MenuBuilder)
{
MenuBuilder.BeginSection("CommonAssetActions", LOCTEXT("CommonAssetActionsMenuHeading", "Common"));
{
// Edit
MenuBuilder.AddMenuEntry(
LOCTEXT("EditAsset", "Edit..."),
LOCTEXT("EditAssetTooltip", "Opens the selected asset(s) for edit."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteEditAsset) )
);
// Rename
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Rename, NAME_None,
LOCTEXT("Rename", "Rename"),
LOCTEXT("RenameTooltip", "Rename the selected asset."),
FSlateIcon()
);
// Duplicate
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Duplicate, NAME_None,
LOCTEXT("Duplicate", "Duplicate"),
LOCTEXT("DuplicateTooltip", "Create a copy of the selected asset(s)."),
FSlateIcon()
);
// Save
MenuBuilder.AddMenuEntry(
LOCTEXT("SaveAsset", "Save"),
LOCTEXT("SaveAssetTooltip", "Saves the asset to file."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteSaveAsset ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSaveAsset )
)
);
// Delete
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Delete, NAME_None,
LOCTEXT("Delete", "Delete"),
LOCTEXT("DeleteTooltip", "Delete the selected assets."),
FSlateIcon()
);
// Asset Actions sub-menu
MenuBuilder.AddSubMenu(
LOCTEXT("AssetActionsSubMenuLabel", "Asset Actions"),
LOCTEXT("AssetActionsSubMenuToolTip", "Other asset actions"),
FNewMenuDelegate::CreateSP(this, &FAssetContextMenu::MakeAssetActionsSubMenu),
FUIAction(
FExecuteAction(),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteAssetActions )
),
NAME_None,
EUserInterfaceActionType::Button,
false,
FSlateIcon()
);
}
MenuBuilder.EndSection();
return true;
}
void FAssetContextMenu::MakeAssetActionsSubMenu(FMenuBuilder& MenuBuilder)
{
// Create BP Using This
MenuBuilder.AddMenuEntry(
LOCTEXT("CreateBlueprintUsing", "Create Blueprint Using This..."),
LOCTEXT("CreateBlueprintUsingTooltip", "Create a new Blueprint and add this asset to it"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.CreateClassBlueprint"),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteCreateBlueprintUsing),
FCanExecuteAction::CreateSP(this, &FAssetContextMenu::CanExecuteCreateBlueprintUsing)
)
);
// Capture Thumbnail
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)
)
);
}
// Clear Thumbnail
if (CanClearCustomThumbnails())
{
MenuBuilder.AddMenuEntry(
LOCTEXT("ClearCustomThumbnail", "Clear Thumbnail"),
LOCTEXT("ClearCustomThumbnailTooltip", "Clears all custom thumbnails for selected assets."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteClearThumbnail) )
);
}
// FIND ACTIONS
MenuBuilder.BeginSection("AssetContextFindActions", LOCTEXT("AssetContextFindActionsMenuHeading", "Find"));
{
// Select Actors Using This Asset
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)
)
);
// Open Containing Folder
MenuBuilder.AddMenuEntry(
LOCTEXT("SyncToAssetTree", "Show in Sources Panel"),
LOCTEXT("SyncToAssetTreeTooltip", "Selects the folder that contains this asset in the Content Browser Sources Panel."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteSyncToAssetTree),
FCanExecuteAction::CreateSP(this, &FAssetContextMenu::CanExecuteSyncToAssetTree)
)
);
// Find in Explorer
MenuBuilder.AddMenuEntry(
ContentBrowserUtils::GetExploreFolderText(),
LOCTEXT("FindInExplorerTooltip", "Finds this asset on disk"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteFindInExplorer ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteFindInExplorer )
)
);
}
MenuBuilder.EndSection();
// MOVE ACTIONS
MenuBuilder.BeginSection("AssetContextMoveActions", LOCTEXT("AssetContextMoveActionsMenuHeading", "Move"));
{
// Export
MenuBuilder.AddMenuEntry(
LOCTEXT("Export", "Export..."),
LOCTEXT("ExportTooltip", "Export the selected assets to file."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteExport ) )
);
// Bulk Export
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 ) )
);
}
// Migrate
MenuBuilder.AddMenuEntry(
LOCTEXT("MigrateAsset", "Migrate..."),
LOCTEXT("MigrateAssetTooltip", "Copies all selected assets and their dependencies to another project"),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteMigrateAsset ) )
);
}
MenuBuilder.EndSection();
// ADVANCED ACTIONS
MenuBuilder.BeginSection("AssetContextAdvancedActions", LOCTEXT("AssetContextAdvancedActionsMenuHeading", "Advanced"));
{
// Replace References
if (CanExecuteConsolidate())
{
MenuBuilder.AddMenuEntry(
LOCTEXT("ReplaceReferences", "Replace References"),
LOCTEXT("ConsolidateTooltip", "Replace references to the selected assets."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteConsolidate)
)
);
}
// Property Matrix
bool bCanUsePropertyMatrix = true;
static FName MaterialName(TEXT("Material"));
static FName MaterialInstanceConstantName(TEXT("MaterialInstanceConstant"));
for (auto& Asset : SelectedAssets)
{
if (Asset.AssetClass == MaterialName || Asset.AssetClass == MaterialInstanceConstantName)
{
bCanUsePropertyMatrix = false;
break;
}
}
if (bCanUsePropertyMatrix)
{
MenuBuilder.AddMenuEntry(
LOCTEXT("PropertyMatrix", "Bulk Edit via 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)
)
);
}
// Chunk actions
if (GetDefault<UEditorExperimentalSettings>()->bContextMenuChunkAssignments)
{
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) )
);
}
}
MenuBuilder.EndSection();
}
bool FAssetContextMenu::CanExecuteAssetActions() const
{
return !bAtLeastOneClassSelected;
}
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.AddMenuEntry(
LOCTEXT("SizeMap", "Size Map..."),
LOCTEXT("SizeMapTooltip", "Shows an interactive map of the approximate memory used by this asset and everything it references."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteShowSizeMap )
)
);
}
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( "AssetCode"/*, LOCTEXT("AssetCodeHeading", "C++")*/ );
{
MenuBuilder.AddMenuEntry(
FText::Format( LOCTEXT("GoToCodeForAsset", "Open {0}"), FText::FromString( CodeFileName ) ),
FText::Format( LOCTEXT("GoToCodeForAsset_ToolTip", "Opens the header file for this asset ({0}) in a code editing program"), FText::FromString( CodeFileName ) ),
FSlateIcon(FEditorStyle::GetStyleSetName(), "ContentBrowser.AssetActions.GoToCodeForAsset"),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToCodeForAsset, SelectedClass ) )
);
}
MenuBuilder.EndSection();
}
}
const FString DocumentationLink = FEditorClassUtils::GetDocumentationLink(SelectedClass);
if (bIsBlueprint || !DocumentationLink.IsEmpty())
{
bAddedOption = true;
MenuBuilder.BeginSection( "AssetDocumentation"/*, LOCTEXT("AseetDocsHeading", "Documentation")*/ );
{
if (bIsBlueprint)
{
if (!DocumentationLink.IsEmpty())
{
MenuBuilder.AddMenuEntry(
FText::Format( LOCTEXT("GoToDocsForAssetWithClass", "View Documentation - {0}"), SelectedClass->GetDisplayNameText() ),
FText::Format( LOCTEXT("GoToDocsForAssetWithClass_ToolTip", "Click to open documentation for {0}"), SelectedClass->GetDisplayNameText() ),
FSlateIcon(FEditorStyle::GetStyleSetName(), "HelpIcon.Hovered" ),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToDocsForAsset, SelectedClass ) )
);
}
UEnum* BlueprintTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EBlueprintType"), true);
FString* EnumString = SelectedAssets[0].TagsAndValues.Find(GET_MEMBER_NAME_CHECKED(UBlueprint,BlueprintType));
EBlueprintType BlueprintType = (EnumString ? (EBlueprintType)BlueprintTypeEnum->FindEnumIndex(**EnumString) : BPTYPE_Normal);
switch (BlueprintType)
{
case BPTYPE_FunctionLibrary:
MenuBuilder.AddMenuEntry(
LOCTEXT("GoToDocsForMacroBlueprint", "View Documentation - Function Library"),
LOCTEXT("GoToDocsForMacroBlueprint_ToolTip", "Click to open documentation on blueprint function libraries"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "HelpIcon.Hovered" ),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToDocsForAsset, UBlueprint::StaticClass(), FString(TEXT("UBlueprint_FunctionLibrary")) ) )
);
break;
case BPTYPE_Interface:
MenuBuilder.AddMenuEntry(
LOCTEXT("GoToDocsForInterfaceBlueprint", "View Documentation - Interface"),
LOCTEXT("GoToDocsForInterfaceBlueprint_ToolTip", "Click to open documentation on blueprint interfaces"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "HelpIcon.Hovered" ),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToDocsForAsset, UBlueprint::StaticClass(), FString(TEXT("UBlueprint_Interface")) ) )
);
break;
case BPTYPE_MacroLibrary:
MenuBuilder.AddMenuEntry(
LOCTEXT("GoToDocsForInterfaceBlueprint", "View Documentation - Macro"),
LOCTEXT("GoToDocsForInterfaceBlueprint_ToolTip", "Click to open documentation on blueprint macros"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "HelpIcon.Hovered" ),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToDocsForAsset, UBlueprint::StaticClass(), FString(TEXT("UBlueprint_Macro")) ) )
);
break;
default:
MenuBuilder.AddMenuEntry(
LOCTEXT("GoToDocsForBlueprint", "View Documentation - Blueprint"),
LOCTEXT("GoToDocsForBlueprint_ToolTip", "Click to open documentation on blueprints"),
FSlateIcon(FEditorStyle::GetStyleSetName(), "HelpIcon.Hovered" ),
FUIAction( FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteGoToDocsForAsset, UBlueprint::StaticClass(), FString(TEXT("UBlueprint")) ) )
);
}
}
else
{
MenuBuilder.AddMenuEntry(
LOCTEXT("GoToDocsForAsset", "View Documentation"),
LOCTEXT("GoToDocsForAsset_ToolTip", "Click to open documentation"),
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.AddMenuSeparator();
if ( ISourceControlModule::Get().IsEnabled() )
{
// SCC sub menu
MenuBuilder.AddSubMenu(
LOCTEXT("SourceControlSubMenuLabel", "Source Control"),
LOCTEXT("SourceControlSubMenuToolTip", "Source control actions."),
FNewMenuDelegate::CreateSP(this, &FAssetContextMenu::FillSourceControlSubMenu),
FUIAction(
FExecuteAction(),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSourceControlActions )
),
NAME_None,
EUserInterfaceActionType::Button,
false,
FSlateIcon(FEditorStyle::GetStyleSetName(), "SourceControl.StatusIcon.On")
);
}
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(FEditorStyle::GetStyleSetName(), "SourceControl.StatusIcon.Unknown"),
FUIAction(
FExecuteAction::CreateSP( this, &FAssetContextMenu::ExecuteEnableSourceControl ),
FCanExecuteAction::CreateSP( this, &FAssetContextMenu::CanExecuteSourceControlActions )
)
);
}
// Diff selected
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)
)
);
}
return true;
}
void FAssetContextMenu::FillSourceControlSubMenu(FMenuBuilder& MenuBuilder)
{
if( CanExecuteSCCMerge() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("SCCMerge", "Merge"),
LOCTEXT("SCCMergeTooltip", "Opens the blueprint editor with the merge tool open."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP(this, &FAssetContextMenu::ExecuteSCCMerge),
FCanExecuteAction::CreateSP(this, &FAssetContextMenu::CanExecuteSCCMerge)
)
);
}
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 )
)
);
}
}
bool FAssetContextMenu::CanExecuteSourceControlActions() const
{
return !bAtLeastOneClassSelected;
}
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;
}
bool FAssetContextMenu::AreImportedAssetActionsVisible() const
{
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
// Check that all of the selected assets are imported
for (auto& SelectedAsset : SelectedAssets)
{
auto AssetClass = SelectedAsset.GetClass();
if (AssetClass)
{
auto AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(AssetClass).Pin();
if (!AssetTypeActions.IsValid() || !AssetTypeActions->IsImportedAsset())
{
return false;
}
}
}
return true;
}
bool FAssetContextMenu::CanExecuteImportedAssetActions(const TArray<FString> ResolvedFilePaths) const
{
// Verify that all the file paths are legitimate
for (const auto& SourceFilePath : ResolvedFilePaths)
{
if (!SourceFilePath.Len() || IFileManager::Get().FileSize(*SourceFilePath) == INDEX_NONE)
{
return false;
}
}
return true;
}
void FAssetContextMenu::ExecuteReimport()
{
// Reimport all selected assets
for (auto& SelectedAsset : SelectedAssets)
{
const auto Asset = SelectedAsset.GetAsset();
if (Asset)
{
FReimportManager::Instance()->Reimport(Asset, /*bAskForNewFileIfMissing=*/true);
}
}
}
void FAssetContextMenu::ExecuteFindSourceInExplorer(const TArray<FString> ResolvedFilePaths)
{
// Open all files in the explorer
for (const auto& SourceFilePath : ResolvedFilePaths)
{
FPlatformProcess::ExploreFolder(*FPaths::GetPath(SourceFilePath));
}
}
void FAssetContextMenu::ExecuteOpenInExternalEditor(const TArray<FString> ResolvedFilePaths)
{
// Open all files in their respective editor
for (const auto& SourceFilePath : ResolvedFilePaths)
{
FPlatformProcess::LaunchFileInDefaultExternalApplication(*SourceFilePath, NULL, ELaunchVerb::Edit);
}
}
void FAssetContextMenu::GetSelectedAssetsByClass(TMap<UClass*, TArray<UObject*> >& OutSelectedAssetsByClass) const
{
// Sort all selected assets by class
for (const auto& SelectedAsset : SelectedAssets)
{
auto Asset = SelectedAsset.GetAsset();
auto AssetClass = Asset->GetClass();
if ( !OutSelectedAssetsByClass.Contains(AssetClass) )
{
OutSelectedAssetsByClass.Add(AssetClass);
}
OutSelectedAssetsByClass[AssetClass].Add(Asset);
}
}
void FAssetContextMenu::GetSelectedAssetSourceFilePaths(TArray<FString>& OutFilePaths) const
{
OutFilePaths.Empty();
TMap<UClass*, TArray<UObject*> > SelectedAssetsByClass;
GetSelectedAssetsByClass(SelectedAssetsByClass);
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
// Get the source file paths for the assets of each type
for (const auto& AssetsByClassPair : SelectedAssetsByClass)
{
const auto AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(AssetsByClassPair.Key);
if (AssetTypeActions.IsValid())
{
const auto& TypeAssets = AssetsByClassPair.Value;
TArray<FString> AssetSourcePaths;
AssetTypeActions.Pin()->GetResolvedSourceFilePaths(TypeAssets, AssetSourcePaths);
OutFilePaths.Append(AssetSourcePaths);
}
}
}
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)
{
FScopedSlowTask SlowTask(2 + AssetsToFind.Num(), NSLOCTEXT("AssetContextMenu", "FindAssetInWorld", "Finding actors that use this asset..."));
SlowTask.MakeDialog();
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
TArray< TWeakObjectPtr<UObject> > OutObjects;
WorldReferenceGenerator ObjRefGenerator;
SlowTask.EnterProgressFrame();
ObjRefGenerator.BuildReferencingData();
for (int32 AssetIdx = 0; AssetIdx < AssetsToFind.Num(); ++AssetIdx)
{
SlowTask.EnterProgressFrame();
ObjRefGenerator.MarkAllObjects();
ObjRefGenerator.Generate(AssetsToFind[AssetIdx], OutObjects);
}
SlowTask.EnterProgressFrame();
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);
}
}
}
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::ExecuteEditAsset()
{
TMap<UClass*, TArray<UObject*> > SelectedAssetsByClass;
GetSelectedAssetsByClass(SelectedAssetsByClass);
// Open
for (const auto& AssetsByClassPair : SelectedAssetsByClass)
{
const auto& TypeAssets = AssetsByClassPair.Value;
FAssetEditorManager::Get().OpenEditorForAssets(TypeAssets);
}
}
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 = TEXT("");
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::ExecuteShowSizeMap()
{
TArray<FName> PackageNames;
for ( auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt )
{
PackageNames.Add(AssetIt->PackageName);
}
if ( PackageNames.Num() > 0 )
{
ISizeMapModule::Get().InvokeSizeMapTab(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)
{
ExecuteGoToDocsForAsset(SelectedClass, FString());
}
void FAssetContextMenu::ExecuteGoToDocsForAsset(UClass* SelectedClass, const FString ExcerptSection)
{
if (SelectedClass)
{
FString DocumentationLink = FEditorClassUtils::GetDocumentationLink(SelectedClass, ExcerptSection);
if (!DocumentationLink.IsEmpty())
{
IDocumentation::Get()->Open(DocumentationLink, FDocumentationSourceInfo(TEXT("cb_docs")));
}
}
}
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::ExecuteSCCMerge()
{
FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
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();
auto AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass( CurrentObject->GetClass() ).Pin();
if( AssetTypeActions.IsValid() )
{
AssetTypeActions->Merge(CurrentObject);
}
}
}
}
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<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 )
{
TArray<FString> PackageNames;
GetSelectedPackageNames(PackageNames);
const bool bUseSourceControlStateCache = true;
FSourceControlWindows::PromptForCheckin(bUseSourceControlStateCache, 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);
}
ExecuteSCCRefresh();
}
}
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
{
const TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
uint32 NumNonRedirectors = 0;
for(auto& AssetData : AssetViewSelectedAssets)
{
if(!AssetData.IsValid())
{
continue;
}
if(AssetData.AssetClass == NAME_Class)
{
return false;
}
if(AssetData.AssetClass != UObjectRedirector::StaticClass()->GetFName())
{
++NumNonRedirectors;
}
}
return (NumNonRedirectors > 0);
}
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 // A single asset
&& AssetViewSelectedAssets[0].AssetClass != UObjectRedirector::StaticClass()->GetFName() // Which isn't a redirector
&& AssetViewSelectedAssets[0].AssetClass != NAME_Class; // And isn't a class
const bool bOneFolderSelected = AssetViewSelectedAssets.Num() == 0 && SelectedFolders.Num() == 1 // A single folder
&& !ContentBrowserUtils::IsClassPath(SelectedFolders[0]); // Which doesn't belong to a class path
return (bOneAssetSelected || bOneFolderSelected) && !AssetView.Pin()->IsThumbnailEditMode();
}
bool FAssetContextMenu::CanExecuteDelete() const
{
TArray< FAssetData > AssetViewSelectedAssets = AssetView.Pin()->GetSelectedAssets();
TArray< FString > SelectedFolders = AssetView.Pin()->GetSelectedFolders();
int32 NumAssetItems, NumClassItems;
ContentBrowserUtils::CountItemTypes(AssetViewSelectedAssets, NumAssetItems, NumClassItems);
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedFolders, NumAssetPaths, NumClassPaths);
// We can't delete classes, or folders containing classes
return (NumAssetItems > 0 && NumClassItems == 0) || (NumAssetPaths > 0 && NumClassPaths == 0);
}
bool FAssetContextMenu::CanExecuteRemoveFromCollection() const
{
return SourcesData.Collections.Num() == 1;
}
bool FAssetContextMenu::CanExecuteSCCRefresh() const
{
return ISourceControlModule::Get().IsEnabled();
}
bool FAssetContextMenu::CanExecuteSCCMerge() const
{
FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
bool bCanExecuteMerge = bCanExecuteSCCMerge;
for (int32 AssetIdx = 0; AssetIdx < SelectedAssets.Num() && bCanExecuteMerge; AssetIdx++)
{
// Get the actual asset (will load it)
const FAssetData& AssetData = SelectedAssets[AssetIdx];
UObject* CurrentObject = AssetData.GetAsset();
if (CurrentObject)
{
auto AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(CurrentObject->GetClass()).Pin();
if (AssetTypeActions.IsValid())
{
bCanExecuteMerge = AssetTypeActions->CanMerge();
}
}
else
{
bCanExecuteMerge = false;
}
}
return bCanExecuteMerge;
}
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
{
if ( bAtLeastOneClassSelected )
{
return false;
}
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 && !bAtLeastOneClassSelected)
{
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;
bAtLeastOneClassSelected = false;
bCanExecuteSCCMerge = 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;
}
bAtLeastOneClassSelected |= AssetData.AssetClass == NAME_Class;
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->IsConflicted() )
{
bCanExecuteSCCMerge = true;
}
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
&& bAtLeastOneClassSelected
&& bCanExecuteSCCMerge
&& 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