Files
UnrealEngineUWP/Engine/Source/Editor/ContentBrowser/Private/PathContextMenu.cpp

1075 lines
34 KiB
C++
Raw Normal View History

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "ContentBrowserPCH.h"
#include "PathViewTypes.h"
#include "PathContextMenu.h"
#include "ISourceControlModule.h"
#include "SourceControlWindows.h"
#include "ContentBrowserModule.h"
#include "ReferenceViewer.h"
#include "ISizeMapModule.h"
#include "AssetToolsModule.h"
#include "Editor/UnrealEd/Public/PackageTools.h"
#include "SColorPicker.h"
#include "GenericCommands.h"
#include "NativeClassHierarchy.h"
#define LOCTEXT_NAMESPACE "ContentBrowser"
FPathContextMenu::FPathContextMenu(const TWeakPtr<SWidget>& InParentContent)
: ParentContent(InParentContent)
, bCanExecuteSCCCheckOut(false)
, bCanExecuteSCCOpenForAdd(false)
, bCanExecuteSCCCheckIn(false)
{
}
void FPathContextMenu::SetOnNewAssetRequested(const FNewAssetOrClassContextMenu::FOnNewAssetRequested& InOnNewAssetRequested)
{
OnNewAssetRequested = InOnNewAssetRequested;
}
void FPathContextMenu::SetOnNewClassRequested(const FNewAssetOrClassContextMenu::FOnNewClassRequested& InOnNewClassRequested)
{
OnNewClassRequested = InOnNewClassRequested;
}
void FPathContextMenu::SetOnImportAssetRequested( const FNewAssetOrClassContextMenu::FOnImportAssetRequested& InOnImportAssetRequested )
{
OnImportAssetRequested = InOnImportAssetRequested;
}
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
void FPathContextMenu::SetOnRenameFolderRequested(const FOnRenameFolderRequested& InOnRenameFolderRequested)
{
OnRenameFolderRequested = InOnRenameFolderRequested;
}
void FPathContextMenu::SetOnFolderDeleted(const FOnFolderDeleted& InOnFolderDeleted)
{
OnFolderDeleted = InOnFolderDeleted;
}
void FPathContextMenu::SetSelectedPaths(const TArray<FString>& InSelectedPaths)
{
SelectedPaths = InSelectedPaths;
}
TSharedRef<FExtender> FPathContextMenu::MakePathViewContextMenuExtender(const TArray<FString>& InSelectedPaths)
{
// 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_SelectedPaths> MenuExtenderDelegates = ContentBrowserModule.GetAllPathViewContextMenuExtenders();
TArray<TSharedPtr<FExtender>> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute( SelectedPaths ));
}
}
TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);
MenuExtender->AddMenuExtension("NewFolder", EExtensionHook::After, TSharedPtr<FUICommandList>(), FMenuExtensionDelegate::CreateSP(this, &FPathContextMenu::MakePathViewContextMenu));
return MenuExtender.ToSharedRef();
}
void FPathContextMenu::MakePathViewContextMenu(FMenuBuilder& MenuBuilder)
{
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedPaths, NumAssetPaths, NumClassPaths);
// Only add something if at least one folder is selected
if ( SelectedPaths.Num() > 0 )
{
const bool bHasAssetPaths = NumAssetPaths > 0;
const bool bHasClassPaths = NumClassPaths > 0;
// Common operations section //
MenuBuilder.BeginSection("PathViewFolderOptions", LOCTEXT("PathViewOptionsMenuHeading", "Folder Options") );
{
if(bHasAssetPaths)
{
FText NewAssetToolTip;
if(SelectedPaths.Num() == 1)
{
if(CanCreateAsset())
{
NewAssetToolTip = FText::Format(LOCTEXT("NewAssetTooltip_CreateIn", "Create a new asset in {0}."), FText::FromString(SelectedPaths[0]));
}
else
{
NewAssetToolTip = FText::Format(LOCTEXT("NewAssetTooltip_InvalidPath", "Cannot create new assets in {0}."), FText::FromString(SelectedPaths[0]));
}
}
else
{
NewAssetToolTip = LOCTEXT("NewAssetTooltip_InvalidNumberOfPaths", "Can only create assets when there is a single path selected.");
}
// New Asset (submenu)
MenuBuilder.AddSubMenu(
LOCTEXT( "NewAssetLabel", "New Asset" ),
NewAssetToolTip,
FNewMenuDelegate::CreateRaw( this, &FPathContextMenu::MakeNewAssetSubMenu ),
FUIAction(
FExecuteAction(),
FCanExecuteAction::CreateRaw( this, &FPathContextMenu::CanCreateAsset )
),
NAME_None,
EUserInterfaceActionType::Button,
false,
FSlateIcon()
);
}
if(bHasClassPaths)
{
FText NewClassToolTip;
if(SelectedPaths.Num() == 1)
{
if(CanCreateClass())
{
NewClassToolTip = FText::Format(LOCTEXT("NewClassTooltip_CreateIn", "Create a new class in {0}."), FText::FromString(SelectedPaths[0]));
}
else
{
NewClassToolTip = FText::Format(LOCTEXT("NewClassTooltip_InvalidPath", "Cannot create new classes in {0}."), FText::FromString(SelectedPaths[0]));
}
}
else
{
NewClassToolTip = LOCTEXT("NewClassTooltip_InvalidNumberOfPaths", "Can only create classes when there is a single path selected.");
}
// New Class
MenuBuilder.AddMenuEntry(
LOCTEXT("NewClassLabel", "New C++ Class..."),
NewClassToolTip,
FSlateIcon(FEditorStyle::GetStyleSetName(), "MainFrame.AddCodeToProject"),
FUIAction(
FExecuteAction::CreateRaw( this, &FPathContextMenu::ExecuteCreateClass ),
FCanExecuteAction::CreateRaw( this, &FPathContextMenu::CanCreateClass )
)
);
}
// Explore
MenuBuilder.AddMenuEntry(
ContentBrowserUtils::GetExploreFolderText(),
LOCTEXT("ExploreTooltip", "Finds this folder on disk."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteExplore ) )
);
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Rename, NAME_None,
LOCTEXT("RenameFolder", "Rename"),
LOCTEXT("RenameFolderTooltip", "Rename the selected folder.")
);
// If any colors have already been set, display color options as a sub menu
if ( ContentBrowserUtils::HasCustomColors() )
{
// Set Color (submenu)
MenuBuilder.AddSubMenu(
LOCTEXT("SetColor", "Set Color"),
LOCTEXT("SetColorTooltip", "Sets the color this folder should appear as."),
FNewMenuDelegate::CreateRaw( this, &FPathContextMenu::MakeSetColorSubMenu ),
false,
FSlateIcon()
);
}
else
{
// Set Color
MenuBuilder.AddMenuEntry(
LOCTEXT("SetColor", "Set Color"),
LOCTEXT("SetColorTooltip", "Sets the color this folder should appear as."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecutePickColor ) )
);
}
}
MenuBuilder.EndSection();
if(bHasAssetPaths)
{
// Bulk operations section //
MenuBuilder.BeginSection("PathContextBulkOperations", LOCTEXT("AssetTreeBulkMenuHeading", "Bulk Operations") );
{
// Save
MenuBuilder.AddMenuEntry(
LOCTEXT("SaveFolder", "Save All"),
LOCTEXT("SaveFolderTooltip", "Saves all modified assets in this folder."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteSaveFolder ) )
);
// Delete
MenuBuilder.AddMenuEntry(FGenericCommands::Get().Delete, NAME_None,
LOCTEXT("DeleteFolder", "Delete"),
LOCTEXT("DeleteFolderTooltip", "Removes this folder and all assets it contains."),
FSlateIcon()
);
// Reference Viewer
MenuBuilder.AddMenuEntry(
LOCTEXT("ReferenceViewer", "Reference Viewer..."),
LOCTEXT("ReferenceViewerOnFolderTooltip", "Shows a graph of references for this folder."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteReferenceViewer ) )
);
// Size Map
MenuBuilder.AddMenuEntry(
LOCTEXT("SizeMap", "Size Map..."),
LOCTEXT("SizeMapOnFolderTooltip", "Shows an interactive map of the approximate memory used by the assets in this folder and everything they reference."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteSizeMap ) )
);
// Fix Up Redirectors in Folder
MenuBuilder.AddMenuEntry(
LOCTEXT("FixUpRedirectorsInFolder", "Fix Up Redirectors in Folder"),
LOCTEXT("FixUpRedirectorsInFolderTooltip", "Finds referencers to all redirectors in the selected folders and resaves them if possible, then deletes any redirectors that had all their referencers fixed."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteFixUpRedirectorsInFolder ) )
);
if ( NumAssetPaths == 1 && NumClassPaths == 0 )
{
// Migrate Folder
MenuBuilder.AddMenuEntry(
LOCTEXT("MigrateFolder", "Migrate..."),
LOCTEXT("MigrateFolderTooltip", "Copies assets found in this folder and their dependencies to another game content folder."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteMigrateFolder ) )
);
}
}
MenuBuilder.EndSection();
// Source control section //
MenuBuilder.BeginSection("PathContextSourceControl", LOCTEXT("AssetTreeSCCMenuHeading", "Source Control") );
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
if ( SourceControlProvider.IsEnabled() )
{
// Check out
MenuBuilder.AddMenuEntry(
LOCTEXT("FolderSCCCheckOut", "Check Out"),
LOCTEXT("FolderSCCCheckOutTooltip", "Checks out all assets from source control which are in this folder."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteSCCCheckOut ),
FCanExecuteAction::CreateSP( this, &FPathContextMenu::CanExecuteSCCCheckOut )
)
);
// Open for Add
MenuBuilder.AddMenuEntry(
LOCTEXT("FolderSCCOpenForAdd", "Mark For Add"),
LOCTEXT("FolderSCCOpenForAddTooltip", "Adds all assets to source control that are in this folder and not already added."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteSCCOpenForAdd ),
FCanExecuteAction::CreateSP( this, &FPathContextMenu::CanExecuteSCCOpenForAdd )
)
);
// Check in
MenuBuilder.AddMenuEntry(
LOCTEXT("FolderSCCCheckIn", "Check In"),
LOCTEXT("FolderSCCCheckInTooltip", "Checks in all assets to source control which are in this folder."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteSCCCheckIn ),
FCanExecuteAction::CreateSP( this, &FPathContextMenu::CanExecuteSCCCheckIn )
)
);
// Sync
MenuBuilder.AddMenuEntry(
LOCTEXT("FolderSCCSync", "Sync"),
LOCTEXT("FolderSCCSyncTooltip", "Syncs all the assets in this folder to the latest version."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteSCCSync ),
FCanExecuteAction::CreateSP( this, &FPathContextMenu::CanExecuteSCCSync )
)
);
}
else
{
MenuBuilder.AddMenuEntry(
LOCTEXT("FolderSCCConnect", "Connect To Source Control"),
LOCTEXT("FolderSCCConnectTooltip", "Connect to source control to allow source control operations to be performed on content and levels."),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteSCCConnect ),
FCanExecuteAction::CreateSP( this, &FPathContextMenu::CanExecuteSCCConnect )
)
);
}
MenuBuilder.EndSection();
}
}
}
bool FPathContextMenu::CanCreateAsset() const
{
// We can only create assets when we have a single asset path selected
return SelectedPaths.Num() == 1 && !ContentBrowserUtils::IsClassPath(SelectedPaths[0]);
}
void FPathContextMenu::MakeNewAssetSubMenu(FMenuBuilder& MenuBuilder)
{
if ( SelectedPaths.Num() )
{
FNewAssetOrClassContextMenu::MakeContextMenu(
MenuBuilder,
SelectedPaths,
OnNewAssetRequested,
FNewAssetOrClassContextMenu::FOnNewClassRequested(),
FNewAssetOrClassContextMenu::FOnNewFolderRequested(),
OnImportAssetRequested,
FNewAssetOrClassContextMenu::FOnGetContentRequested()
);
}
}
void FPathContextMenu::ExecuteCreateClass()
{
OnNewClassRequested.ExecuteIfBound(SelectedPaths[0]);
}
bool FPathContextMenu::CanCreateClass() const
{
// We can only create assets when we have a single class path selected
return SelectedPaths.Num() == 1 && ContentBrowserUtils::IsValidPathToCreateNewClass(SelectedPaths[0]);
}
void FPathContextMenu::MakeSetColorSubMenu(FMenuBuilder& MenuBuilder)
{
// New Color
MenuBuilder.AddMenuEntry(
LOCTEXT("NewColor", "New Color"),
LOCTEXT("NewColorTooltip", "Changes the color this folder should appear as."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecutePickColor ) )
);
// Clear Color (only required if any of the selection has one)
if ( SelectedHasCustomColors() )
{
MenuBuilder.AddMenuEntry(
LOCTEXT("ClearColor", "Clear Color"),
LOCTEXT("ClearColorTooltip", "Resets the color this folder appears as."),
FSlateIcon(),
FUIAction( FExecuteAction::CreateSP( this, &FPathContextMenu::ExecuteResetColor ) )
);
}
// Add all the custom colors the user has chosen so far
TArray< FLinearColor > CustomColors;
if ( ContentBrowserUtils::HasCustomColors( &CustomColors ) )
{
MenuBuilder.BeginSection("PathContextCustomColors", LOCTEXT("CustomColorsExistingColors", "Existing Colors") );
{
for ( int32 ColorIndex = 0; ColorIndex < CustomColors.Num(); ColorIndex++ )
{
const FLinearColor& Color = CustomColors[ ColorIndex ];
MenuBuilder.AddWidget(
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(2, 0, 0, 0)
[
SNew(SButton)
.ButtonStyle( FEditorStyle::Get(), "Menu.Button" )
.OnClicked( this, &FPathContextMenu::OnColorClicked, Color )
[
SNew(SColorBlock)
.Color( Color )
.Size( FVector2D(77,16) )
]
],
LOCTEXT("CustomColor", ""),
/*bNoIndent=*/true
);
}
}
MenuBuilder.EndSection();
}
}
void FPathContextMenu::ExecuteMigrateFolder()
{
const FString& SourcesPath = GetFirstSelectedPath();
if ( ensure(SourcesPath.Len()) )
{
// @todo Make sure the asset registry has completed discovering assets, or else GetAssetsByPath() will not find all the assets in the folder! Add some UI to wait for this with a cancel button
FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
if ( AssetRegistryModule.Get().IsLoadingAssets() )
{
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT( "MigrateFolderAssetsNotDiscovered", "You must wait until asset discovery is complete to migrate a folder" ));
return;
}
// Get a list of package names for input into MigratePackages
TArray<FAssetData> AssetDataList;
TArray<FName> PackageNames;
ContentBrowserUtils::GetAssetsInPaths(SelectedPaths, AssetDataList);
for ( auto AssetIt = AssetDataList.CreateConstIterator(); AssetIt; ++AssetIt )
{
PackageNames.Add((*AssetIt).PackageName);
}
// Load all the assets in the selected paths
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
AssetToolsModule.Get().MigratePackages( PackageNames );
}
}
void FPathContextMenu::ExecuteExplore()
{
for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
{
const FString& Path = SelectedPaths[PathIdx];
FString FilePath;
if (ContentBrowserUtils::IsClassPath(Path))
{
TSharedRef<FNativeClassHierarchy> NativeClassHierarchy = FContentBrowserSingleton::Get().GetNativeClassHierarchy();
if (NativeClassHierarchy->GetFileSystemPath(Path, FilePath))
{
FilePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*FilePath);
}
}
else
{
FilePath = FPaths::ConvertRelativePathToFull(FPackageName::LongPackageNameToFilename(Path + TEXT("/")));
}
if (!FilePath.IsEmpty())
{
// If the folder has not yet been created, make is right before we try to explore to it
if (!IFileManager::Get().DirectoryExists(*FilePath))
{
IFileManager::Get().MakeDirectory(*FilePath, /*Tree=*/true);
}
FPlatformProcess::ExploreFolder(*FilePath);
}
}
}
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
bool FPathContextMenu::CanExecuteRename() const
{
// We can't rename when we have more than one path selected
if (SelectedPaths.Num() != 1)
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
{
return false;
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
}
// We can't rename a root folder
if (ContentBrowserUtils::IsRootDir(SelectedPaths[0]))
{
return false;
}
// We can't rename *any* folders that belong to class roots
if (ContentBrowserUtils::IsClassPath(SelectedPaths[0]))
{
return false;
}
return true;
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
}
void FPathContextMenu::ExecuteRename()
{
check(SelectedPaths.Num() == 1);
if (OnRenameFolderRequested.IsBound())
{
OnRenameFolderRequested.Execute(SelectedPaths[0]);
}
}
void FPathContextMenu::ExecuteResetColor()
{
ResetColors();
}
void FPathContextMenu::ExecutePickColor()
{
// Spawn a color picker, so the user can select which color they want
TArray<FLinearColor*> LinearColorArray;
FColorPickerArgs PickerArgs;
PickerArgs.bIsModal = false;
PickerArgs.ParentWidget = ParentContent.Pin();
if ( SelectedPaths.Num() > 0 )
{
// Make sure an color entry exists for all the paths, otherwise they won't update in realtime with the widget color
for (int32 PathIdx = SelectedPaths.Num() - 1; PathIdx >= 0; --PathIdx)
{
const FString& Path = SelectedPaths[PathIdx];
TSharedPtr<FLinearColor> Color = ContentBrowserUtils::LoadColor( Path );
if ( !Color.IsValid() )
{
Color = MakeShareable( new FLinearColor( ContentBrowserUtils::GetDefaultColor() ) );
ContentBrowserUtils::SaveColor( Path, Color, true );
}
else
{
// Default the color to the first valid entry
PickerArgs.InitialColorOverride = *Color.Get();
}
LinearColorArray.Add( Color.Get() );
}
PickerArgs.LinearColorArray = &LinearColorArray;
}
PickerArgs.OnColorPickerWindowClosed = FOnWindowClosed::CreateSP(this, &FPathContextMenu::NewColorComplete);
OpenColorPicker(PickerArgs);
}
void FPathContextMenu::NewColorComplete(const TSharedRef<SWindow>& Window)
{
// Save the colors back in the config (ptr should have already updated by the widget)
for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
{
const FString& Path = SelectedPaths[PathIdx];
const TSharedPtr<FLinearColor> Color = ContentBrowserUtils::LoadColor( Path );
check( Color.IsValid() );
ContentBrowserUtils::SaveColor( Path, Color );
}
}
FReply FPathContextMenu::OnColorClicked( const FLinearColor InColor )
{
// Make sure an color entry exists for all the paths, otherwise it can't save correctly
for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
{
const FString& Path = SelectedPaths[PathIdx];
TSharedPtr<FLinearColor> Color = ContentBrowserUtils::LoadColor( Path );
if ( !Color.IsValid() )
{
Color = MakeShareable( new FLinearColor() );
}
*Color.Get() = InColor;
ContentBrowserUtils::SaveColor( Path, Color );
}
// Dismiss the menu here, as we can't make the 'clear' option appear if a folder has just had a color set for the first time
FSlateApplication::Get().DismissAllMenus();
return FReply::Handled();
}
void FPathContextMenu::ResetColors()
{
// Clear the custom colors for all the selected paths
for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
{
const FString& Path = SelectedPaths[PathIdx];
ContentBrowserUtils::SaveColor( Path, NULL );
}
}
void FPathContextMenu::ExecuteSaveFolder()
{
// Get a list of package names in the selected paths
TArray<FString> PackageNames;
GetPackageNamesInSelectedPaths(PackageNames);
// Form a list of packages from the assets
TArray<UPackage*> Packages;
for (int32 PackageIdx = 0; PackageIdx < PackageNames.Num(); ++PackageIdx)
{
UPackage* Package = FindPackage(NULL, *PackageNames[PackageIdx]);
// Only save loaded and dirty packages
if ( Package != NULL && Package->IsDirty() )
{
Packages.Add(Package);
}
}
// Save all packages that were found
if ( Packages.Num() )
{
ContentBrowserUtils::SavePackages(Packages);
}
}
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
bool FPathContextMenu::CanExecuteDelete() const
{
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedPaths, NumAssetPaths, NumClassPaths);
// We can't delete folders containing classes
return NumAssetPaths > 0 && NumClassPaths == 0;
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
}
void FPathContextMenu::ExecuteDelete()
{
check(SelectedPaths.Num() > 0);
if (ParentContent.IsValid())
{
FText Prompt;
if ( SelectedPaths.Num() == 1 )
{
Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Single", "Delete folder '{0}'?"), FText::FromString(SelectedPaths[0]));
}
else
{
Prompt = FText::Format(LOCTEXT("FolderDeleteConfirm_Multiple", "Delete {0} folders?"), FText::AsNumber(SelectedPaths.Num()));
}
Folder Rename can now be done in the Content Browser Path View #ttp 342267 - Editor Usability: Select a folder in Content Browser and pressing F2 to rename it, triggers a rename of an actor or asset instead! #branch UE4 SContentBrowser Added functions for Can/Execute Rename/Delete which forward to the active context menu Moved Rename/Delete bind commands to the content browser, so it can forward the request to the correct context menu (this was necessary as it wasn't possible to bind to both context menus without the code complaining about it already being bound) Modified GetFolderContextMenu to take an extra param to indicate whether it was the path view or asset view which called it - it then uses this to clear the asset view selection when the path view context menu is requested - we can then use the selection information to determine which context menu was opened later on when processing rename/delete (ideally it should clear when the path view recieves focus, but that's harder to determine). FAssetContextMenu Made Can/Execute Rename/Delete public - so they can be called by the Content Browser Removed BindCommands as it's no longer needed Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. FPathContextMenu Added delegate for when rename folder is requested - the same as FAssetContextMenu Added handling for whether folder renaming is possible, and executing. Removed condition for adding Rename to the menu, as it now always is and is greyed out if not possible. Fixed issue with Delete not mapping to keyboard shortcut SPathView Added function to rename folder (similar to SAssetView) Fixed issue with VerifyFolderNameChanged not generating the correct path when checking to see if a folder exists (it was previous just checking to old path - which would always return true). Modified FolderNameChanged to take 'OldPath' as a param (inline with the delegate change). This function is called when a folder is created or renamed... if it's the latter it has to handle moving the contents of the folder to the new location, which it could only do if it knew where the previous location was (again, similar to SAssetView) SAssetTreeItem Added extra param to FOnNameChanged (OldPath) - so we can move assets when a folder is renamed FTreeItem Renamed variable bNewFolder to bNamingFolder - as it's true whenever the folder is being named, not just when it's new reviewed by Thomas.Sarkanen [CL 2239648 by Andrew Brown in Main branch]
2014-08-01 05:51:26 -04:00
// Spawn a confirmation dialog since this is potentially a highly destructive operation
FOnClicked OnYesClicked = FOnClicked::CreateSP( this, &FPathContextMenu::ExecuteDeleteFolderConfirmed );
ContentBrowserUtils::DisplayConfirmationPopup(
Prompt,
LOCTEXT("FolderDeleteConfirm_Yes", "Delete"),
LOCTEXT("FolderDeleteConfirm_No", "Cancel"),
ParentContent.Pin().ToSharedRef(),
OnYesClicked);
}
}
void FPathContextMenu::ExecuteReferenceViewer()
{
TArray<FString> PackageNamesAsStrings;
GetPackageNamesInSelectedPaths(PackageNamesAsStrings);
TArray<FName> PackageNames;
for ( auto PackageNameIt = PackageNamesAsStrings.CreateConstIterator(); PackageNameIt; ++PackageNameIt )
{
PackageNames.Add(**PackageNameIt);
}
if ( PackageNames.Num() > 0 )
{
IReferenceViewerModule::Get().InvokeReferenceViewerTab(PackageNames);
}
}
void FPathContextMenu::ExecuteSizeMap()
{
TArray<FString> PackageNamesAsStrings;
GetPackageNamesInSelectedPaths(PackageNamesAsStrings);
TArray<FName> PackageNames;
for ( auto PackageNameIt = PackageNamesAsStrings.CreateConstIterator(); PackageNameIt; ++PackageNameIt )
{
PackageNames.Add(**PackageNameIt);
}
if ( PackageNames.Num() > 0 )
{
ISizeMapModule::Get().InvokeSizeMapTab(PackageNames);
}
}
void FPathContextMenu::ExecuteFixUpRedirectorsInFolder()
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
// Form a filter from the paths
FARFilter Filter;
Filter.bRecursivePaths = true;
for (const auto& Path : SelectedPaths)
{
Filter.PackagePaths.Emplace(*Path);
Filter.ClassNames.Emplace(TEXT("ObjectRedirector"));
}
// Query for a list of assets in the selected paths
TArray<FAssetData> AssetList;
AssetRegistryModule.Get().GetAssets(Filter, AssetList);
if (AssetList.Num() > 0)
{
TArray<FString> ObjectPaths;
for (const auto& Asset : AssetList)
{
ObjectPaths.Add(Asset.ObjectPath.ToString());
}
TArray<UObject*> Objects;
const bool bAllowedToPromptToLoadAssets = true;
const bool bLoadRedirects = true;
if (ContentBrowserUtils::LoadAssetsIfNeeded(ObjectPaths, Objects, bAllowedToPromptToLoadAssets, bLoadRedirects))
{
// Transform Objects array to ObjectRedirectors array
TArray<UObjectRedirector*> Redirectors;
for (auto Object : Objects)
{
auto Redirector = CastChecked<UObjectRedirector>(Object);
Redirectors.Add(Redirector);
}
// Load the asset tools module
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
AssetToolsModule.Get().FixupReferencers(Redirectors);
}
}
}
FReply FPathContextMenu::ExecuteDeleteFolderConfirmed()
{
if ( ContentBrowserUtils::DeleteFolders(SelectedPaths) )
{
ResetColors();
if (OnFolderDeleted.IsBound())
{
OnFolderDeleted.Execute();
}
}
return FReply::Handled();
}
void FPathContextMenu::ExecuteSCCCheckOut()
{
// Get a list of package names in the selected paths
TArray<FString> PackageNames;
GetPackageNamesInSelectedPaths(PackageNames);
TArray<UPackage*> PackagesToCheckOut;
for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
{
if ( FPackageName::DoesPackageExist(*PackageIt) )
{
// Since the file exists, create the package if it isn't loaded or just find the one that is already loaded
// No need to load unloaded packages. It isn't needed for the checkout process
UPackage* Package = CreatePackage(NULL, **PackageIt);
PackagesToCheckOut.Add( CreatePackage(NULL, **PackageIt) );
}
}
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 FPathContextMenu::ExecuteSCCOpenForAdd()
{
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
// Get a list of package names in the selected paths
TArray<FString> PackageNames;
GetPackageNamesInSelectedPaths(PackageNames);
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());
}
}
}
if ( PackagesToAdd.Num() > 0 )
{
SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), SourceControlHelpers::PackageFilenames(PackagesToAdd));
}
}
}
void FPathContextMenu::ExecuteSCCCheckIn()
{
// Get a list of package names in the selected paths
TArray<FString> PackageNames;
GetPackageNamesInSelectedPaths(PackageNames);
// Form a list of loaded packages to prompt for save
TArray<UPackage*> LoadedPackages;
for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
{
UPackage* Package = FindPackage(NULL, **PackageIt);
if ( Package )
{
LoadedPackages.Add(Package);
}
}
// 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( LoadedPackages, 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> PendingDeletePaths;
for (const auto& Path : SelectedPaths)
{
PendingDeletePaths.Add(FPaths::ConvertRelativePathToFull(FPackageName::LongPackageNameToFilename(Path + TEXT("/"))));
}
const bool bUseSourceControlStateCache = false;
FSourceControlWindows::PromptForCheckin(bUseSourceControlStateCache, PackageNames, PendingDeletePaths);
}
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 FPathContextMenu::ExecuteSCCSync() const
{
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
TArray<FString> RootPaths;
FPackageName::QueryRootContentPaths( RootPaths );
// find valid paths on disk
TArray<FString> PathsOnDisk;
for(const auto& SelectedPath : SelectedPaths)
{
// note: we make sure to terminate our path here so root directories map correctly.
const FString CompletePath = SelectedPath / "";
const bool bMatchesRoot = RootPaths.ContainsByPredicate([&](const FString& RootPath){ return CompletePath.StartsWith(RootPath); });
if(bMatchesRoot)
{
FString PathOnDisk = FPackageName::LongPackageNameToFilename(CompletePath);
PathsOnDisk.Add(PathOnDisk);
}
}
if ( PathsOnDisk.Num() > 0 )
{
// attempt to unload all assets under this path
TArray<FString> PackageNames;
GetPackageNamesInSelectedPaths(PackageNames);
// Form a list of loaded packages to prompt for save
TArray<UPackage*> LoadedPackages;
for( const auto& PackageName : PackageNames )
{
UPackage* Package = FindPackage(nullptr, *PackageName);
if ( Package != nullptr )
{
LoadedPackages.Add(Package);
}
}
FText ErrorMessage;
PackageTools::UnloadPackages(LoadedPackages, ErrorMessage);
if(!ErrorMessage.IsEmpty())
{
FMessageDialog::Open( EAppMsgType::Ok, ErrorMessage );
}
else
{
SourceControlProvider.Execute(ISourceControlOperation::Create<FSync>(), PathsOnDisk);
}
}
else
{
UE_LOG(LogContentBrowser, Warning, TEXT("Couldn't find any valid paths to sync."))
}
}
void FPathContextMenu::ExecuteSCCConnect() const
{
ISourceControlModule::Get().ShowLoginDialog(FSourceControlLoginClosed(), ELoginWindowMode::Modeless);
}
bool FPathContextMenu::CanExecuteSCCCheckOut() const
{
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedPaths, NumAssetPaths, NumClassPaths);
// Can only perform SCC operations on asset paths
return bCanExecuteSCCCheckOut && (NumAssetPaths > 0 && NumClassPaths == 0);
}
bool FPathContextMenu::CanExecuteSCCOpenForAdd() const
{
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedPaths, NumAssetPaths, NumClassPaths);
// Can only perform SCC operations on asset paths
return bCanExecuteSCCOpenForAdd && (NumAssetPaths > 0 && NumClassPaths == 0);
}
bool FPathContextMenu::CanExecuteSCCCheckIn() const
{
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedPaths, NumAssetPaths, NumClassPaths);
// Can only perform SCC operations on asset paths
return bCanExecuteSCCCheckIn && (NumAssetPaths > 0 && NumClassPaths == 0);
}
bool FPathContextMenu::CanExecuteSCCSync() const
{
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedPaths, NumAssetPaths, NumClassPaths);
// Can only perform SCC operations on asset paths
return (NumAssetPaths > 0 && NumClassPaths == 0);
}
bool FPathContextMenu::CanExecuteSCCConnect() const
{
int32 NumAssetPaths, NumClassPaths;
ContentBrowserUtils::CountPathTypes(SelectedPaths, NumAssetPaths, NumClassPaths);
// Can only perform SCC operations on asset paths
return (!ISourceControlModule::Get().IsEnabled() || !ISourceControlModule::Get().GetProvider().IsAvailable()) && (NumAssetPaths > 0 && NumClassPaths == 0);
}
void FPathContextMenu::CacheCanExecuteVars()
{
// Cache whether we can execute any of the source control commands
bCanExecuteSCCCheckOut = false;
bCanExecuteSCCOpenForAdd = false;
bCanExecuteSCCCheckIn = false;
ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
if ( SourceControlProvider.IsEnabled() && SourceControlProvider.IsAvailable() )
{
TArray<FString> PackageNames;
GetPackageNamesInSelectedPaths(PackageNames);
// Check the SCC state for each package in the selected paths
for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
{
FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(*PackageIt), EStateCacheUsage::Use);
if(SourceControlState.IsValid())
{
if ( SourceControlState->CanCheckout() )
{
bCanExecuteSCCCheckOut = true;
}
else if ( !SourceControlState->IsSourceControlled() )
{
bCanExecuteSCCOpenForAdd = true;
}
else if ( SourceControlState->CanCheckIn() )
{
bCanExecuteSCCCheckIn = true;
}
}
if ( bCanExecuteSCCCheckOut && bCanExecuteSCCOpenForAdd && bCanExecuteSCCCheckIn )
{
// All SCC options are available, no need to keep iterating
break;
}
}
}
}
void FPathContextMenu::GetPackageNamesInSelectedPaths(TArray<FString>& OutPackageNames) const
{
// Load the asset registry module
FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
// Form a filter from the paths
FARFilter Filter;
Filter.bRecursivePaths = true;
for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
{
const FString& Path = SelectedPaths[PathIdx];
new (Filter.PackagePaths) FName(*Path);
}
// Query for a list of assets in the selected paths
TArray<FAssetData> AssetList;
AssetRegistryModule.Get().GetAssets(Filter, AssetList);
// Form a list of unique package names from the assets
TSet<FName> UniquePackageNames;
for (int32 AssetIdx = 0; AssetIdx < AssetList.Num(); ++AssetIdx)
{
UniquePackageNames.Add(AssetList[AssetIdx].PackageName);
}
// Add all unique package names to the output
for ( auto PackageIt = UniquePackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
{
OutPackageNames.Add( (*PackageIt).ToString() );
}
}
FString FPathContextMenu::GetFirstSelectedPath() const
{
return SelectedPaths.Num() > 0 ? SelectedPaths[0] : TEXT("");
}
bool FPathContextMenu::SelectedHasCustomColors() const
{
for (int32 PathIdx = 0; PathIdx < SelectedPaths.Num(); ++PathIdx)
{
// Ignore any that are the default color
const FString& Path = SelectedPaths[PathIdx];
const TSharedPtr<FLinearColor> Color = ContentBrowserUtils::LoadColor( Path );
if ( Color.IsValid() && !Color->Equals( ContentBrowserUtils::GetDefaultColor() ) )
{
return true;
}
}
return false;
}
#undef LOCTEXT_NAMESPACE