Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/EdGraphSchema_K2.cpp
Matt Kuhlenschmidt 4fa92bfa50 Copying //UE4/Dev-Editor to //UE4/Dev-Main (Source: //UE4/Dev-Editor @ 3152045)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================

Change 3106449 on 2016/08/30 by Michael.Dupuis

	#jira UETOOL-229 Added generic command icons used in Edit Menu (including contextual menu)

Change 3133997 on 2016/09/21 by Alex.Delesky

	#jira UE-34079 - FSceneView parameter for FBatchedElements::Draw is no longer an optional parameter.

Change 3134132 on 2016/09/21 by Jamie.Dale

	Added the "unattended" flag when running the localzation commandlets via UAT

Change 3134147 on 2016/09/21 by Frank.Fella

	Core - Add multitouch support to windows.

Change 3134349 on 2016/09/21 by Michael.Dupuis

	#jira UE-36151 Update the title bar to display the branch also

Change 3134355 on 2016/09/21 by Michael.Dupuis

	#jira UE-36041 When initially creating a projet and trying to add everything to source control, also add the uproject file

Change 3134447 on 2016/09/21 by Alexis.Matte

	#jira UE-36064
	The SpriteComponent is now properly reused when duplicating a light.

Change 3134451 on 2016/09/21 by Alexis.Matte

	#jira UE-22782
	Make sure when we re-import we dont try to match the mesh name if the option bCombineToSingle is true

Change 3134457 on 2016/09/21 by Jamie.Dale

	FInternationalization::Leetify now deals with escape sequences correctly.

	This also changes it to mark all leetified text with the start and end marker, and the unit tests have been updated accordingly.

Change 3134685 on 2016/09/21 by Matt.Kuhlenschmidt

	Cleaned up some of the UI for the new material slot workflow (part 1)

Change 3134743 on 2016/09/21 by Matt.Kuhlenschmidt

	Fix crash clicking OK to the import mesh dialog in persona when a path is not selected.  The OK button is now disabled until a valid path is selected

Change 3134760 on 2016/09/21 by Matt.Kuhlenschmidt

	Guard against cascade crash if cascade was shutdown but there is an old cascade particle system component in the undo buffer that attempts to refresh cascade

Change 3134837 on 2016/09/21 by Matt.Kuhlenschmidt

	Fix static analysis warnings in this file

Change 3134939 on 2016/09/21 by Nick.Darnell

	Automation - Moving the functional testing hooks for the editor into a plugin, the goal is to make it so you don't have to see all the extra stuff automation adds to the editor until the game project needs it.  Refactoring the way automation messages are serialized, no longer relying on a comma delimited string to serialize automation data.  Improving the filter for automation window so it lets you put in a path, and find it that way.  Fixing RunTests from the commandline so that it properly finds the tests, instead of jumping out of the loop as soon as it finds any one match.

Change 3134941 on 2016/09/21 by Nick.Darnell

	Automation - Functional tests now support warnings as errors on a per test basis.  Continued itteration on screenshot comparison.

Change 3135051 on 2016/09/21 by Matt.Kuhlenschmidt

	Fixed size of material slots in persona details panel

Change 3135076 on 2016/09/21 by Nick.Darnell

	Automation - Adding a missing file.

Change 3135584 on 2016/09/22 by Gareth.Martin

	Removed unused ULandscapeInfo::BeginDestroy
	Slight cleanup to FLandscapeEditDataInterface::DeleteLayer

Change 3135585 on 2016/09/22 by Gareth.Martin

	New Count algo
	Const'd Copy/Accumulate/Transform

Change 3135599 on 2016/09/22 by Gareth.Martin

	Resolve crash if a Landscape Layer Info object is force deleted while it is in use :(
	#jira UE-35709

Change 3135724 on 2016/09/22 by Michael.Dupuis

	#jira UE-32662 Remove old migration code that was causing this side effect

Change 3135726 on 2016/09/22 by Nick.Darnell

	Slate - Removing the SLATE_PRE_MULTIPLY macro.

Change 3135730 on 2016/09/22 by Nick.Darnell

	UMG - Exposing the 2D hit location that the WidgetInteractionComponent hit on the WidgetComponent.

Change 3135738 on 2016/09/22 by Matt.Kuhlenschmidt

	Ensure any handles to the backbuffer are released before the backbuffer is resized

	https://jira.it.epicgames.net/browse/UE-30488

Change 3135810 on 2016/09/22 by Ben.Marsh

	Build: Compile tools before running automated tests.

Change 3135993 on 2016/09/22 by Matt.Kuhlenschmidt

	Ensure you can unpause after toggling play/pause with a keyboard shortcut.  We were processing the keybinding on key down AND up which caused the unpause to instantly pause again
	https://jira.it.epicgames.net/browse/UE-36276

Change 3136257 on 2016/09/22 by Matt.Kuhlenschmidt

	Fixed assign of materials to components not working if you multi-select multiple components of a blueprint and assign the material.  The construction script was running before all the material had been set on all components.

Change 3136318 on 2016/09/22 by Alex.Delesky

	#jira UE-7405 - Forcing the mouse cursor to show and then clicking inside the viewport will no longer cause the mouse to lose focus

Change 3136494 on 2016/09/22 by Matt.Kuhlenschmidt

	Fix crash restarting a mission in Odin

Change 3136741 on 2016/09/22 by Cody.Albert

	Fixed SButton and SWidget to not multiply color and opacity, since that's already being done by SCompoundWidget

	#jira UE-36322

Change 3137711 on 2016/09/23 by Matt.Kuhlenschmidt

	Added guard against slate rendering with deleted materials.  In this will now be caught with a looged with the deleted material name.

Change 3137713 on 2016/09/23 by Matt.Kuhlenschmidt

	Removed the pooled draw elements stuff which is not used

Change 3137791 on 2016/09/23 by Nick.Darnell

	MediaPlayer - Removing .png from one of the style files, as that's not required.  Was causing warnings to be logged on load.

Change 3137793 on 2016/09/23 by Nick.Darnell

	Localization - Adjusting some log statements to say with instead of w/

Change 3137796 on 2016/09/23 by Nick.Darnell

	Slate - Adding missing and replacing corrupted style files in the editor.

Change 3137864 on 2016/09/23 by Matt.Kuhlenschmidt

	Fixed "actors are referenced are you sure you want to delete" dialog appearing when you are copying a lot of actors to another level

Change 3137876 on 2016/09/23 by Jamie.Dale

	Added allocation tagging to MProf2

	This hooks into the existing FScopeCycleCounterUObject used by the stats system to track object tags (object, package, and class) when an allocation is made. Tags should be in the format "Category:Tag", and the "Tag" part may include "/" to create levels in the tag hierarchy (as shown in the MProf2 tool).

	#jira UETOOL-950

Change 3137982 on 2016/09/23 by Gareth.Martin

	Added Invoke() support to CopyIf

Change 3137983 on 2016/09/23 by Gareth.Martin

	Added ULandscapeInfo::ForAllLandscapeProxies to clean up a lot of horrible code
	- Removed some dependencies on ALandscape and ALandscapeStreamingProxy in the process :)
	Also made FLandscapeEditorLayerSettings's constructor explicit to clean up some other horrible code

Change 3138053 on 2016/09/23 by Matt.Kuhlenschmidt

	Fixed Child Actor Template properties not visible after changing Child Actor Class

Change 3138079 on 2016/09/23 by Jamie.Dale

	Fixing some Clang warnings

Change 3138087 on 2016/09/23 by Jamie.Dale

	Added GetResourceSizeEx and GetResourceSizeBytes, and deprecated GetResourceSize

	GetResourceSizeEx populates a struct which reports not only how much memory is allocated, but also which arenas the memory was allocated from. GetResourceSizeBytes just wraps a call to GetResourceSizeEx and returns the total size from all arenas (this has the same behavior as the now deprecated GetResourceSize).

	Classes that used to override GetResourceSize should instead override GetResourceSizeEx and report their allocations as appropriate.

	#jira UETOOL-952

Change 3138127 on 2016/09/23 by Gareth.Martin

	Fixed crash when merging levels containing landscape
	#jira UE-36267

Change 3138821 on 2016/09/23 by Stephan.Jiang

	Fixes "Select all input node" doesn't work properly on output node.

	#jira UE-36335

Change 3138915 on 2016/09/23 by Stephan.Jiang

	Disable "select all linked nodes" for output nodes in material editor

Change 3139341 on 2016/09/25 by Nick.Darnell

	Automation - Moving the Blueprint Compiler Tests into the RuntimeTests plugin, and making the context client only since that's the only valid place to runt these tests.

Change 3139342 on 2016/09/25 by Nick.Darnell

	Landscape - Fixing some compiler errors on mac.

Change 3139345 on 2016/09/25 by Nick.Darnell

	Automation - Spelling and cleanup.

Change 3139346 on 2016/09/25 by Nick.Darnell

	Engine - Changing a check to an ensure, there's no reason to crash if this happens.

Change 3139347 on 2016/09/25 by Nick.Darnell

	Automation  - Making EFunctionalTestResult a BlueprintType

Change 3139348 on 2016/09/25 by Nick.Darnell

	Automation - Adding another test map.

Change 3139676 on 2016/09/26 by Michael.Dupuis

	#jira UE-32335 If we are a config object simply permit the transaction for undo/redo

Change 3139702 on 2016/09/26 by Nick.Darnell

	UMG - Making GetLocalHitLocation on UWidgetComponent virtual.

Change 3139760 on 2016/09/26 by Alexis.Matte

	Make sure we remove override materials from the list when the mesh point by the component has less materials.
	#jira UE-28845

Change 3139761 on 2016/09/26 by Alex.Delesky

	Added additional validation code to FPropertyNode to now properly validate TMap value and key nodes.

	#jira none

Change 3139843 on 2016/09/26 by Alex.Delesky

	#jira UE-36066 - Clearing all options from a ComboBox String when selecting an option will now also clear out the selected text correctly.

Change 3139880 on 2016/09/26 by Frank.Fella

	QAGame - Update multitouch test with less crashy assets?

Change 3139908 on 2016/09/26 by Matt.Kuhlenschmidt

	Fix selections having the potential to be out of sync after undo/redo

Change 3139928 on 2016/09/26 by Nick.Darnell

	Automation - tweaking the test maps some more.

Change 3140646 on 2016/09/26 by Matt.Kuhlenschmidt

	Fix false positive with the test for vaild materials being rendered by slate.

Change 3140912 on 2016/09/26 by Frank.Fella

	Core - Fix multitouch ifdef which was preventing it from actually being enabled.Also update test level blueprint so that it's actually testable.

Change 3141218 on 2016/09/27 by Matt.Kuhlenschmidt

	PR #2798: BP open anim interferes with mouse movement (Contributed by projectgheist)

Change 3141223 on 2016/09/27 by Jamie.Dale

	Updated UTextProperty::Identical to no longer compare display strings all the time

	It was supposed to compare the identity at runtime as the display string can change at runtime. This was preventing FText properties from being used in TSet/TMap as the hash needs to be consistent.

	#jira UE-36456

Change 3141242 on 2016/09/27 by Richard.TalbotWatkin

	Fixed various issues where making changes to components in the Blueprint Editor could cause a crash if there is an active component in the component visualizer.
	#jira UE-36402 - Editor crash when adding a spline component after having deleted another spline component
	Duplicated CL 3139370 from //UE4/Release-4.13
	Duplicated CL 3139878 from //UE4/Release-4.13

Change 3141323 on 2016/09/27 by Michael.Dupuis

	#jira UE-35081 Enable bDisplayEngineVersionInBadge by default

Change 3141798 on 2016/09/27 by tim.gautier

	Added UMG_DisplayWidget

Change 3143038 on 2016/09/28 by Jamie.Dale

	Added extra context to FTableRowBase::OnPostDataImport

	It now takes the owning data table and the row name as parameters. This allows is to do more useful fix-up that depends on the context of where it's used (such as stabilized text keys).

Change 3143039 on 2016/09/28 by Jamie.Dale

	Optimized UTextProperty::Identical to use a pointer comparison rather than read out the identity

	Only texts which have the same display string pointer can have the same identity, so this is a much faster check.

Change 3143098 on 2016/09/28 by Gareth.Martin

	Fixed crash when loading duplicated landscape levels
	#jira UE-34890

Change 3143300 on 2016/09/28 by Gareth.Martin

	Fixed crash when duplicating a level containing a landscape through the content browser
	#jira UE-34890

Change 3143389 on 2016/09/28 by Jamie.Dale

	LastResort is no longer staged in shipping builds

	You can now use the Content/SlateDebug folder to store any Slate resources that shouldn't be used in a shipping build (either for the Engine or for a game).

	This also removes the old bUsesSlateEditorStyle hack as everything should have migrated away from that now.

Change 3143565 on 2016/09/28 by Matt.Kuhlenschmidt

	Fixed this file

Change 3143717 on 2016/09/28 by Michael.Dupuis

	Fixed lowercase

Change 3143798 on 2016/09/28 by Matt.Kuhlenschmidt

	Fixed StreamingPauseRendering code to not attempt to tick the scene viewport on the slate loading thread while where are blocked on level streaming.  The viewport is rendered once and the render target is passed to slate instead of the entire viewport

Change 3143820 on 2016/09/28 by Alexis.Matte

	Use the PersonaToolikit to get the mesh

Change 3143833 on 2016/09/28 by Matt.Kuhlenschmidt

	Added guard for UE-36499

Change 3144144 on 2016/09/28 by Matt.Kuhlenschmidt

	Fix constructor init order

Change 3144821 on 2016/09/29 by Jamie.Dale

	Fixed feedback loop in FSlateEditableTextLayout::ComputeDesiredSize

	If the wrapping width is less than the scrollbar width, the scrollbar could constantly re-appear and then disappear.

Change 3144867 on 2016/09/29 by Matt.Kuhlenschmidt

	Added a commandlet to allow command line importing of any asset type the engine supports.
	- Intermediate checkin

Change 3144875 on 2016/09/29 by Nick.Darnell

	Automation - Adding CornellBox example map, fixing some additional tests, removing the Movement Test.

Change 3144975 on 2016/09/29 by Matt.Kuhlenschmidt

	Disable auto-applying of scalability settings
	The user will still be asked if the settings should be applied but if they do nothing the notification will just go away without setting stuff.

Change 3145274 on 2016/09/29 by Jamie.Dale

	New asset menu no longer has a scrollbar on most displays

Change 3146004 on 2016/09/29 by Matt.Barnes

	Adding two FBX test files to help facilitate QA coverage on the new material/section workflow.

Change 3146377 on 2016/09/30 by Gareth.Martin

	Fixed landscape rendering errors after using the "change component size" tool
	- also affected initial import of a landscape+weightmaps
	#jira UE-34518

Change 3146455 on 2016/09/30 by Jamie.Dale

	Fixing more menus that had scrollbars due to only being able to use half the vertical resolution

Change 3146466 on 2016/09/30 by Gareth.Martin

	Fixed not being able to erase foliage attached to BSP
	#jira UE-36297

Change 3146471 on 2016/09/30 by Jamie.Dale

	Can no longer localize sequences via the Content Browser

Change 3146569 on 2016/09/30 by Jamie.Dale

	Fixed UGatherTextFromSourceCommandlet::ParseSourceText being able to underflow while parsing

Change 3147116 on 2016/09/30 by Michael.Dupuis

	#jira UE-33068 Update selection once after the bulk operation so the AssetContextMenu will have a proper selection, but only perform this if we're not in UserSearchingMode (which mean AssetPicker)

Change 3148091 on 2016/10/01 by Matt.Barnes

	Adding a map and relevant assets to facilitate testing around the new material blueprint nodes - GetMaterialSlotNames, GetMaterialIndex, and SetMaterialByName, respectively

Change 3148714 on 2016/10/03 by Nick.Darnell

	PR #2770: [Git plugin] Fix bug where history and merging do not work if the user has format.pretty settings in their gitconfig (Contributed by SRombauts)

	#jira UE-35568

Change 3148793 on 2016/10/03 by Nick.Darnell

	Automation - TPS for SM_Cornellbox

Change 3148801 on 2016/10/03 by Nick.Darnell

	PR #2820: [WidgetBlueprintLibrary] Throwing Essential-Functionality-Understanding PIE Errors when Player Controller ptr not supplied (Contributed by EverNewJoy)

	#jira UE-36711

Change 3148805 on 2016/10/03 by Nick.Darnell

	PR #2822: Add missing base includes and forward declarations for UProgressBar and UTextBlock (Contributed by error454)

	#jira UE-36715

Change 3148813 on 2016/10/03 by Nick.Darnell

	UMG - The retainer now contains the SVirtualWindow directly in the hierarchy.  This should now make it possible to reliably focus elements inside the retainer widget.

Change 3148855 on 2016/10/03 by Gareth.Martin

	Fixed performance regression when importing landscape heightmaps
	#jira UE-36659

Change 3149482 on 2016/10/03 by Cody.Albert

	Added link to Support landing page to Help menu

	#jira UE-36603

Change 3149520 on 2016/10/03 by tim.gautier

	Edited UMG_Behavior - Collapsed now has a toggleable state to demonstrate functionality.

Change 3149945 on 2016/10/04 by Gareth.Martin

	Fixed invisible landscape components when using tessellation on landscape material
	#jira UE-35494

Change 3149951 on 2016/10/04 by Gareth.Martin

	Reduced material update log spam when creating and editing landscapes
	- Some will remain, landscape fundementally manipulates a lot of material instances
	#jira UE-34440

Change 3150143 on 2016/10/04 by Matt.Kuhlenschmidt

	Fix not crashing when trying to clear objects which have a specific class filter in the property settings
	https://jira.it.epicgames.net/browse/UE-36692

Change 3150614 on 2016/10/04 by Nick.Darnell

	Git - Disabling some logging in non-debug builds of the git source control plugin.

Change 3151647 on 2016/10/05 by Matt.Kuhlenschmidt

	Fix loc warnings in these files (duplicate loc keys)

Change 3151679 on 2016/10/05 by Nick.Darnell

	Editor - Fixing the build, removing the VREditor module from the dynamically loaded list.

Change 3151722 on 2016/10/05 by Gareth.Martin

	Fix breakage to LandscapeEdModeSplineTools.cpp caused by Dev-VREditor from main integration

Change 3151816 on 2016/10/05 by Gareth.Martin

	Fixed more breakage to Landscape caused by Dev-VREditor from main integration

[CL 3152072 by Matt Kuhlenschmidt in Main branch]
2016-10-05 13:23:01 -04:00

6629 lines
236 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "UObject/DevObjectVersion.h"
#include "Engine/LevelScriptBlueprint.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Kismet/KismetArrayLibrary.h"
#include "Kismet/KismetMathLibrary.h"
#include "GraphEditorActions.h"
#include "GraphEditorSettings.h"
#include "ScopedTransaction.h"
#include "ComponentAssetBroker.h"
#include "BlueprintEditorSettings.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Kismet2/KismetDebugUtilities.h"
#include "KismetCompiler.h"
#include "ComponentAssetBroker.h"
#include "AssetData.h"
#include "Editor/UnrealEd/Public/EdGraphUtilities.h"
#include "DefaultValueHelper.h"
#include "ObjectEditorUtils.h"
#include "ActorEditorUtils.h"
#include "TextPackageNamespaceUtil.h"
#include "ComponentTypeRegistry.h"
#include "BlueprintComponentNodeSpawner.h"
#include "AssetRegistryModule.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "HotReloadInterface.h"
#include "K2Node_CastByteToEnum.h"
#include "K2Node_ClassDynamicCast.h"
#include "K2Node_GetEnumeratorName.h"
#include "K2Node_GetEnumeratorNameAsString.h"
#include "K2Node_Tunnel.h"
#include "K2Node_SetFieldsInStruct.h"
#include "K2Node_ConvertAsset.h"
#include "GenericCommands.h"
//////////////////////////////////////////////////////////////////////////
// FBlueprintMetadata
const FName FBlueprintMetadata::MD_AllowableBlueprintVariableType(TEXT("BlueprintType"));
const FName FBlueprintMetadata::MD_NotAllowableBlueprintVariableType(TEXT("NotBlueprintType"));
const FName FBlueprintMetadata::MD_BlueprintSpawnableComponent(TEXT("BlueprintSpawnableComponent"));
const FName FBlueprintMetadata::MD_IsBlueprintBase(TEXT("IsBlueprintBase"));
const FName FBlueprintMetadata::MD_RestrictedToClasses(TEXT("RestrictedToClasses"));
const FName FBlueprintMetadata::MD_ChildCanTick(TEXT("ChildCanTick"));
const FName FBlueprintMetadata::MD_ChildCannotTick(TEXT("ChildCannotTick"));
const FName FBlueprintMetadata::MD_IgnoreCategoryKeywordsInSubclasses(TEXT("IgnoreCategoryKeywordsInSubclasses"));
const FName FBlueprintMetadata::MD_Protected(TEXT("BlueprintProtected"));
const FName FBlueprintMetadata::MD_Latent(TEXT("Latent"));
const FName FBlueprintMetadata::MD_UnsafeForConstructionScripts(TEXT("UnsafeDuringActorConstruction"));
const FName FBlueprintMetadata::MD_FunctionCategory(TEXT("Category"));
const FName FBlueprintMetadata::MD_DeprecatedFunction(TEXT("DeprecatedFunction"));
const FName FBlueprintMetadata::MD_DeprecationMessage(TEXT("DeprecationMessage"));
const FName FBlueprintMetadata::MD_CompactNodeTitle(TEXT("CompactNodeTitle"));
const FName FBlueprintMetadata::MD_DisplayName(TEXT("DisplayName"));
const FName FBlueprintMetadata::MD_InternalUseParam(TEXT("InternalUseParam"));
const FName FBlueprintMetadata::MD_ExposeOnSpawn(TEXT("ExposeOnSpawn"));
const FName FBlueprintMetadata::MD_HideSelfPin(TEXT("HideSelfPin"));
const FName FBlueprintMetadata::MD_DefaultToSelf(TEXT("DefaultToSelf"));
const FName FBlueprintMetadata::MD_WorldContext(TEXT("WorldContext"));
const FName FBlueprintMetadata::MD_CallableWithoutWorldContext(TEXT("CallableWithoutWorldContext"));
const FName FBlueprintMetadata::MD_DevelopmentOnly(TEXT("DevelopmentOnly"));
const FName FBlueprintMetadata::MD_AutoCreateRefTerm(TEXT("AutoCreateRefTerm"));
const FName FBlueprintMetadata::MD_ShowWorldContextPin(TEXT("ShowWorldContextPin"));
const FName FBlueprintMetadata::MD_Private(TEXT("BlueprintPrivate"));
const FName FBlueprintMetadata::MD_BlueprintInternalUseOnly(TEXT("BlueprintInternalUseOnly"));
const FName FBlueprintMetadata::MD_NeedsLatentFixup(TEXT("NeedsLatentFixup"));
const FName FBlueprintMetadata::MD_LatentCallbackTarget(TEXT("LatentCallbackTarget"));
const FName FBlueprintMetadata::MD_AllowPrivateAccess(TEXT("AllowPrivateAccess"));
const FName FBlueprintMetadata::MD_ExposeFunctionCategories(TEXT("ExposeFunctionCategories"));
const FName FBlueprintMetadata::MD_CannotImplementInterfaceInBlueprint(TEXT("CannotImplementInterfaceInBlueprint"));
const FName FBlueprintMetadata::MD_ProhibitedInterfaces(TEXT("ProhibitedInterfaces"));
const FName FBlueprintMetadata::MD_FunctionKeywords(TEXT("Keywords"));
const FName FBlueprintMetadata::MD_ExpandEnumAsExecs(TEXT("ExpandEnumAsExecs"));
const FName FBlueprintMetadata::MD_CommutativeAssociativeBinaryOperator(TEXT("CommutativeAssociativeBinaryOperator"));
const FName FBlueprintMetadata::MD_MaterialParameterCollectionFunction(TEXT("MaterialParameterCollectionFunction"));
const FName FBlueprintMetadata::MD_Tooltip(TEXT("Tooltip"));
const FName FBlueprintMetadata::MD_CallInEditor(TEXT("CallInEditor"));
const FName FBlueprintMetadata::MD_DataTablePin(TEXT("DataTablePin"));
const FName FBlueprintMetadata::MD_NativeMakeFunction(TEXT("HasNativeMake"));
const FName FBlueprintMetadata::MD_NativeBreakFunction(TEXT("HasNativeBreak"));
const FName FBlueprintMetadata::MD_DynamicOutputType(TEXT("DeterminesOutputType"));
const FName FBlueprintMetadata::MD_DynamicOutputParam(TEXT("DynamicOutputParam"));
const FName FBlueprintMetadata::MD_ArrayParam(TEXT("ArrayParm"));
const FName FBlueprintMetadata::MD_ArrayDependentParam(TEXT("ArrayTypeDependentParams"));
const FName FBlueprintMetadata::MD_SetParam(TEXT("SetParam"));
const FName FBlueprintMetadata::MD_Bitmask(TEXT("Bitmask"));
const FName FBlueprintMetadata::MD_BitmaskEnum(TEXT("BitmaskEnum"));
const FName FBlueprintMetadata::MD_Bitflags(TEXT("Bitflags"));
//////////////////////////////////////////////////////////////////////////
#define LOCTEXT_NAMESPACE "KismetSchema"
UEdGraphSchema_K2::FPinTypeTreeInfo::FPinTypeTreeInfo(const FText& InFriendlyName, const FString& CategoryName, const UEdGraphSchema_K2* Schema, const FText& InTooltip, bool bInReadOnly/*=false*/, FTypesDatabase* TypesDatabase /*=nullptr*/)
: PossibleObjectReferenceTypes(0)
{
Init(InFriendlyName, CategoryName, Schema, InTooltip, bInReadOnly, TypesDatabase);
}
struct FUnloadedAssetData
{
FStringAssetReference StringAssetReference;
FText AssetFriendlyName;
FText Tooltip;
uint8 PossibleObjectReferenceTypes;
FUnloadedAssetData()
: PossibleObjectReferenceTypes(0)
{}
FUnloadedAssetData(const FAssetData& InAsset, uint8 InPossibleObjectReferenceTypes = 0)
: StringAssetReference(InAsset.ToStringReference())
, AssetFriendlyName(FText::FromString(FName::NameToDisplayString(InAsset.AssetName.ToString(), false)))
, PossibleObjectReferenceTypes(InPossibleObjectReferenceTypes)
{
InAsset.GetTagValue("Tooltip", Tooltip);
if (Tooltip.IsEmpty())
{
Tooltip = FText::FromString(InAsset.ObjectPath.ToString());
}
}
};
struct FLoadedAssetData
{
FText Tooltip;
UObject* Object;
uint8 PossibleObjectReferenceTypes;
FLoadedAssetData()
: Object(nullptr)
, PossibleObjectReferenceTypes(0) {}
FLoadedAssetData(UObject* InObject, uint8 InPossibleObjectReferenceTypes = 0)
: Object(InObject)
, PossibleObjectReferenceTypes(InPossibleObjectReferenceTypes)
{
UStruct* Struct = Cast<UStruct>(Object);
Tooltip = Struct ? Struct->GetToolTipText() : FText::GetEmpty();
}
};
struct FTypesDatabase
{
typedef TSharedPtr<TArray<FLoadedAssetData>> FLoadedTypesList;
TMap<FString, FLoadedTypesList> LoadedTypesMap;
typedef TSharedPtr<TArray<FUnloadedAssetData>> FUnLoadedTypesList;
TMap<FString, FUnLoadedTypesList> UnLoadedTypesMap;
};
/** Helper class to gather variable types */
class FGatherTypesHelper
{
private:
typedef TSharedPtr<UEdGraphSchema_K2::FPinTypeTreeInfo> FPinTypeTreeInfoPtr;
struct FCompareChildren
{
FORCEINLINE bool operator()(const FPinTypeTreeInfoPtr A, const FPinTypeTreeInfoPtr B) const
{
return (A->GetDescription().ToString() < B->GetDescription().ToString());
}
};
public:
static void FillLoadedTypesDatabase(FTypesDatabase& TypesDatabase, bool bIndexTypesOnly)
{
// Loaded types
TypesDatabase.LoadedTypesMap.Reset();
//(Type == UEdGraphSchema_K2::PC_Enum)
{
FTypesDatabase::FLoadedTypesList LoadedTypesList = MakeShareable(new TArray<FLoadedAssetData>());
// Generate a list of all potential enums which have "BlueprintType=true" in their metadata
for (TObjectIterator<UEnum> EnumIt; EnumIt; ++EnumIt)
{
UEnum* CurrentEnum = *EnumIt;
if (UEdGraphSchema_K2::IsAllowableBlueprintVariableType(CurrentEnum))
{
LoadedTypesList->Add(FLoadedAssetData(CurrentEnum));
}
}
TypesDatabase.LoadedTypesMap.Add(UEdGraphSchema_K2::PC_Enum, LoadedTypesList);
}
if (!bIndexTypesOnly)
{
//(Type == UEdGraphSchema_K2::PC_Struct)
{
FTypesDatabase::FLoadedTypesList LoadedTypesList = MakeShareable(new TArray<FLoadedAssetData>());
// Find script structs marked with "BlueprintType=true" in their metadata, and add to the list
for (TObjectIterator<UScriptStruct> StructIt; StructIt; ++StructIt)
{
UScriptStruct* ScriptStruct = *StructIt;
if (UEdGraphSchema_K2::IsAllowableBlueprintVariableType(ScriptStruct))
{
LoadedTypesList->Add(FLoadedAssetData(ScriptStruct));
}
}
TypesDatabase.LoadedTypesMap.Add(UEdGraphSchema_K2::PC_Struct, LoadedTypesList);
}
//(Type == UEdGraphSchema_K2::PC_Class || Type == UEdGraphSchema_K2::PC_AssetClass) UEdGraphSchema_K2::PC_Interface)
//(Type == UEdGraphSchema_K2::PC_Object || Type == UEdGraphSchema_K2::PC_Asset)
{
FTypesDatabase::FLoadedTypesList InterfaceLoadedTypesList = MakeShareable(new TArray<FLoadedAssetData>());
FTypesDatabase::FLoadedTypesList AllObjectLoadedTypesList = MakeShareable(new TArray<FLoadedAssetData>());
// Generate a list of all potential objects which have "BlueprintType=true" in their metadata
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
{
UClass* CurrentClass = *ClassIt;
const bool bIsInterface = CurrentClass->IsChildOf(UInterface::StaticClass());
const bool bIsBlueprintType = UEdGraphSchema_K2::IsAllowableBlueprintVariableType(CurrentClass);
const bool bIsDeprecated = CurrentClass->HasAnyClassFlags(CLASS_Deprecated);
if (bIsBlueprintType && !bIsDeprecated)
{
if (bIsInterface)
{
InterfaceLoadedTypesList->Add(FLoadedAssetData(CurrentClass));
}
else
{
AllObjectLoadedTypesList->Add(FLoadedAssetData(CurrentClass, static_cast<uint8>(EObjectReferenceType::AllTypes)));
}
}
}
TypesDatabase.LoadedTypesMap.Add(UEdGraphSchema_K2::AllObjectTypes, AllObjectLoadedTypesList);
TypesDatabase.LoadedTypesMap.Add(UEdGraphSchema_K2::PC_Interface, InterfaceLoadedTypesList);
}
}
}
static void FillUnLoadedTypesDatabase(FTypesDatabase& TypesDatabase, bool bIndexTypesOnly)
{
// Loaded types
TypesDatabase.UnLoadedTypesMap.Reset();
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
{
TArray<FAssetData> AssetData;
AssetRegistryModule.Get().GetAssetsByClass(UUserDefinedEnum::StaticClass()->GetFName(), AssetData);
FTypesDatabase::FUnLoadedTypesList UnLoadedTypesList = MakeShareable(new TArray<FUnloadedAssetData>());
for (int32 AssetIndex = 0; AssetIndex < AssetData.Num(); ++AssetIndex)
{
const FAssetData& Asset = AssetData[AssetIndex];
if (Asset.IsValid() && !Asset.IsAssetLoaded())
{
UnLoadedTypesList->Add(FUnloadedAssetData(Asset));
}
}
TypesDatabase.UnLoadedTypesMap.Add(UEdGraphSchema_K2::PC_Enum, UnLoadedTypesList);
}
if (!bIndexTypesOnly)
{
{
TArray<FAssetData> AssetData;
AssetRegistryModule.Get().GetAssetsByClass(UUserDefinedStruct::StaticClass()->GetFName(), AssetData);
FTypesDatabase::FUnLoadedTypesList UnLoadedTypesList = MakeShareable(new TArray<FUnloadedAssetData>());
for (int32 AssetIndex = 0; AssetIndex < AssetData.Num(); ++AssetIndex)
{
const FAssetData& Asset = AssetData[AssetIndex];
if (Asset.IsValid() && !Asset.IsAssetLoaded())
{
UnLoadedTypesList->Add(FUnloadedAssetData(Asset));
}
}
TypesDatabase.UnLoadedTypesMap.Add(UEdGraphSchema_K2::PC_Struct, UnLoadedTypesList);
}
//else if (Schema->PC_Object == CategoryName || Schema->PC_Class == CategoryName || Schema->PC_Interface == CategoryName || Schema->PC_Asset == CategoryName || Schema->PC_AssetClass == CategoryName)
{
TArray<FAssetData> AssetData;
AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetFName(), AssetData);
const FString BPInterfaceTypeAllowed(TEXT("BPTYPE_Interface"));
const FString BPNormalTypeAllowed(TEXT("BPTYPE_Normal"));
FTypesDatabase::FUnLoadedTypesList UnLoadedInterfacesList = MakeShareable(new TArray<FUnloadedAssetData>());
FTypesDatabase::FUnLoadedTypesList UnLoadedClassesList = MakeShareable(new TArray<FUnloadedAssetData>());
for (int32 AssetIndex = 0; AssetIndex < AssetData.Num(); ++AssetIndex)
{
const FAssetData& Asset = AssetData[AssetIndex];
if (Asset.IsValid() && !Asset.IsAssetLoaded())
{
const FString BlueprintTypeStr = Asset.GetTagValueRef<FString>("BlueprintType");
const bool bNormalBP = BlueprintTypeStr == BPNormalTypeAllowed;
const bool bInterfaceBP = BlueprintTypeStr == BPInterfaceTypeAllowed;
if (bNormalBP || bInterfaceBP)
{
const uint32 ClassFlags = Asset.GetTagValueRef<uint32>("ClassFlags");
if (!(ClassFlags & CLASS_Deprecated))
{
if (bNormalBP)
{
UnLoadedClassesList->Add(FUnloadedAssetData(Asset, static_cast<uint8>(EObjectReferenceType::AllTypes)));
}
else if (bInterfaceBP)
{
UnLoadedInterfacesList->Add(FUnloadedAssetData(Asset));
}
}
}
}
}
TypesDatabase.UnLoadedTypesMap.Add(UEdGraphSchema_K2::PC_Interface, UnLoadedInterfacesList);
TypesDatabase.UnLoadedTypesMap.Add(UEdGraphSchema_K2::AllObjectTypes, UnLoadedClassesList);
}
}
}
/**
* Gathers all valid sub-types (loaded and unloaded) of a passed category and sorts them alphabetically
* @param FriendlyName Friendly name to be used for the tooltip if there is no available data
* @param CategoryName Category (type) to find sub-types of
* @param TypesDatabase Types database
* @param OutChildren All the gathered children
*/
static void Gather(const FText& FriendlyName, const FString& CategoryName, FTypesDatabase& TypesDatabase, TArray<FPinTypeTreeInfoPtr>& OutChildren)
{
FEdGraphPinType LoadedPinSubtype;
LoadedPinSubtype.PinCategory = (CategoryName == UEdGraphSchema_K2::PC_Enum ? UEdGraphSchema_K2::PC_Byte : CategoryName);
LoadedPinSubtype.PinSubCategory = TEXT("");
LoadedPinSubtype.PinSubCategoryObject = NULL;
auto LoadedSubTypesPtr = TypesDatabase.LoadedTypesMap.Find(CategoryName);
if (LoadedSubTypesPtr && LoadedSubTypesPtr->IsValid())
{
for (auto LoadedAssetData : *LoadedSubTypesPtr->Get())
{
OutChildren.Add(MakeShareable(new UEdGraphSchema_K2::FPinTypeTreeInfo(LoadedPinSubtype.PinCategory
, LoadedAssetData.Object
, LoadedAssetData.Tooltip.IsEmpty() ? FriendlyName : LoadedAssetData.Tooltip
, false
, LoadedAssetData.PossibleObjectReferenceTypes)));
}
}
auto UnLoadedSubTypesPtr = TypesDatabase.UnLoadedTypesMap.Find(CategoryName);
if (UnLoadedSubTypesPtr && UnLoadedSubTypesPtr->IsValid())
{
for (FUnloadedAssetData& It : *UnLoadedSubTypesPtr->Get())
{
FPinTypeTreeInfoPtr TypeTreeInfo = MakeShareable(new UEdGraphSchema_K2::FPinTypeTreeInfo(It.AssetFriendlyName
, CategoryName
, It.StringAssetReference
, It.Tooltip
, false
, It.PossibleObjectReferenceTypes));
OutChildren.Add(TypeTreeInfo);
}
}
OutChildren.Sort(FCompareChildren());
}
/** Loads an asset based on the AssetReference through the asset registry */
static UObject* LoadAsset(const FStringAssetReference& AssetReference)
{
if (AssetReference.IsValid())
{
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
const FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*AssetReference.ToString());
return AssetData.GetAsset();
}
return nullptr;
}
};
const FEdGraphPinType& UEdGraphSchema_K2::FPinTypeTreeInfo::GetPinType(bool bForceLoadedSubCategoryObject)
{
if (bForceLoadedSubCategoryObject)
{
// Only attempt to load the sub category object if we need to
if ( SubCategoryObjectAssetReference.IsValid() && (!PinType.PinSubCategoryObject.IsValid() || FStringAssetReference(PinType.PinSubCategoryObject.Get()) != SubCategoryObjectAssetReference) )
{
UObject* LoadedObject = FGatherTypesHelper::LoadAsset(SubCategoryObjectAssetReference);
if(UBlueprint* BlueprintObject = Cast<UBlueprint>(LoadedObject))
{
PinType.PinSubCategoryObject = *BlueprintObject->GeneratedClass;
}
else
{
PinType.PinSubCategoryObject = LoadedObject;
}
}
}
else
{
if (SubCategoryObjectAssetReference.IsValid())
{
const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
const FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*SubCategoryObjectAssetReference.ToString());
if(!AssetData.IsAssetLoaded())
{
UObject* LoadedObject = FindObject<UClass>(ANY_PACKAGE, *AssetData.AssetClass.ToString());
// If the unloaded asset is a Blueprint, we need to pull the generated class and assign that
if(UBlueprint* BlueprintObject = Cast<UBlueprint>(LoadedObject))
{
PinType.PinSubCategoryObject = *BlueprintObject->GeneratedClass;
}
else
{
PinType.PinSubCategoryObject = LoadedObject;
}
}
else
{
PinType.PinSubCategoryObject = AssetData.GetAsset();
}
}
}
return PinType;
}
void UEdGraphSchema_K2::FPinTypeTreeInfo::Init(const FText& InFriendlyName, const FString& CategoryName, const UEdGraphSchema_K2* Schema, const FText& InTooltip, bool bInReadOnly, FTypesDatabase* TypesDatabase)
{
check( !CategoryName.IsEmpty() );
check( Schema );
FriendlyName = InFriendlyName;
Tooltip = InTooltip;
PinType.PinCategory = (CategoryName == PC_Enum? PC_Byte : CategoryName);
PinType.PinSubCategory = TEXT("");
PinType.PinSubCategoryObject = NULL;
bReadOnly = bInReadOnly;
CachedDescription = GenerateDescription();
if (Schema->DoesTypeHaveSubtypes(CategoryName))
{
if (TypesDatabase)
{
FGatherTypesHelper::Gather(InFriendlyName, CategoryName, *TypesDatabase, Children);
}
}
}
UEdGraphSchema_K2::FPinTypeTreeInfo::FPinTypeTreeInfo(const FString& CategoryName, UObject* SubCategoryObject, const FText& InTooltip, bool bInReadOnly/*=false*/, uint8 InPossibleObjectReferenceTypes)
: PossibleObjectReferenceTypes(InPossibleObjectReferenceTypes)
{
check( !CategoryName.IsEmpty() );
check( SubCategoryObject );
Tooltip = InTooltip;
PinType.PinCategory = CategoryName;
PinType.PinSubCategoryObject = SubCategoryObject;
bReadOnly = bInReadOnly;
CachedDescription = GenerateDescription();
}
UEdGraphSchema_K2::FPinTypeTreeInfo::FPinTypeTreeInfo(const FText& InFriendlyName, const FString& CategoryName, const FStringAssetReference& SubCategoryObject, const FText& InTooltip, bool bInReadOnly, uint8 InPossibleObjectReferenceTypes)
: PossibleObjectReferenceTypes(InPossibleObjectReferenceTypes)
{
FriendlyName = InFriendlyName;
check(!CategoryName.IsEmpty());
check(SubCategoryObject.IsValid());
Tooltip = InTooltip;
PinType.PinCategory = CategoryName;
SubCategoryObjectAssetReference = SubCategoryObject;
PinType.PinSubCategoryObject = SubCategoryObjectAssetReference.ResolveObject();
bReadOnly = bInReadOnly;
CachedDescription = GenerateDescription();
}
FText UEdGraphSchema_K2::FPinTypeTreeInfo::GenerateDescription()
{
if (!FriendlyName.IsEmpty())
{
return FriendlyName;
}
else if (PinType.PinSubCategoryObject.IsValid())
{
FText DisplayName;
if (UField* SubCategoryField = Cast<UField>(PinType.PinSubCategoryObject.Get()))
{
DisplayName = SubCategoryField->GetDisplayNameText();
}
else
{
DisplayName = FText::FromString(FName::NameToDisplayString(PinType.PinSubCategoryObject->GetName(), PinType.PinCategory == PC_Boolean));
}
return DisplayName;
}
else
{
return LOCTEXT("PinDescriptionError", "Error!");
}
}
FText UEdGraphSchema_K2::FPinTypeTreeInfo::GetDescription() const
{
return CachedDescription;
}
const FString UEdGraphSchema_K2::PC_Exec(TEXT("exec"));
const FString UEdGraphSchema_K2::PC_Boolean(TEXT("bool"));
const FString UEdGraphSchema_K2::PC_Byte(TEXT("byte"));
const FString UEdGraphSchema_K2::PC_Class(TEXT("class"));
const FString UEdGraphSchema_K2::PC_Int(TEXT("int"));
const FString UEdGraphSchema_K2::PC_Float(TEXT("float"));
const FString UEdGraphSchema_K2::PC_Name(TEXT("name"));
const FString UEdGraphSchema_K2::PC_Delegate(TEXT("delegate"));
const FString UEdGraphSchema_K2::PC_MCDelegate(TEXT("mcdelegate"));
const FString UEdGraphSchema_K2::PC_Object(TEXT("object"));
const FString UEdGraphSchema_K2::PC_Interface(TEXT("interface"));
const FString UEdGraphSchema_K2::PC_String(TEXT("string"));
const FString UEdGraphSchema_K2::PC_Text(TEXT("text"));
const FString UEdGraphSchema_K2::PC_Struct(TEXT("struct"));
const FString UEdGraphSchema_K2::PC_Wildcard(TEXT("wildcard"));
const FString UEdGraphSchema_K2::PC_Enum(TEXT("enum"));
const FString UEdGraphSchema_K2::PC_Asset(TEXT("asset"));
const FString UEdGraphSchema_K2::PC_AssetClass(TEXT("assetclass"));
const FString UEdGraphSchema_K2::PSC_Self(TEXT("self"));
const FString UEdGraphSchema_K2::PSC_Index(TEXT("index"));
const FString UEdGraphSchema_K2::PSC_Bitmask(TEXT("bitmask"));
const FString UEdGraphSchema_K2::PN_Execute(TEXT("execute"));
const FString UEdGraphSchema_K2::PN_Then(TEXT("then"));
const FString UEdGraphSchema_K2::PN_Completed(TEXT("Completed"));
const FString UEdGraphSchema_K2::PN_DelegateEntry(TEXT("delegate"));
const FString UEdGraphSchema_K2::PN_EntryPoint(TEXT("EntryPoint"));
const FString UEdGraphSchema_K2::PN_Self(TEXT("self"));
const FString UEdGraphSchema_K2::PN_Else(TEXT("else"));
const FString UEdGraphSchema_K2::PN_Loop(TEXT("loop"));
const FString UEdGraphSchema_K2::PN_After(TEXT("after"));
const FString UEdGraphSchema_K2::PN_ReturnValue(TEXT("ReturnValue"));
const FString UEdGraphSchema_K2::PN_ObjectToCast(TEXT("Object"));
const FString UEdGraphSchema_K2::PN_Condition(TEXT("Condition"));
const FString UEdGraphSchema_K2::PN_Start(TEXT("Start"));
const FString UEdGraphSchema_K2::PN_Stop(TEXT("Stop"));
const FString UEdGraphSchema_K2::PN_Index(TEXT("Index"));
const FString UEdGraphSchema_K2::PN_Item(TEXT("Item"));
const FString UEdGraphSchema_K2::PN_CastSucceeded(TEXT("then"));
const FString UEdGraphSchema_K2::PN_CastFailed(TEXT("CastFailed"));
const FString UEdGraphSchema_K2::PN_CastedValuePrefix(TEXT("As"));
const FString UEdGraphSchema_K2::PN_MatineeFinished(TEXT("Finished"));
const FName UEdGraphSchema_K2::FN_UserConstructionScript(TEXT("UserConstructionScript"));
const FName UEdGraphSchema_K2::FN_ExecuteUbergraphBase(TEXT("ExecuteUbergraph"));
const FName UEdGraphSchema_K2::GN_EventGraph(TEXT("EventGraph"));
const FName UEdGraphSchema_K2::GN_AnimGraph(TEXT("AnimGraph"));
const FText UEdGraphSchema_K2::VR_DefaultCategory(LOCTEXT("Default", "Default"));
const int32 UEdGraphSchema_K2::AG_LevelReference = 100;
const UScriptStruct* UEdGraphSchema_K2::VectorStruct = nullptr;
const UScriptStruct* UEdGraphSchema_K2::RotatorStruct = nullptr;
const UScriptStruct* UEdGraphSchema_K2::TransformStruct = nullptr;
const UScriptStruct* UEdGraphSchema_K2::LinearColorStruct = nullptr;
const UScriptStruct* UEdGraphSchema_K2::ColorStruct = nullptr;
bool UEdGraphSchema_K2::bGeneratingDocumentation = false;
int32 UEdGraphSchema_K2::CurrentCacheRefreshID = 0;
const FString UEdGraphSchema_K2::AllObjectTypes(TEXT("ObjectTypes"));
UEdGraphSchema_K2::UEdGraphSchema_K2(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// Initialize cached static references to well-known struct types
if (VectorStruct == nullptr)
{
VectorStruct = TBaseStructure<FVector>::Get();
RotatorStruct = TBaseStructure<FRotator>::Get();
TransformStruct = TBaseStructure<FTransform>::Get();
LinearColorStruct = TBaseStructure<FLinearColor>::Get();
ColorStruct = TBaseStructure<FColor>::Get();
}
}
bool UEdGraphSchema_K2::DoesFunctionHaveOutParameters( const UFunction* Function ) const
{
if ( Function != NULL )
{
for ( TFieldIterator<UProperty> PropertyIt(Function); PropertyIt; ++PropertyIt )
{
if ( PropertyIt->PropertyFlags & CPF_OutParm )
{
return true;
}
}
}
return false;
}
bool UEdGraphSchema_K2::CanFunctionBeUsedInGraph(const UClass* InClass, const UFunction* InFunction, const UEdGraph* InDestGraph, uint32 InAllowedFunctionTypes, bool bInCalledForEach, FText* OutReason) const
{
if (CanUserKismetCallFunction(InFunction))
{
bool bLatentFuncsAllowed = true;
bool bIsConstructionScript = false;
if(InDestGraph != nullptr)
{
bLatentFuncsAllowed = (GetGraphType(InDestGraph) == GT_Ubergraph || (GetGraphType(InDestGraph) == GT_Macro));
bIsConstructionScript = IsConstructionScript(InDestGraph);
}
const bool bIsPureFunc = (InFunction->HasAnyFunctionFlags(FUNC_BlueprintPure) != false);
if (bIsPureFunc)
{
const bool bAllowPureFuncs = (InAllowedFunctionTypes & FT_Pure) != 0;
if (!bAllowPureFuncs)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("PureFunctionsNotAllowed", "Pure functions are not allowed.");
}
return false;
}
}
else
{
const bool bAllowImperativeFuncs = (InAllowedFunctionTypes & FT_Imperative) != 0;
if (!bAllowImperativeFuncs)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("ImpureFunctionsNotAllowed", "Impure functions are not allowed.");
}
return false;
}
}
const bool bIsConstFunc = (InFunction->HasAnyFunctionFlags(FUNC_Const) != false);
const bool bAllowConstFuncs = (InAllowedFunctionTypes & FT_Const) != 0;
if (bIsConstFunc && !bAllowConstFuncs)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("ConstFunctionsNotAllowed", "Const functions are not allowed.");
}
return false;
}
const bool bIsLatent = InFunction->HasMetaData(FBlueprintMetadata::MD_Latent);
if (bIsLatent && !bLatentFuncsAllowed)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("LatentFunctionsNotAllowed", "Latent functions cannot be used here.");
}
return false;
}
const bool bIsProtected = InFunction->GetBoolMetaData(FBlueprintMetadata::MD_Protected);
const bool bFuncBelongsToSubClass = InClass->IsChildOf(InFunction->GetOuterUClass());
if (bIsProtected)
{
const bool bAllowProtectedFuncs = (InAllowedFunctionTypes & FT_Protected) != 0;
if (!bAllowProtectedFuncs)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("ProtectedFunctionsNotAllowed", "Protected functions are not allowed.");
}
return false;
}
if (!bFuncBelongsToSubClass)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("ProtectedFunctionInaccessible", "Function is protected and inaccessible.");
}
return false;
}
}
const bool bIsPrivate = InFunction->GetBoolMetaData(FBlueprintMetadata::MD_Private);
const bool bFuncBelongsToClass = bFuncBelongsToSubClass && (InFunction->GetOuterUClass() == InClass);
if (bIsPrivate && !bFuncBelongsToClass)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("PrivateFunctionInaccessible", "Function is private and inaccessible.");
}
return false;
}
const bool bIsUnsafeForConstruction = InFunction->GetBoolMetaData(FBlueprintMetadata::MD_UnsafeForConstructionScripts);
if (bIsUnsafeForConstruction && bIsConstructionScript)
{
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("FunctionUnsafeForConstructionScript", "Function cannot be used in a Construction Script.");
}
return false;
}
const bool bRequiresWorldContext = InFunction->HasMetaData(FBlueprintMetadata::MD_WorldContext);
if (bRequiresWorldContext)
{
if (InDestGraph && !InFunction->HasMetaData(FBlueprintMetadata::MD_CallableWithoutWorldContext))
{
const FString& ContextParam = InFunction->GetMetaData(FBlueprintMetadata::MD_WorldContext);
if (InFunction->FindPropertyByName(FName(*ContextParam)) != nullptr)
{
auto BP = FBlueprintEditorUtils::FindBlueprintForGraph(InDestGraph);
const bool bIsFunctLib = BP && (EBlueprintType::BPTYPE_FunctionLibrary == BP->BlueprintType);
UClass* ParentClass = BP ? BP->ParentClass : NULL;
const bool bIncompatibleParrent = ParentClass && (!ParentClass->GetDefaultObject()->ImplementsGetWorld() && !ParentClass->HasMetaData(FBlueprintMetadata::MD_ShowWorldContextPin));
if (!bIsFunctLib && bIncompatibleParrent)
{
if (OutReason != nullptr)
{
*OutReason = LOCTEXT("FunctionRequiresWorldContext", "Function requires a world context.");
}
return false;
}
}
}
}
const bool bFunctionStatic = InFunction->HasAllFunctionFlags(FUNC_Static);
const bool bHasReturnParams = (InFunction->GetReturnProperty() != NULL);
const bool bHasArrayPointerParms = InFunction->HasMetaData(FBlueprintMetadata::MD_ArrayParam);
const bool bAllowForEachCall = !bFunctionStatic && !bIsLatent && !bIsPureFunc && !bIsConstFunc && !bHasReturnParams && !bHasArrayPointerParms;
if (bInCalledForEach && !bAllowForEachCall)
{
if(OutReason != nullptr)
{
if(bFunctionStatic)
{
*OutReason = LOCTEXT("StaticFunctionsNotAllowedInForEachContext", "Static functions cannot be used within a ForEach context.");
}
else if(bIsLatent)
{
*OutReason = LOCTEXT("LatentFunctionsNotAllowedInForEachContext", "Latent functions cannot be used within a ForEach context.");
}
else if(bIsPureFunc)
{
*OutReason = LOCTEXT("PureFunctionsNotAllowedInForEachContext", "Pure functions cannot be used within a ForEach context.");
}
else if(bIsConstFunc)
{
*OutReason = LOCTEXT("ConstFunctionsNotAllowedInForEachContext", "Const functions cannot be used within a ForEach context.");
}
else if(bHasReturnParams)
{
*OutReason = LOCTEXT("FunctionsWithReturnValueNotAllowedInForEachContext", "Functions that return a value cannot be used within a ForEach context.");
}
else if(bHasArrayPointerParms)
{
*OutReason = LOCTEXT("FunctionsWithArrayParmsNotAllowedInForEachContext", "Functions with array parameters cannot be used within a ForEach context.");
}
else
{
*OutReason = LOCTEXT("FunctionNotAllowedInForEachContext", "Function cannot be used within a ForEach context.");
}
}
return false;
}
return true;
}
if(OutReason != nullptr)
{
*OutReason = LOCTEXT("FunctionInvalid", "Invalid function.");
}
return false;
}
UFunction* UEdGraphSchema_K2::GetCallableParentFunction(UFunction* Function) const
{
if( Function && Cast<UClass>(Function->GetOuter()) )
{
const FName FunctionName = Function->GetFName();
// Search up the parent scopes
UClass* ParentClass = CastChecked<UClass>(Function->GetOuter())->GetSuperClass();
UFunction* ClassFunction = ParentClass->FindFunctionByName(FunctionName);
return ClassFunction;
}
return NULL;
}
bool UEdGraphSchema_K2::CanUserKismetCallFunction(const UFunction* Function)
{
return Function &&
(Function->HasAllFunctionFlags(FUNC_BlueprintCallable) && !Function->HasAllFunctionFlags(FUNC_Delegate) && !Function->GetBoolMetaData(FBlueprintMetadata::MD_BlueprintInternalUseOnly) && !Function->HasMetaData(FBlueprintMetadata::MD_DeprecatedFunction));
}
bool UEdGraphSchema_K2::CanKismetOverrideFunction(const UFunction* Function)
{
return Function &&
(Function->HasAllFunctionFlags(FUNC_BlueprintEvent) && !Function->HasAllFunctionFlags(FUNC_Delegate) && !Function->GetBoolMetaData(FBlueprintMetadata::MD_BlueprintInternalUseOnly) && !Function->HasMetaData(FBlueprintMetadata::MD_DeprecatedFunction));
}
bool UEdGraphSchema_K2::HasFunctionAnyOutputParameter(const UFunction* InFunction)
{
check(InFunction);
for (TFieldIterator<UProperty> PropIt(InFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
UProperty* FuncParam = *PropIt;
if (FuncParam->HasAnyPropertyFlags(CPF_ReturnParm) || (FuncParam->HasAnyPropertyFlags(CPF_OutParm) && !FuncParam->HasAnyPropertyFlags(CPF_ReferenceParm) && !FuncParam->HasAnyPropertyFlags(CPF_ConstParm)))
{
return true;
}
}
return false;
}
bool UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(const UFunction* InFunction)
{
// First check we are override-able, non-static and non-const
if (!InFunction || !CanKismetOverrideFunction(InFunction) || InFunction->HasAnyFunctionFlags(FUNC_Static|FUNC_Const))
{
return false;
}
// Then look to see if we have any output, return, or reference params
return !HasFunctionAnyOutputParameter(InFunction);
}
bool UEdGraphSchema_K2::FunctionCanBeUsedInDelegate(const UFunction* InFunction)
{
if (!InFunction ||
!CanUserKismetCallFunction(InFunction) ||
InFunction->HasMetaData(FBlueprintMetadata::MD_Latent) ||
InFunction->HasAllFunctionFlags(FUNC_BlueprintPure))
{
return false;
}
return true;
}
FText UEdGraphSchema_K2::GetFriendlySignatureName(const UFunction* Function)
{
return UK2Node_CallFunction::GetUserFacingFunctionName( Function );
}
void UEdGraphSchema_K2::GetAutoEmitTermParameters(const UFunction* Function, TArray<FString>& AutoEmitParameterNames) const
{
AutoEmitParameterNames.Empty();
if( Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm) )
{
FString MetaData = Function->GetMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm);
MetaData.ParseIntoArray(AutoEmitParameterNames, TEXT(","), true);
for (int32 NameIndex = 0; NameIndex < AutoEmitParameterNames.Num();)
{
FString& ParameterName = AutoEmitParameterNames[NameIndex];
ParameterName.Trim();
ParameterName.TrimTrailing();
if (ParameterName.IsEmpty())
{
AutoEmitParameterNames.RemoveAtSwap(NameIndex);
}
else
{
++NameIndex;
}
}
}
}
bool UEdGraphSchema_K2::FunctionHasParamOfType(const UFunction* InFunction, UEdGraph const* InGraph, const FEdGraphPinType& DesiredPinType, bool bWantOutput) const
{
TSet<FString> HiddenPins;
FBlueprintEditorUtils::GetHiddenPinsForFunction(InGraph, InFunction, HiddenPins);
// Iterate over all params of function
for (TFieldIterator<UProperty> PropIt(InFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
UProperty* FuncParam = *PropIt;
// Ensure that this isn't a hidden parameter
if (!HiddenPins.Contains(FuncParam->GetName()))
{
// See if this is the direction we want (input or output)
const bool bIsFunctionInput = !FuncParam->HasAnyPropertyFlags(CPF_OutParm) || FuncParam->HasAnyPropertyFlags(CPF_ReferenceParm);
if (bIsFunctionInput != bWantOutput)
{
// See if this pin has compatible types
FEdGraphPinType ParamPinType;
bool bConverted = ConvertPropertyToPinType(FuncParam, ParamPinType);
if (bConverted)
{
UClass* Context = nullptr;
UBlueprint* Blueprint = Cast<UBlueprint>(InGraph->GetOuter());
if (Blueprint)
{
Context = Blueprint->GeneratedClass;
}
if (bIsFunctionInput && ArePinTypesCompatible(DesiredPinType, ParamPinType, Context))
{
return true;
}
else if (!bIsFunctionInput && ArePinTypesCompatible(ParamPinType, DesiredPinType, Context))
{
return true;
}
}
}
}
}
// Boo, no pin of this type
return false;
}
void UEdGraphSchema_K2::AddExtraFunctionFlags(const UEdGraph* CurrentGraph, int32 ExtraFlags) const
{
for (auto It = CurrentGraph->Nodes.CreateConstIterator(); It; ++It)
{
if (UK2Node_FunctionEntry* Node = Cast<UK2Node_FunctionEntry>(*It))
{
Node->AddExtraFlags(ExtraFlags);
}
}
}
void UEdGraphSchema_K2::MarkFunctionEntryAsEditable(const UEdGraph* CurrentGraph, bool bNewEditable) const
{
for (auto It = CurrentGraph->Nodes.CreateConstIterator(); It; ++It)
{
if (UK2Node_EditablePinBase* Node = Cast<UK2Node_EditablePinBase>(*It))
{
Node->bIsEditable = bNewEditable;
}
}
}
bool UEdGraphSchema_K2::IsActorValidForLevelScriptRefs(const AActor* TestActor, const UBlueprint* Blueprint) const
{
check(Blueprint);
return TestActor
&& FBlueprintEditorUtils::IsLevelScriptBlueprint(Blueprint)
&& (TestActor->GetLevel() == FBlueprintEditorUtils::GetLevelFromBlueprint(Blueprint))
&& FKismetEditorUtilities::IsActorValidForLevelScript(TestActor);
}
void UEdGraphSchema_K2::ReplaceSelectedNode(UEdGraphNode* SourceNode, AActor* TargetActor)
{
check(SourceNode);
if (TargetActor != NULL)
{
UK2Node_Literal* LiteralNode = (UK2Node_Literal*)(SourceNode);
if (LiteralNode)
{
const FScopedTransaction Transaction( LOCTEXT("ReplaceSelectedNodeUndoTransaction", "Replace Selected Node") );
LiteralNode->Modify();
LiteralNode->SetObjectRef( TargetActor );
LiteralNode->ReconstructNode();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(CastChecked<UEdGraph>(SourceNode->GetOuter()));
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
}
}
void UEdGraphSchema_K2::AddSelectedReplaceableNodes( UBlueprint* Blueprint, const UEdGraphNode* InGraphNode, FMenuBuilder* MenuBuilder ) const
{
//Only allow replace object reference functionality for literal nodes
const UK2Node_Literal* LiteralNode = Cast<UK2Node_Literal>(InGraphNode);
if (LiteralNode)
{
USelection* SelectedActors = GEditor->GetSelectedActors();
for(FSelectionIterator Iter(*SelectedActors); Iter; ++Iter)
{
// We only care about actors that are referenced in the world for literals, and also in the same level as this blueprint
AActor* Actor = Cast<AActor>(*Iter);
if( LiteralNode->GetObjectRef() != Actor && IsActorValidForLevelScriptRefs(Actor, Blueprint) )
{
FText Description = FText::Format( LOCTEXT("ChangeToActorName", "Change to <{0}>"), FText::FromString( Actor->GetActorLabel() ) );
FText ToolTip = LOCTEXT("ReplaceNodeReferenceToolTip", "Replace node reference");
MenuBuilder->AddMenuEntry( Description, ToolTip, FSlateIcon(), FUIAction(
FExecuteAction::CreateUObject((UEdGraphSchema_K2*const)this, &UEdGraphSchema_K2::ReplaceSelectedNode, const_cast< UEdGraphNode* >(InGraphNode), Actor) ) );
}
}
}
}
bool UEdGraphSchema_K2::CanUserKismetAccessVariable(const UProperty* Property, const UClass* InClass, EDelegateFilterMode FilterMode)
{
const bool bIsDelegate = Property->IsA(UMulticastDelegateProperty::StaticClass());
const bool bIsAccessible = Property->HasAllPropertyFlags(CPF_BlueprintVisible);
const bool bIsAssignableOrCallable = Property->HasAnyPropertyFlags(CPF_BlueprintAssignable | CPF_BlueprintCallable);
const bool bPassesDelegateFilter = (bIsAccessible && !bIsDelegate && (FilterMode != MustBeDelegate)) ||
(bIsAssignableOrCallable && bIsDelegate && (FilterMode != CannotBeDelegate));
const bool bHidden = FObjectEditorUtils::IsVariableCategoryHiddenFromClass(Property, InClass);
return !Property->HasAnyPropertyFlags(CPF_Parm) && bPassesDelegateFilter && !bHidden;
}
bool UEdGraphSchema_K2::ClassHasBlueprintAccessibleMembers(const UClass* InClass) const
{
// @TODO Don't show other blueprints yet...
UBlueprint* ClassBlueprint = UBlueprint::GetBlueprintFromClass(InClass);
if (!InClass->HasAnyClassFlags(CLASS_Deprecated | CLASS_NewerVersionExists) && (ClassBlueprint == NULL))
{
// Find functions
for (TFieldIterator<UFunction> FunctionIt(InClass, EFieldIteratorFlags::IncludeSuper); FunctionIt; ++FunctionIt)
{
UFunction* Function = *FunctionIt;
const bool bIsBlueprintProtected = Function->GetBoolMetaData(FBlueprintMetadata::MD_Protected);
const bool bHidden = FObjectEditorUtils::IsFunctionHiddenFromClass(Function, InClass);
if (UEdGraphSchema_K2::CanUserKismetCallFunction(Function) && !bIsBlueprintProtected && !bHidden)
{
return true;
}
}
// Find vars
for (TFieldIterator<UProperty> PropertyIt(InClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
{
UProperty* Property = *PropertyIt;
if (CanUserKismetAccessVariable(Property, InClass, CannotBeDelegate))
{
return true;
}
}
}
return false;
}
bool UEdGraphSchema_K2::IsAllowableBlueprintVariableType(const class UEnum* InEnum)
{
return InEnum && (InEnum->GetBoolMetaData(FBlueprintMetadata::MD_AllowableBlueprintVariableType) || InEnum->IsA<UUserDefinedEnum>());
}
bool UEdGraphSchema_K2::IsAllowableBlueprintVariableType(const class UClass* InClass)
{
if (InClass)
{
// No Skeleton classes or reinstancing classes (they would inherit the BlueprintType metadata)
if (FKismetEditorUtilities::IsClassABlueprintSkeleton(InClass)
|| InClass->HasAnyClassFlags(CLASS_NewerVersionExists))
{
return false;
}
// No Blueprint Macro Libraries
if (FKismetEditorUtilities::IsClassABlueprintMacroLibrary(InClass))
{
return false;
}
// UObject is an exception, and is always a blueprint-able type
if(InClass == UObject::StaticClass())
{
return true;
}
static const FBoolConfigValueHelper NotBlueprintType(TEXT("EditoronlyBP"), TEXT("bBlueprintIsNotBlueprintType"));
if (NotBlueprintType && InClass->IsChildOf(UBlueprint::StaticClass()))
{
return false;
}
// cannot have level script variables
if (InClass->IsChildOf(ALevelScriptActor::StaticClass()))
{
return false;
}
const UClass* ParentClass = InClass;
while(ParentClass)
{
// Climb up the class hierarchy and look for "BlueprintType" and "NotBlueprintType" to see if this class is allowed.
if(ParentClass->GetBoolMetaData(FBlueprintMetadata::MD_AllowableBlueprintVariableType)
|| ParentClass->HasMetaData(FBlueprintMetadata::MD_BlueprintSpawnableComponent))
{
return true;
}
else if(ParentClass->GetBoolMetaData(FBlueprintMetadata::MD_NotAllowableBlueprintVariableType))
{
return false;
}
ParentClass = ParentClass->GetSuperClass();
}
}
return false;
}
bool UEdGraphSchema_K2::IsAllowableBlueprintVariableType(const class UScriptStruct *InStruct)
{
if (auto UDStruct = Cast<const UUserDefinedStruct>(InStruct))
{
if (EUserDefinedStructureStatus::UDSS_UpToDate != UDStruct->Status.GetValue())
{
return false;
}
}
return InStruct && (InStruct->GetBoolMetaDataHierarchical(FBlueprintMetadata::MD_AllowableBlueprintVariableType));
}
bool UEdGraphSchema_K2::DoesGraphSupportImpureFunctions(const UEdGraph* InGraph) const
{
const EGraphType GraphType = GetGraphType(InGraph);
const bool bAllowImpureFuncs = GraphType != GT_Animation; //@TODO: It's really more nuanced than this (e.g., in a function someone wants to write as pure)
return bAllowImpureFuncs;
}
bool UEdGraphSchema_K2::IsPropertyExposedOnSpawn(const UProperty* Property)
{
if (Property)
{
const bool bMeta = Property->HasMetaData(FBlueprintMetadata::MD_ExposeOnSpawn);
const bool bFlag = Property->HasAllPropertyFlags(CPF_ExposeOnSpawn);
if (bMeta != bFlag)
{
UE_LOG(LogBlueprint, Warning
, TEXT("ExposeOnSpawn ambiguity. Property '%s', MetaData '%s', Flag '%s'")
, *Property->GetFullName()
, bMeta ? *GTrue.ToString() : *GFalse.ToString()
, bFlag ? *GTrue.ToString() : *GFalse.ToString());
}
return bMeta || bFlag;
}
return false;
}
// if node is a get/set variable and the variable it refers to does not exist
static bool IsUsingNonExistantVariable(const UEdGraphNode* InGraphNode, UBlueprint* OwnerBlueprint)
{
bool bNonExistantVariable = false;
const bool bBreakOrMakeStruct =
InGraphNode->IsA(UK2Node_BreakStruct::StaticClass()) ||
InGraphNode->IsA(UK2Node_MakeStruct::StaticClass());
if (!bBreakOrMakeStruct)
{
if (const UK2Node_Variable* Variable = Cast<const UK2Node_Variable>(InGraphNode))
{
if (Variable->VariableReference.IsSelfContext())
{
TArray<FName> CurrentVars;
FBlueprintEditorUtils::GetClassVariableList(OwnerBlueprint, CurrentVars);
if ( false == CurrentVars.Contains(Variable->GetVarName()) )
{
bNonExistantVariable = true;
}
}
else if(Variable->VariableReference.IsLocalScope())
{
// If there is no member scope, or we can't find the local variable in the member scope, then it's non-existant
UStruct* MemberScope = Variable->VariableReference.GetMemberScope(Variable->GetBlueprintClassFromNode());
if (MemberScope == nullptr || !FBlueprintEditorUtils::FindLocalVariable(OwnerBlueprint, MemberScope, Variable->GetVarName()))
{
bNonExistantVariable = true;
}
}
}
}
return bNonExistantVariable;
}
bool UEdGraphSchema_K2::PinHasSplittableStructType(const UEdGraphPin* InGraphPin) const
{
const FEdGraphPinType& PinType = InGraphPin->PinType;
bool bCanSplit = (!PinType.IsContainer() && PinType.PinCategory == PC_Struct);
if (bCanSplit)
{
UScriptStruct* StructType = CastChecked<UScriptStruct>(InGraphPin->PinType.PinSubCategoryObject.Get());
if (InGraphPin->Direction == EGPD_Input)
{
bCanSplit = UK2Node_MakeStruct::CanBeMade(StructType, true, true);
if (!bCanSplit)
{
const FString& MetaData = StructType->GetMetaData(TEXT("HasNativeMake"));
UFunction* Function = FindObject<UFunction>(NULL, *MetaData, true);
bCanSplit = (Function != NULL);
}
}
else
{
bCanSplit = UK2Node_BreakStruct::CanBeBroken(StructType, true, true);
if (!bCanSplit)
{
const FString& MetaData = StructType->GetMetaData(TEXT("HasNativeBreak"));
UFunction* Function = FindObject<UFunction>(NULL, *MetaData, true);
bCanSplit = (Function != NULL);
}
}
}
return bCanSplit;
}
bool UEdGraphSchema_K2::PinDefaultValueIsEditable(const UEdGraphPin& InGraphPin) const
{
// Array types are not currently assignable without a 'make array' node:
if( InGraphPin.PinType.IsContainer() )
{
return false;
}
// User defined structures (from code or from data) cannot accept default values:
if( InGraphPin.PinType.PinCategory == PC_Struct )
{
// Only the built in struct types are editable as 'default' values on a pin.
// See FNodeFactory::CreatePinWidget for justification of the above statement!
UObject const& SubCategoryObject = *InGraphPin.PinType.PinSubCategoryObject;
return &SubCategoryObject == VectorStruct
|| &SubCategoryObject == RotatorStruct
|| &SubCategoryObject == TransformStruct
|| &SubCategoryObject == LinearColorStruct
|| &SubCategoryObject == ColorStruct
|| &SubCategoryObject == FCollisionProfileName::StaticStruct();
}
return true;
}
void UEdGraphSchema_K2::SelectAllNodesInDirection(TEnumAsByte<enum EEdGraphPinDirection> InDirection, UEdGraph* Graph, UEdGraphPin* InGraphPin)
{
TArray<UEdGraphPin*> AllPins = InGraphPin->LinkedTo;
if (AllPins.Num() == 0)
{
return;
}
for (UEdGraphPin* Pin : AllPins)
{
UEdGraphNode* OwningNode = Pin->GetOwningNode();
FKismetEditorUtilities::AddToSelection(Graph, OwningNode);
TArray<UEdGraphPin*> LinkedPins = Pin->GetOwningNode()->GetAllPins();
for (UEdGraphPin* InputPin : LinkedPins)
{
if (InputPin->Direction == InDirection)
{
SelectAllNodesInDirection(InDirection, Graph, InputPin);
}
}
}
}
void UEdGraphSchema_K2::GetContextMenuActions(const UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, FMenuBuilder* MenuBuilder, bool bIsDebugging) const
{
check(CurrentGraph);
UBlueprint* OwnerBlueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(CurrentGraph);
if (InGraphPin != NULL)
{
MenuBuilder->BeginSection("EdGraphSchemaPinActions", LOCTEXT("PinActionsMenuHeader", "Pin Actions"));
{
if (!bIsDebugging)
{
// Break pin links
if (InGraphPin->LinkedTo.Num() > 1)
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().BreakPinLinks );
}
// Add the change pin type action, if this is a select node
if (InGraphNode->IsA(UK2Node_Select::StaticClass()))
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().ChangePinType);
}
// add sub menu for break link to
if (InGraphPin->LinkedTo.Num() > 0)
{
MenuBuilder->AddMenuEntry(
InGraphPin->Direction == EEdGraphPinDirection::EGPD_Input ? LOCTEXT("SelectAllInputNodes", "Select All Input Nodes") : LOCTEXT("SelectAllOutputNodes", "Select All Output Nodes"),
InGraphPin->Direction == EEdGraphPinDirection::EGPD_Input ? LOCTEXT("SelectAllInputNodesTooltip", "Adds all input Nodes linked to this Pin to selection") : LOCTEXT("SelectAllOutputNodesTooltip", "Adds all output Nodes linked to this Pin to selection"),
FSlateIcon(),
FUIAction(FExecuteAction::CreateUObject((UEdGraphSchema_K2*const)this, &UEdGraphSchema_K2::SelectAllNodesInDirection, InGraphPin->Direction, const_cast<UEdGraph*>(CurrentGraph), const_cast<UEdGraphPin*>(InGraphPin))));
if(InGraphPin->LinkedTo.Num() > 1)
{
MenuBuilder->AddSubMenu(
LOCTEXT("BreakLinkTo", "Break Link To..."),
LOCTEXT("BreakSpecificLinks", "Break a specific link..."),
FNewMenuDelegate::CreateUObject( (UEdGraphSchema_K2*const)this, &UEdGraphSchema_K2::GetBreakLinkToSubMenuActions, const_cast<UEdGraphPin*>(InGraphPin)));
MenuBuilder->AddSubMenu(
LOCTEXT("JumpToConnection", "Jump to Connection..."),
LOCTEXT("JumpToSpecificConnection", "Jump to specific connection..."),
FNewMenuDelegate::CreateUObject( (UEdGraphSchema_K2*const)this, &UEdGraphSchema_K2::GetJumpToConnectionSubMenuActions, const_cast<UEdGraphPin*>(InGraphPin)));
MenuBuilder->AddSubMenu(
LOCTEXT("StraightenConnection", "Straighten Connection To..."),
LOCTEXT("StraightenConnection_Tip", "Straighten a specific connection"),
FNewMenuDelegate::CreateUObject( this, &UEdGraphSchema_K2::GetStraightenConnectionToSubMenuActions, const_cast<UEdGraphPin*>(InGraphPin)));
}
else
{
((UEdGraphSchema_K2*const)this)->GetBreakLinkToSubMenuActions(*MenuBuilder, const_cast<UEdGraphPin*>(InGraphPin));
((UEdGraphSchema_K2*const)this)->GetJumpToConnectionSubMenuActions(*MenuBuilder, const_cast<UEdGraphPin*>(InGraphPin));
UEdGraphPin* Pin = InGraphPin->LinkedTo[0];
FText PinName = Pin->GetDisplayName();
FText NodeName = Pin->GetOwningNode()->GetNodeTitle(ENodeTitleType::ListView);
MenuBuilder->AddMenuEntry(
FGraphEditorCommands::Get().StraightenConnections,
NAME_None,
FText::Format(LOCTEXT("StraightenDescription_SinglePin", "Straighten Connection to {0} ({1})"), NodeName, PinName),
FText::Format(LOCTEXT("StraightenDescription_SinglePin_Node_Tip", "Straighten the connection between this pin, and {0} ({1})"), NodeName, PinName),
FSlateIcon(NAME_None, NAME_None, NAME_None)
);
}
}
// Conditionally add the var promotion pin if this is an output pin and it's not an exec pin
if (InGraphPin->PinType.PinCategory != PC_Exec)
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().PromoteToVariable );
if (FBlueprintEditorUtils::DoesSupportLocalVariables(CurrentGraph))
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().PromoteToLocalVariable );
}
}
if (InGraphPin->PinType.PinCategory == PC_Struct && InGraphNode->CanSplitPin(InGraphPin))
{
// If the pin cannot be split, create an error tooltip to use
FText Tooltip;
if (PinHasSplittableStructType(InGraphPin))
{
Tooltip = FGraphEditorCommands::Get().SplitStructPin->GetDescription();
}
else
{
Tooltip = LOCTEXT("SplitStructPin_Error", "Cannot split the struct pin, it may be missing Blueprint exposed properties!");
}
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().SplitStructPin, NAME_None, FGraphEditorCommands::Get().SplitStructPin->GetLabel(), Tooltip );
}
if (InGraphPin->ParentPin != NULL)
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().RecombineStructPin );
}
// Conditionally add the execution path pin removal if this is an execution branching node
if( InGraphPin->Direction == EGPD_Output && InGraphPin->GetOwningNode())
{
if (CastChecked<UK2Node>(InGraphPin->GetOwningNode())->CanEverRemoveExecutionPin())
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().RemoveExecutionPin );
}
}
if (UK2Node_SetFieldsInStruct::ShowCustomPinActions(InGraphPin, true))
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().RemoveThisStructVarPin);
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().RemoveOtherStructVarPins);
}
}
}
MenuBuilder->EndSection();
// Add the watch pin / unwatch pin menu items
MenuBuilder->BeginSection("EdGraphSchemaWatches", LOCTEXT("WatchesHeader", "Watches"));
{
if (!IsMetaPin(*InGraphPin))
{
const UEdGraphPin* WatchedPin = ((InGraphPin->Direction == EGPD_Input) && (InGraphPin->LinkedTo.Num() > 0)) ? InGraphPin->LinkedTo[0] : InGraphPin;
if (FKismetDebugUtilities::IsPinBeingWatched(OwnerBlueprint, WatchedPin))
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().StopWatchingPin );
}
else
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().StartWatchingPin );
}
}
}
MenuBuilder->EndSection();
}
else if (InGraphNode != NULL)
{
if (IsUsingNonExistantVariable(InGraphNode, OwnerBlueprint))
{
MenuBuilder->BeginSection("EdGraphSchemaNodeActions", LOCTEXT("NodeActionsMenuHeader", "Node Actions"));
{
GetNonExistentVariableMenu(InGraphNode,OwnerBlueprint, MenuBuilder);
}
MenuBuilder->EndSection();
}
else
{
MenuBuilder->BeginSection("EdGraphSchemaNodeActions", LOCTEXT("NodeActionsMenuHeader", "Node Actions"));
{
if (!bIsDebugging)
{
// Replaceable node display option
AddSelectedReplaceableNodes( OwnerBlueprint, InGraphNode, MenuBuilder );
// Node contextual actions
MenuBuilder->AddMenuEntry( FGenericCommands::Get().Delete );
MenuBuilder->AddMenuEntry( FGenericCommands::Get().Cut );
MenuBuilder->AddMenuEntry( FGenericCommands::Get().Copy );
MenuBuilder->AddMenuEntry( FGenericCommands::Get().Duplicate );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().ReconstructNodes );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().BreakNodeLinks );
// Conditionally add the action to add an execution pin, if this is an execution node
if( InGraphNode->IsA(UK2Node_ExecutionSequence::StaticClass()) || InGraphNode->IsA(UK2Node_Switch::StaticClass()) )
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().AddExecutionPin );
}
// Conditionally add the action to create a super function call node, if this is an event or function entry
if( InGraphNode->IsA(UK2Node_Event::StaticClass()) || InGraphNode->IsA(UK2Node_FunctionEntry::StaticClass()) )
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().AddParentNode );
}
// Conditionally add the actions to add or remove an option pin, if this is a select node
if (InGraphNode->IsA(UK2Node_Select::StaticClass()))
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().AddOptionPin);
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().RemoveOptionPin);
}
// Don't show the "Assign selected Actor" option if more than one actor is selected
if (InGraphNode->IsA(UK2Node_ActorBoundEvent::StaticClass()) && GEditor->GetSelectedActorCount() == 1)
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().AssignReferencedActor);
}
// Add the goto source code action for native functions
if (InGraphNode->IsA(UK2Node_CallFunction::StaticClass()))
{
const UEdGraphNode* ResultEventNode= NULL;
if(Cast<UK2Node_CallFunction>(InGraphNode)->GetFunctionGraph(ResultEventNode))
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().GoToDefinition);
}
else
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().GotoNativeFunctionDefinition);
}
}
// Functions, macros, and composite nodes support going to a definition
if (InGraphNode->IsA(UK2Node_MacroInstance::StaticClass()) || InGraphNode->IsA(UK2Node_Composite::StaticClass()))
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().GoToDefinition);
}
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().FindReferences);
// show search for references for variable nodes and goto source code action
if (InGraphNode->IsA(UK2Node_Variable::StaticClass()))
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().GotoNativeVariableDefinition);
GetReplaceVariableMenu(InGraphNode,OwnerBlueprint, MenuBuilder, true);
}
if (InGraphNode->IsA(UK2Node_SetFieldsInStruct::StaticClass()))
{
MenuBuilder->AddMenuEntry(FGraphEditorCommands::Get().RestoreAllStructVarPins);
}
MenuBuilder->AddMenuEntry(FGenericCommands::Get().Rename, NAME_None, LOCTEXT("Rename", "Rename"), LOCTEXT("Rename_Tooltip", "Renames selected function or variable in blueprint.") );
}
// Select referenced actors in the level
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().SelectReferenceInLevel );
}
MenuBuilder->EndSection(); //EdGraphSchemaNodeActions
if (!bIsDebugging)
{
// Collapse/expand nodes
MenuBuilder->BeginSection("EdGraphSchemaOrganization", LOCTEXT("OrganizationHeader", "Organization"));
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().CollapseNodes );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().CollapseSelectionToFunction );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().CollapseSelectionToMacro );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().ExpandNodes );
if(InGraphNode->IsA(UK2Node_Composite::StaticClass()))
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().PromoteSelectionToFunction );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().PromoteSelectionToMacro );
}
MenuBuilder->AddSubMenu(LOCTEXT("AlignmentHeader", "Alignment"), FText(), FNewMenuDelegate::CreateLambda([](FMenuBuilder& InMenuBuilder){
InMenuBuilder.BeginSection("EdGraphSchemaAlignment", LOCTEXT("AlignHeader", "Align"));
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().AlignNodesTop );
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().AlignNodesMiddle );
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().AlignNodesBottom );
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().AlignNodesLeft );
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().AlignNodesCenter );
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().AlignNodesRight );
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().StraightenConnections );
InMenuBuilder.EndSection();
InMenuBuilder.BeginSection("EdGraphSchemaDistribution", LOCTEXT("DistributionHeader", "Distribution"));
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().DistributeNodesHorizontally );
InMenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().DistributeNodesVertically );
InMenuBuilder.EndSection();
}));
}
MenuBuilder->EndSection();
}
if (const UK2Node* K2Node = Cast<const UK2Node>(InGraphNode))
{
if (!K2Node->IsNodePure())
{
const UBlueprintEditorSettings* EditorSettings = GetDefault<UBlueprintEditorSettings>();
if (EditorSettings && EditorSettings->bAllowExplicitImpureNodeDisabling)
{
// Don't expose the enabled state for disabled nodes that were not explicitly disabled by the user
if (!bIsDebugging && (K2Node->bUserSetEnabledState || K2Node->EnabledState != ENodeEnabledState::Disabled))
{
// Add compile options
MenuBuilder->BeginSection("EdGraphSchemaCompileOptions", LOCTEXT("CompileOptionsHeader", "Compile Options"));
{
MenuBuilder->AddMenuEntry(
FGraphEditorCommands::Get().DisableNodes,
NAME_None,
LOCTEXT("DisableCompile", "Disable (Do Not Compile)"),
LOCTEXT("DisableCompileToolTip", "Selected node(s) will not be compiled."));
TSharedPtr<const FUICommandList> MenuCommandList = MenuBuilder->GetTopCommandList();
if(ensure(MenuCommandList.IsValid()))
{
const FUIAction* SubMenuUIAction = MenuCommandList->GetActionForCommand(FGraphEditorCommands::Get().EnableNodes);
if(ensure(SubMenuUIAction))
{
MenuBuilder->AddSubMenu(
LOCTEXT("EnableCompileSubMenu", "Enable Compile"),
LOCTEXT("EnableCompileSubMenuToolTip", "Options to enable selected node(s) for compile."),
FNewMenuDelegate::CreateLambda([MenuCommandList](FMenuBuilder& SubMenuBuilder)
{
SubMenuBuilder.PushCommandList(MenuCommandList.ToSharedRef());
SubMenuBuilder.AddMenuEntry(
FGraphEditorCommands::Get().EnableNodes_Always,
NAME_None,
LOCTEXT("EnableCompileAlways", "Always"),
LOCTEXT("EnableCompileAlwaysToolTip", "Always compile selected node(s)."));
SubMenuBuilder.AddMenuEntry(
FGraphEditorCommands::Get().EnableNodes_DevelopmentOnly,
NAME_None,
LOCTEXT("EnableCompileDevelopmentOnly", "Development Only"),
LOCTEXT("EnableCompileDevelopmentOnlyToolTip", "Compile selected node(s) for development only."));
SubMenuBuilder.PopCommandList();
}),
*SubMenuUIAction,
NAME_None, FGraphEditorCommands::Get().EnableNodes->GetUserInterfaceType());
}
}
}
MenuBuilder->EndSection();
}
}
// Add breakpoint actions
MenuBuilder->BeginSection("EdGraphSchemaBreakpoints", LOCTEXT("BreakpointsHeader", "Breakpoints"));
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().ToggleBreakpoint );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().AddBreakpoint );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().RemoveBreakpoint );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().EnableBreakpoint );
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().DisableBreakpoint );
}
MenuBuilder->EndSection();
}
}
MenuBuilder->BeginSection("EdGraphSchemaDocumentation", LOCTEXT("DocumentationHeader", "Documentation"));
{
MenuBuilder->AddMenuEntry( FGraphEditorCommands::Get().GoToDocumentation );
}
MenuBuilder->EndSection();
}
}
Super::GetContextMenuActions(CurrentGraph, InGraphNode, InGraphPin, MenuBuilder, bIsDebugging);
}
void UEdGraphSchema_K2::OnCreateNonExistentVariable( UK2Node_Variable* Variable, UBlueprint* OwnerBlueprint)
{
if (UEdGraphPin* Pin = Variable->FindPin(Variable->GetVarNameString()))
{
const FScopedTransaction Transaction( LOCTEXT("CreateMissingVariable", "Create Missing Variable") );
if (FBlueprintEditorUtils::AddMemberVariable(OwnerBlueprint,Variable->GetVarName(), Pin->PinType))
{
FGuid Guid = FBlueprintEditorUtils::FindMemberVariableGuidByName(OwnerBlueprint, Variable->GetVarName());
Variable->VariableReference.SetSelfMember( Variable->GetVarName(), Guid );
}
}
}
void UEdGraphSchema_K2::OnCreateNonExistentLocalVariable( UK2Node_Variable* Variable, UBlueprint* OwnerBlueprint)
{
if (UEdGraphPin* Pin = Variable->FindPin(Variable->GetVarNameString()))
{
const FScopedTransaction Transaction( LOCTEXT("CreateMissingLocalVariable", "Create Missing Local Variable") );
FName VarName = Variable->GetVarName();
if (FBlueprintEditorUtils::AddLocalVariable(OwnerBlueprint, Variable->GetGraph(), VarName, Pin->PinType))
{
FGuid LocalVarGuid = FBlueprintEditorUtils::FindLocalVariableGuidByName(OwnerBlueprint, Variable->GetGraph(), VarName);
if (LocalVarGuid.IsValid())
{
// Loop through every variable in the graph, check if the variable references are the same, and update them
FMemberReference OldReference = Variable->VariableReference;
TArray<UK2Node_Variable*> VariableNodeList;
Variable->GetGraph()->GetNodesOfClass(VariableNodeList);
for( UK2Node_Variable* VariableNode : VariableNodeList)
{
if (VariableNode->VariableReference.IsSameReference(OldReference))
{
VariableNode->VariableReference.SetLocalMember(VarName, FBlueprintEditorUtils::GetTopLevelGraph(Variable->GetGraph())->GetName(), LocalVarGuid);
VariableNode->ReconstructNode();
}
}
}
}
}
}
void UEdGraphSchema_K2::OnReplaceVariableForVariableNode( UK2Node_Variable* Variable, UBlueprint* OwnerBlueprint, FString VariableName, bool bIsSelfMember)
{
if(UEdGraphPin* Pin = Variable->FindPin(Variable->GetVarNameString()))
{
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "GraphEd_ReplaceVariable", "Replace Variable") );
Variable->Modify();
Pin->Modify();
if (bIsSelfMember)
{
FName VarName = FName(*VariableName);
FGuid Guid = FBlueprintEditorUtils::FindMemberVariableGuidByName(OwnerBlueprint, VarName);
Variable->VariableReference.SetSelfMember( VarName, Guid );
}
else
{
UEdGraph* FunctionGraph = FBlueprintEditorUtils::GetTopLevelGraph(Variable->GetGraph());
Variable->VariableReference.SetLocalMember( FName(*VariableName), FunctionGraph->GetName(), FBlueprintEditorUtils::FindLocalVariableGuidByName(OwnerBlueprint, FunctionGraph, *VariableName));
}
Pin->PinName = VariableName;
Variable->ReconstructNode();
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(OwnerBlueprint);
}
}
void UEdGraphSchema_K2::GetReplaceVariableMenu(FMenuBuilder& MenuBuilder, UK2Node_Variable* Variable, UBlueprint* OwnerBlueprint, bool bReplaceExistingVariable/*=false*/)
{
if (UEdGraphPin* Pin = Variable->FindPin(Variable->GetVarNameString()))
{
FName ExistingVariableName = bReplaceExistingVariable? Variable->GetVarName() : NAME_None;
FText ReplaceVariableWithTooltipFormat;
if(!bReplaceExistingVariable)
{
ReplaceVariableWithTooltipFormat = LOCTEXT("ReplaceNonExistantVarToolTip", "Variable '{OldVariable}' does not exist, replace with matching variable '{AlternateVariable}'?");
}
else
{
ReplaceVariableWithTooltipFormat = LOCTEXT("ReplaceExistantVarToolTip", "Replace Variable '{OldVariable}' with matching variable '{AlternateVariable}'?");
}
TArray<FName> Variables;
FBlueprintEditorUtils::GetNewVariablesOfType(OwnerBlueprint, Pin->PinType, Variables);
MenuBuilder.BeginSection(NAME_None, LOCTEXT("Variables", "Variables"));
for (TArray<FName>::TIterator VarIt(Variables); VarIt; ++VarIt)
{
if(*VarIt != ExistingVariableName)
{
const FText AlternativeVar = FText::FromName( *VarIt );
FFormatNamedArguments TooltipArgs;
TooltipArgs.Add(TEXT("OldVariable"), Variable->GetVarNameText());
TooltipArgs.Add(TEXT("AlternateVariable"), AlternativeVar);
const FText Desc = FText::Format(ReplaceVariableWithTooltipFormat, TooltipArgs);
MenuBuilder.AddMenuEntry( AlternativeVar, Desc, FSlateIcon(), FUIAction(
FExecuteAction::CreateStatic(&UEdGraphSchema_K2::OnReplaceVariableForVariableNode, const_cast<UK2Node_Variable* >(Variable),OwnerBlueprint, (*VarIt).ToString(), /*bIsSelfMember=*/true ) ) );
}
}
MenuBuilder.EndSection();
FText ReplaceLocalVariableWithTooltipFormat;
if(!bReplaceExistingVariable)
{
ReplaceLocalVariableWithTooltipFormat = LOCTEXT("ReplaceNonExistantLocalVarToolTip", "Variable '{OldVariable}' does not exist, replace with matching local variable '{AlternateVariable}'?");
}
else
{
ReplaceLocalVariableWithTooltipFormat = LOCTEXT("ReplaceExistantLocalVarToolTip", "Replace Variable '{OldVariable}' with matching local variable '{AlternateVariable}'?");
}
TArray<FName> LocalVariables;
FBlueprintEditorUtils::GetLocalVariablesOfType(Variable->GetGraph(), Pin->PinType, LocalVariables);
MenuBuilder.BeginSection(NAME_None, LOCTEXT("LocalVariables", "LocalVariables"));
for (TArray<FName>::TIterator VarIt(LocalVariables); VarIt; ++VarIt)
{
if(*VarIt != ExistingVariableName)
{
const FText AlternativeVar = FText::FromName( *VarIt );
FFormatNamedArguments TooltipArgs;
TooltipArgs.Add(TEXT("OldVariable"), Variable->GetVarNameText());
TooltipArgs.Add(TEXT("AlternateVariable"), AlternativeVar);
const FText Desc = FText::Format( ReplaceLocalVariableWithTooltipFormat, TooltipArgs );
MenuBuilder.AddMenuEntry( AlternativeVar, Desc, FSlateIcon(), FUIAction(
FExecuteAction::CreateStatic(&UEdGraphSchema_K2::OnReplaceVariableForVariableNode, const_cast<UK2Node_Variable* >(Variable),OwnerBlueprint, (*VarIt).ToString(), /*bIsSelfMember=*/false ) ) );
}
}
MenuBuilder.EndSection();
}
}
void UEdGraphSchema_K2::GetNonExistentVariableMenu( const UEdGraphNode* InGraphNode, UBlueprint* OwnerBlueprint, FMenuBuilder* MenuBuilder ) const
{
if (const UK2Node_Variable* Variable = Cast<const UK2Node_Variable>(InGraphNode))
{
// Creating missing variables should never occur in a Macro Library or Interface, they do not support variables
if(OwnerBlueprint->BlueprintType != BPTYPE_MacroLibrary && OwnerBlueprint->BlueprintType != BPTYPE_Interface )
{
// Creating missing member variables should never occur in a Function Library, they do not support variables
if(OwnerBlueprint->BlueprintType != BPTYPE_FunctionLibrary)
{
// create missing variable
const FText Label = FText::Format( LOCTEXT("CreateNonExistentVar", "Create variable '{0}'"), Variable->GetVarNameText());
const FText Desc = FText::Format( LOCTEXT("CreateNonExistentVarToolTip", "Variable '{0}' does not exist, create it?"), Variable->GetVarNameText());
MenuBuilder->AddMenuEntry( Label, Desc, FSlateIcon(), FUIAction(
FExecuteAction::CreateStatic( &UEdGraphSchema_K2::OnCreateNonExistentVariable, const_cast<UK2Node_Variable* >(Variable),OwnerBlueprint) ) );
}
// Only allow creating missing local variables if in a function graph
if(InGraphNode->GetGraph()->GetSchema()->GetGraphType(InGraphNode->GetGraph()) == GT_Function)
{
const FText Label = FText::Format( LOCTEXT("CreateNonExistentLocalVar", "Create local variable '{0}'"), Variable->GetVarNameText());
const FText Desc = FText::Format( LOCTEXT("CreateNonExistentLocalVarToolTip", "Local variable '{0}' does not exist, create it?"), Variable->GetVarNameText());
MenuBuilder->AddMenuEntry( Label, Desc, FSlateIcon(), FUIAction(
FExecuteAction::CreateStatic( &UEdGraphSchema_K2::OnCreateNonExistentLocalVariable, const_cast<UK2Node_Variable* >(Variable),OwnerBlueprint) ) );
}
}
// delete this node
{
const FText Desc = FText::Format( LOCTEXT("DeleteNonExistentVarToolTip", "Referenced variable '{0}' does not exist, delete this node?"), Variable->GetVarNameText());
MenuBuilder->AddMenuEntry( FGenericCommands::Get().Delete, NAME_None, FGenericCommands::Get().Delete->GetLabel(), Desc);
}
GetReplaceVariableMenu(InGraphNode, OwnerBlueprint, MenuBuilder);
}
}
void UEdGraphSchema_K2::GetReplaceVariableMenu(const UEdGraphNode* InGraphNode, UBlueprint* InOwnerBlueprint, FMenuBuilder* InMenuBuilder, bool bInReplaceExistingVariable/* = false*/) const
{
if (const UK2Node_Variable* Variable = Cast<const UK2Node_Variable>(InGraphNode))
{
// replace with matching variables
if (UEdGraphPin* Pin = Variable->FindPin(Variable->GetVarNameString()))
{
FName ExistingVariableName = bInReplaceExistingVariable? Variable->GetVarName() : NAME_None;
TArray<FName> Variables;
FBlueprintEditorUtils::GetNewVariablesOfType(InOwnerBlueprint, Pin->PinType, Variables);
Variables.RemoveSwap(ExistingVariableName);
TArray<FName> LocalVariables;
FBlueprintEditorUtils::GetLocalVariablesOfType(Variable->GetGraph(), Pin->PinType, LocalVariables);
LocalVariables.RemoveSwap(ExistingVariableName);
if (Variables.Num() > 0 || LocalVariables.Num() > 0)
{
FText ReplaceVariableWithTooltip;
if(bInReplaceExistingVariable)
{
ReplaceVariableWithTooltip = LOCTEXT("ReplaceVariableWithToolTip", "Replace Variable '{0}' with another variable?");
}
else
{
ReplaceVariableWithTooltip = LOCTEXT("ReplaceMissingVariableWithToolTip", "Variable '{0}' does not exist, replace with another variable?");
}
InMenuBuilder->AddSubMenu(
FText::Format( LOCTEXT("ReplaceVariableWith", "Replace variable '{0}' with..."), Variable->GetVarNameText()),
FText::Format( ReplaceVariableWithTooltip, Variable->GetVarNameText()),
FNewMenuDelegate::CreateStatic( &UEdGraphSchema_K2::GetReplaceVariableMenu,
const_cast<UK2Node_Variable*>(Variable),InOwnerBlueprint, bInReplaceExistingVariable));
}
}
}
}
void UEdGraphSchema_K2::GetBreakLinkToSubMenuActions( class FMenuBuilder& MenuBuilder, UEdGraphPin* InGraphPin )
{
// Make sure we have a unique name for every entry in the list
TMap< FString, uint32 > LinkTitleCount;
// Add all the links we could break from
for(TArray<class UEdGraphPin*>::TConstIterator Links(InGraphPin->LinkedTo); Links; ++Links)
{
UEdGraphPin* Pin = *Links;
FText Title = Pin->GetOwningNode()->GetNodeTitle(ENodeTitleType::ListView);
FString TitleString = Title.ToString();
if ( Pin->PinName != TEXT("") )
{
TitleString = FString::Printf(TEXT("%s (%s)"), *TitleString, *Pin->GetDisplayName().ToString());
// Add name of connection if possible
FFormatNamedArguments Args;
Args.Add( TEXT("NodeTitle"), Title );
Args.Add( TEXT("PinName"), Pin->GetDisplayName() );
Title = FText::Format( LOCTEXT("BreakDescPin", "{NodeTitle} ({PinName})"), Args );
}
uint32 &Count = LinkTitleCount.FindOrAdd( TitleString );
FText Description;
FFormatNamedArguments Args;
Args.Add( TEXT("NodeTitle"), Title );
Args.Add( TEXT("NumberOfNodes"), Count );
if ( Count == 0 )
{
Description = FText::Format( LOCTEXT("BreakDesc", "Break link to {NodeTitle}"), Args );
}
else
{
Description = FText::Format( LOCTEXT("BreakDescMulti", "Break link to {NodeTitle} ({NumberOfNodes})"), Args );
}
++Count;
MenuBuilder.AddMenuEntry( Description, Description, FSlateIcon(), FUIAction(
FExecuteAction::CreateUObject(this, &UEdGraphSchema_K2::BreakSinglePinLink, const_cast< UEdGraphPin* >(InGraphPin), *Links)));
}
}
void UEdGraphSchema_K2::GetJumpToConnectionSubMenuActions( class FMenuBuilder& MenuBuilder, UEdGraphPin* InGraphPin )
{
// Make sure we have a unique name for every entry in the list
TMap< FString, uint32 > LinkTitleCount;
// Add all the links we could break from
for(const UEdGraphPin* PinLink : InGraphPin->LinkedTo )
{
FText Title = PinLink->GetOwningNode()->GetNodeTitle(ENodeTitleType::ListView);
FString TitleString = Title.ToString();
if ( PinLink->PinName != TEXT("") )
{
TitleString = FString::Printf(TEXT("%s (%s)"), *TitleString, *PinLink->GetDisplayName().ToString());
// Add name of connection if possible
FFormatNamedArguments Args;
Args.Add( TEXT("NodeTitle"), Title );
Args.Add( TEXT("PinName"), PinLink->GetDisplayName() );
Title = FText::Format( LOCTEXT("JumpToDescPin", "{NodeTitle} ({PinName})"), Args );
}
uint32 &Count = LinkTitleCount.FindOrAdd( TitleString );
FText Description;
FFormatNamedArguments Args;
Args.Add( TEXT("NodeTitle"), Title );
Args.Add( TEXT("NumberOfNodes"), Count );
if ( Count == 0 )
{
Description = FText::Format( LOCTEXT("JumpDesc", "Jump to {NodeTitle}"), Args );
}
else
{
Description = FText::Format( LOCTEXT("JumpDescMulti", "Jump to {NodeTitle} ({NumberOfNodes})"), Args );
}
++Count;
MenuBuilder.AddMenuEntry( Description, Description, FSlateIcon(), FUIAction(
FExecuteAction::CreateStatic(&FKismetEditorUtilities::BringKismetToFocusAttentionOnPin, PinLink)));
}
}
// todo: this is a long way off ideal, but we can't pass context from our menu items onto the graph panel implementation
// It'd be better to be able to pass context through to menu/ui commands
namespace { UEdGraphPin* StraightenDestinationPin = nullptr; }
UEdGraphPin* UEdGraphSchema_K2::GetAndResetStraightenDestinationPin()
{
UEdGraphPin* Temp = StraightenDestinationPin;
StraightenDestinationPin = nullptr;
return Temp;
}
void UEdGraphSchema_K2::GetStraightenConnectionToSubMenuActions( class FMenuBuilder& MenuBuilder, UEdGraphPin* InGraphPin ) const
{
auto MenuCommandList = MenuBuilder.GetTopCommandList();
if(!ensure(MenuCommandList.IsValid()))
{
return;
}
// Make sure we have a unique name for every entry in the list
TMap<FString, uint32> LinkTitleCount;
TMap<UEdGraphNode*, TArray<UEdGraphPin*>> NodeToPins;
for (UEdGraphPin* Pin : InGraphPin->LinkedTo)
{
UEdGraphNode* Node = Pin->GetOwningNode();
if (Node)
{
NodeToPins.FindOrAdd(Node).Add(Pin);
}
}
MenuBuilder.AddMenuEntry( FGraphEditorCommands::Get().StraightenConnections,
NAME_None, LOCTEXT("StraightenAllConnections", "All Connected Pins"),
TAttribute<FText>(), FSlateIcon(NAME_None, NAME_None, NAME_None) );
for (auto& Pair : NodeToPins)
{
FText NodeName = Pair.Key->GetNodeTitle(ENodeTitleType::ListView);
for (UEdGraphPin* Pin : Pair.Value)
{
FText PinName = Pin->GetDisplayName();
MenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("StraightenDescription_Node", "{0} ({1})"), NodeName, Pin->GetDisplayName()),
FText::Format(LOCTEXT("StraightenDescription_Node_Tip", "Straighten the connection between this pin, and {0} ({1})"), NodeName, Pin->GetDisplayName()),
FSlateIcon(),
FExecuteAction::CreateLambda([=]{
if (const FUIAction* UIAction = MenuCommandList->GetActionForCommand(FGraphEditorCommands::Get().StraightenConnections))
{
StraightenDestinationPin = Pin;
UIAction->ExecuteAction.Execute();
}
}));
}
}
}
const FPinConnectionResponse UEdGraphSchema_K2::DetermineConnectionResponseOfCompatibleTypedPins(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UEdGraphPin* InputPin, const UEdGraphPin* OutputPin) const
{
// Now check to see if there are already connections and this is an 'exclusive' connection
const bool bBreakExistingDueToExecOutput = IsExecPin(*OutputPin) && (OutputPin->LinkedTo.Num() > 0);
const bool bBreakExistingDueToDataInput = !IsExecPin(*InputPin) && (InputPin->LinkedTo.Num() > 0);
bool bMultipleSelfException = false;
const UK2Node* OwningNode = Cast<UK2Node>(InputPin->GetOwningNode());
if (bBreakExistingDueToDataInput &&
IsSelfPin(*InputPin) &&
OwningNode &&
OwningNode->AllowMultipleSelfs(false) &&
!InputPin->PinType.IsContainer() &&
!OutputPin->PinType.IsContainer() )
{
//check if the node wont be expanded as foreach call, if there is a link to an array
bool bAnyContainerInput = false;
for(int InputLinkIndex = 0; InputLinkIndex < InputPin->LinkedTo.Num(); InputLinkIndex++)
{
if(const UEdGraphPin* Pin = InputPin->LinkedTo[InputLinkIndex])
{
if(Pin->PinType.IsContainer())
{
bAnyContainerInput = true;
break;
}
}
}
bMultipleSelfException = !bAnyContainerInput;
}
if (bBreakExistingDueToExecOutput)
{
const ECanCreateConnectionResponse ReplyBreakOutputs = (PinA == OutputPin) ? CONNECT_RESPONSE_BREAK_OTHERS_A : CONNECT_RESPONSE_BREAK_OTHERS_B;
return FPinConnectionResponse(ReplyBreakOutputs, TEXT("Replace existing output connections"));
}
else if (bBreakExistingDueToDataInput && !bMultipleSelfException)
{
const ECanCreateConnectionResponse ReplyBreakInputs = (PinA == InputPin) ? CONNECT_RESPONSE_BREAK_OTHERS_A : CONNECT_RESPONSE_BREAK_OTHERS_B;
return FPinConnectionResponse(ReplyBreakInputs, TEXT("Replace existing input connections"));
}
else
{
return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, TEXT(""));
}
}
static FText GetPinIncompatibilityReason(const UEdGraphPin* PinA, const UEdGraphPin* PinB, bool* bIsFatalOut = nullptr)
{
const FEdGraphPinType& PinAType = PinA->PinType;
const FEdGraphPinType& PinBType = PinB->PinType;
FFormatNamedArguments MessageArgs;
MessageArgs.Add(TEXT("PinAName"), PinA->GetDisplayName());
MessageArgs.Add(TEXT("PinBName"), PinB->GetDisplayName());
MessageArgs.Add(TEXT("PinAType"), UEdGraphSchema_K2::TypeToText(PinAType));
MessageArgs.Add(TEXT("PinBType"), UEdGraphSchema_K2::TypeToText(PinBType));
const UEdGraphPin* InputPin = (PinA->Direction == EGPD_Input) ? PinA : PinB;
const FEdGraphPinType& InputType = InputPin->PinType;
const UEdGraphPin* OutputPin = (InputPin == PinA) ? PinB : PinA;
const FEdGraphPinType& OutputType = OutputPin->PinType;
FText MessageFormat = LOCTEXT("DefaultPinIncompatibilityMessage", "{PinAType} is not compatible with {PinBType}.");
if (bIsFatalOut != nullptr)
{
// the incompatible pins should generate an error by default
*bIsFatalOut = true;
}
if (OutputType.PinCategory == UEdGraphSchema_K2::PC_Struct)
{
if (InputType.PinCategory == UEdGraphSchema_K2::PC_Struct)
{
MessageFormat = LOCTEXT("StructsIncompatible", "Only exactly matching structures are considered compatible.");
const UStruct* OutStruct = Cast<const UStruct>(OutputType.PinSubCategoryObject.Get());
const UStruct* InStruct = Cast<const UStruct>(InputType.PinSubCategoryObject.Get());
if ((OutStruct != nullptr) && (InStruct != nullptr) && OutStruct->IsChildOf(InStruct))
{
MessageFormat = LOCTEXT("ChildStructIncompatible", "Only exactly matching structures are considered compatible. Derived structures are disallowed.");
}
}
}
else if (OutputType.PinCategory == UEdGraphSchema_K2::PC_Class)
{
if ((InputType.PinCategory == UEdGraphSchema_K2::PC_Object) ||
(InputType.PinCategory == UEdGraphSchema_K2::PC_Interface))
{
MessageArgs.Add(TEXT("OutputName"), OutputPin->GetDisplayName());
MessageArgs.Add(TEXT("InputName"), InputPin->GetDisplayName());
MessageFormat = LOCTEXT("ClassObjectIncompatible", "'{PinAName}' and '{PinBName}' are incompatible ('{OutputName}' is an object type, and '{InputName}' is a reference to an object instance).");
if ((InputType.PinCategory == UEdGraphSchema_K2::PC_Object) && (bIsFatalOut != nullptr))
{
// under the hood class is an object, so it's not fatal
*bIsFatalOut = false;
}
}
}
else if ((OutputType.PinCategory == UEdGraphSchema_K2::PC_Object) )//|| (OutputType.PinCategory == UEdGraphSchema_K2::PC_Interface))
{
if (InputType.PinCategory == UEdGraphSchema_K2::PC_Class)
{
MessageArgs.Add(TEXT("OutputName"), OutputPin->GetDisplayName());
MessageArgs.Add(TEXT("InputName"), InputPin->GetDisplayName());
MessageArgs.Add(TEXT("InputType"), UEdGraphSchema_K2::TypeToText(InputType));
MessageFormat = LOCTEXT("CannotGetClass", "'{PinAName}' and '{PinBName}' are not inherently compatible ('{InputName}' is an object type, and '{OutputName}' is a reference to an object instance).\nWe cannot use {OutputName}'s class because it is not a child of {InputType}.");
}
}
return FText::Format(MessageFormat, MessageArgs);
}
const FPinConnectionResponse UEdGraphSchema_K2::CanCreateConnection(const UEdGraphPin* PinA, const UEdGraphPin* PinB) const
{
const UK2Node* OwningNodeA = Cast<UK2Node>(PinA->GetOwningNodeUnchecked());
const UK2Node* OwningNodeB = Cast<UK2Node>(PinB->GetOwningNodeUnchecked());
if (!OwningNodeA || !OwningNodeB)
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Invalid nodes"));
}
// Make sure the pins are not on the same node
if (OwningNodeA == OwningNodeB)
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Both are on the same node"));
}
// node can disallow the connection
{
FString RespondMessage;
if(OwningNodeA && OwningNodeA->IsConnectionDisallowed(PinA, PinB, RespondMessage))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, RespondMessage);
}
if(OwningNodeB && OwningNodeB->IsConnectionDisallowed(PinB, PinA, RespondMessage))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, RespondMessage);
}
}
// Compare the directions
const UEdGraphPin* InputPin = NULL;
const UEdGraphPin* OutputPin = NULL;
if (!CategorizePinsByDirection(PinA, PinB, /*out*/ InputPin, /*out*/ OutputPin))
{
return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, TEXT("Directions are not compatible"));
}
bool bIgnoreArray = false;
if(const UK2Node* OwningNode = Cast<UK2Node>(InputPin->GetOwningNode()))
{
const bool bAllowMultipleSelfs = OwningNode->AllowMultipleSelfs(true); // it applies also to ForEachCall
const bool bNotAnArrayFunction = !InputPin->PinType.bIsArray;
const bool bNotAMapFunction = !InputPin->PinType.bIsMap;
const bool bNotASetFunction = !InputPin->PinType.bIsSet;
const bool bSelfPin = IsSelfPin(*InputPin);
bIgnoreArray = bAllowMultipleSelfs && bNotAnArrayFunction && bNotAMapFunction && bNotASetFunction && bSelfPin;
}
// Find the calling context in case one of the pins is of type object and has a value of Self
UClass* CallingContext = NULL;
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(PinA->GetOwningNodeUnchecked());
if (Blueprint)
{
CallingContext = (Blueprint->GeneratedClass != NULL) ? Blueprint->GeneratedClass : Blueprint->ParentClass;
}
// Compare the types
const bool bTypesMatch = ArePinsCompatible(OutputPin, InputPin, CallingContext, bIgnoreArray);
if (bTypesMatch)
{
return DetermineConnectionResponseOfCompatibleTypedPins(PinA, PinB, InputPin, OutputPin);
}
else
{
// Autocasting
FName DummyName;
UClass* DummyClass;
UK2Node* DummyNode;
const bool bCanAutocast = SearchForAutocastFunction(OutputPin, InputPin, /*out*/ DummyName, DummyClass);
const bool bCanAutoConvert = FindSpecializedConversionNode(OutputPin, InputPin, false, /* out */ DummyNode);
if (bCanAutocast || bCanAutoConvert)
{
return FPinConnectionResponse(CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE, FString::Printf(TEXT("Convert %s to %s"), *TypeToText(OutputPin->PinType).ToString(), *TypeToText(InputPin->PinType).ToString()));
}
else
{
bool bIsFatal = true;
FText IncompatibilityReasonText = GetPinIncompatibilityReason(PinA, PinB, &bIsFatal);
FPinConnectionResponse ConnectionResponse(CONNECT_RESPONSE_DISALLOW, IncompatibilityReasonText.ToString());
if (bIsFatal)
{
ConnectionResponse.SetFatal();
}
return ConnectionResponse;
}
}
}
bool UEdGraphSchema_K2::TryCreateConnection(UEdGraphPin* PinA, UEdGraphPin* PinB) const
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(PinA->GetOwningNode());
bool bModified = UEdGraphSchema::TryCreateConnection(PinA, PinB);
if (bModified && !PinA->IsPendingKill())
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
return bModified;
}
struct FAutocastFunctionMap : private FNoncopyable
{
private:
static FAutocastFunctionMap* AutocastFunctionMap;
TMap<FString, TWeakObjectPtr<UFunction>> InnerMap;
FDelegateHandle OnHotReloadDelegateHandle;
FDelegateHandle OnModulesChangedDelegateHandle;
static FString GenerateTypeData(const FEdGraphPinType& PinType)
{
auto Obj = PinType.PinSubCategoryObject.Get();
FString PinSubCategory = PinType.PinSubCategory;
if (PinSubCategory.StartsWith(UEdGraphSchema_K2::PSC_Bitmask))
{
// Exclude the bitmask subcategory string from integral types so that autocast will work.
PinSubCategory = TEXT("");
}
return FString::Printf(TEXT("%s;%s;%s"), *PinType.PinCategory, *PinSubCategory, Obj ? *Obj->GetPathName() : TEXT(""));
}
static FString GenerateCastData(const FEdGraphPinType& InputPinType, const FEdGraphPinType& OutputPinType)
{
return FString::Printf(TEXT("%s;%s"), *GenerateTypeData(InputPinType), *GenerateTypeData(OutputPinType));
}
static bool IsInputParam(uint64 PropertyFlags)
{
const uint64 ConstOutParamFlag = CPF_OutParm | CPF_ConstParm;
const uint64 IsConstOut = PropertyFlags & ConstOutParamFlag;
return (CPF_Parm == (PropertyFlags & (CPF_Parm | CPF_ReturnParm)))
&& ((0 == IsConstOut) || (ConstOutParamFlag == IsConstOut));
}
static const UProperty* GetFirstInputProperty(const UFunction* Function)
{
for (auto Property : TFieldRange<const UProperty>(Function))
{
if (Property && IsInputParam(Property->PropertyFlags))
{
return Property;
}
}
return nullptr;
}
void InsertFunction(UFunction* Function, const UEdGraphSchema_K2* Schema)
{
FEdGraphPinType InputPinType;
Schema->ConvertPropertyToPinType(GetFirstInputProperty(Function), InputPinType);
FEdGraphPinType OutputPinType;
Schema->ConvertPropertyToPinType(Function->GetReturnProperty(), OutputPinType);
InnerMap.Add(GenerateCastData(InputPinType, OutputPinType), Function);
}
public:
static bool IsAutocastFunction(const UFunction* Function)
{
const FName BlueprintAutocast(TEXT("BlueprintAutocast"));
return Function
&& Function->HasMetaData(BlueprintAutocast)
&& Function->HasAllFunctionFlags(FUNC_Static | FUNC_Native | FUNC_Public | FUNC_BlueprintPure)
&& Function->GetReturnProperty()
&& GetFirstInputProperty(Function);
}
void Refresh()
{
#ifdef SCHEMA_K2_AUTOCASTFUNCTIONMAP_LOG_TIME
static_assert(false, "Macro redefinition.");
#endif
#define SCHEMA_K2_AUTOCASTFUNCTIONMAP_LOG_TIME 0
#if SCHEMA_K2_AUTOCASTFUNCTIONMAP_LOG_TIME
const auto StartTime = FPlatformTime::Seconds();
#endif //SCHEMA_K2_AUTOCASTFUNCTIONMAP_LOG_TIME
InnerMap.Empty();
auto Schema = GetDefault<UEdGraphSchema_K2>();
TArray<UClass*> Libraries;
GetDerivedClasses(UBlueprintFunctionLibrary::StaticClass(), Libraries);
for (auto Library : Libraries)
{
if (Library && (CLASS_Native == (Library->ClassFlags & (CLASS_Native | CLASS_Deprecated | CLASS_NewerVersionExists))))
{
for (auto Function : TFieldRange<UFunction>(Library, EFieldIteratorFlags::ExcludeSuper, EFieldIteratorFlags::ExcludeDeprecated))
{
if (IsAutocastFunction(Function))
{
InsertFunction(Function, Schema);
}
}
}
}
#if SCHEMA_K2_AUTOCASTFUNCTIONMAP_LOG_TIME
const auto EndTime = FPlatformTime::Seconds();
UE_LOG(LogBlueprint, Warning, TEXT("FAutocastFunctionMap::Refresh took %fs"), EndTime - StartTime);
#endif //SCHEMA_K2_AUTOCASTFUNCTIONMAP_LOG_TIME
#undef SCHEMA_K2_AUTOCASTFUNCTIONMAP_LOG_TIME
}
UFunction* Find(const FEdGraphPinType& InputPinType, const FEdGraphPinType& OutputPinType) const
{
const TWeakObjectPtr<UFunction>* FuncPtr = InnerMap.Find(GenerateCastData(InputPinType, OutputPinType));
return FuncPtr ? FuncPtr->Get() : nullptr;
}
static FAutocastFunctionMap& Get()
{
if (AutocastFunctionMap == nullptr)
{
AutocastFunctionMap = new FAutocastFunctionMap();
}
return *AutocastFunctionMap;
}
static void OnProjectHotReloaded(bool bWasTriggeredAutomatically)
{
if (AutocastFunctionMap)
{
AutocastFunctionMap->Refresh();
}
}
static void OnModulesChanged(FName ModuleThatChanged, EModuleChangeReason ReasonForChange)
{
if (AutocastFunctionMap)
{
AutocastFunctionMap->Refresh();
}
}
FAutocastFunctionMap()
{
Refresh();
IHotReloadInterface& HotReloadSupport = FModuleManager::LoadModuleChecked<IHotReloadInterface>("HotReload");
OnHotReloadDelegateHandle = HotReloadSupport.OnHotReload().AddStatic(&FAutocastFunctionMap::OnProjectHotReloaded);
OnModulesChangedDelegateHandle = FModuleManager::Get().OnModulesChanged().AddStatic(&OnModulesChanged);
}
~FAutocastFunctionMap()
{
if (auto HotReloadSupport = FModuleManager::GetModulePtr<IHotReloadInterface>("HotReload"))
{
HotReloadSupport->OnHotReload().Remove(OnHotReloadDelegateHandle);
}
FModuleManager::Get().OnModulesChanged().Remove(OnModulesChangedDelegateHandle);
}
};
FAutocastFunctionMap* FAutocastFunctionMap::AutocastFunctionMap = nullptr;
bool UEdGraphSchema_K2::SearchForAutocastFunction(const UEdGraphPin* OutputPin, const UEdGraphPin* InputPin, /*out*/ FName& TargetFunction, /*out*/ UClass*& FunctionOwner) const
{
// NOTE: Under no circumstances should anyone *ever* add a questionable cast to this function.
// If it could be at all confusing why a function is provided, to even a novice user, err on the side of do not cast!!!
// This includes things like string->int (does it do length, atoi, or what?) that would be autocasts in a traditional scripting language
TargetFunction = NAME_None;
FunctionOwner = nullptr;
if (OutputPin->PinType.bIsArray != InputPin->PinType.bIsArray ||
OutputPin->PinType.bIsMap != InputPin->PinType.bIsMap ||
OutputPin->PinType.bIsSet != InputPin->PinType.bIsSet)
{
return false;
}
// SPECIAL CASES, not supported by FAutocastFunctionMap
if ((OutputPin->PinType.PinCategory == PC_Interface) && (InputPin->PinType.PinCategory == PC_Object))
{
UClass const* InputClass = Cast<UClass const>(InputPin->PinType.PinSubCategoryObject.Get());
bool const bInputIsUObject = ((InputClass != NULL) && (InputClass == UObject::StaticClass()));
if (bInputIsUObject)
{
UFunction* Function = UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, Conv_InterfaceToObject));
TargetFunction = Function->GetFName();
FunctionOwner = Function->GetOwnerClass();
}
}
else if (OutputPin->PinType.PinCategory == PC_Object)
{
UClass const* OutputClass = Cast<UClass const>(OutputPin->PinType.PinSubCategoryObject.Get());
if (InputPin->PinType.PinCategory == PC_Class)
{
UClass const* InputClass = Cast<UClass const>(InputPin->PinType.PinSubCategoryObject.Get());
if ((OutputClass != nullptr) &&
(InputClass != nullptr) &&
OutputClass->IsChildOf(InputClass))
{
UFunction* Function = UGameplayStatics::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UGameplayStatics, GetObjectClass));
TargetFunction = Function->GetFName();
FunctionOwner = Function->GetOwnerClass();
}
}
else if (InputPin->PinType.PinCategory == PC_String)
{
UFunction* Function = UKismetSystemLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetSystemLibrary, GetDisplayName));
TargetFunction = Function->GetFName();
FunctionOwner = Function->GetOwnerClass();
}
}
else if (OutputPin->PinType.PinCategory == PC_Struct)
{
const UScriptStruct* OutputStructType = Cast<const UScriptStruct>(OutputPin->PinType.PinSubCategoryObject.Get());
if (OutputStructType == TBaseStructure<FRotator>::Get())
{
const UScriptStruct* InputStructType = Cast<const UScriptStruct>(InputPin->PinType.PinSubCategoryObject.Get());
if ((InputPin->PinType.PinCategory == PC_Struct) && (InputStructType == TBaseStructure<FTransform>::Get()))
{
UFunction* Function = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetMathLibrary, MakeTransform));
TargetFunction = Function->GetFName();
FunctionOwner = Function->GetOwnerClass();
}
}
}
if (TargetFunction == NAME_None)
{
const auto& AutocastFunctionMap = FAutocastFunctionMap::Get();
if (auto Func = AutocastFunctionMap.Find(OutputPin->PinType, InputPin->PinType))
{
TargetFunction = Func->GetFName();
FunctionOwner = Func->GetOwnerClass();
}
}
return TargetFunction != NAME_None;
}
bool UEdGraphSchema_K2::FindSpecializedConversionNode(const UEdGraphPin* OutputPin, const UEdGraphPin* InputPin, bool bCreateNode, /*out*/ UK2Node*& TargetNode) const
{
bool bCanConvert = false;
TargetNode = NULL;
// Conversion for scalar -> array
if( (!OutputPin->PinType.bIsArray && InputPin->PinType.bIsArray) && ArePinTypesCompatible(OutputPin->PinType, InputPin->PinType, NULL, true) )
{
bCanConvert = true;
if(bCreateNode)
{
TargetNode = NewObject<UK2Node_MakeArray>();
}
}
// If connecting an object to a 'call function' self pin, and not currently compatible, see if there is a property we can call a function on
else if (InputPin->GetOwningNode()->IsA(UK2Node_CallFunction::StaticClass()) && IsSelfPin(*InputPin) &&
((OutputPin->PinType.PinCategory == PC_Object) || (OutputPin->PinType.PinCategory == PC_Interface)))
{
UK2Node_CallFunction* CallFunctionNode = (UK2Node_CallFunction*)(InputPin->GetOwningNode());
UClass* OutputPinClass = Cast<UClass>(OutputPin->PinType.PinSubCategoryObject.Get());
UClass* FunctionClass = CallFunctionNode->FunctionReference.GetMemberParentClass(CallFunctionNode->GetBlueprintClassFromNode());
if(FunctionClass != NULL && OutputPinClass != NULL)
{
// Iterate over object properties..
for (TFieldIterator<UObjectProperty> PropIt(OutputPinClass, EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt)
{
UObjectProperty* ObjProp = *PropIt;
// .. if we have a blueprint visible var, and is of the type which contains this function..
if(ObjProp->HasAllPropertyFlags(CPF_BlueprintVisible) && ObjProp->PropertyClass->IsChildOf(FunctionClass))
{
// say we can convert
bCanConvert = true;
// Create 'get variable' node
if(bCreateNode)
{
UK2Node_VariableGet* GetNode = NewObject<UK2Node_VariableGet>();
GetNode->VariableReference.SetFromField<UProperty>(ObjProp, false);
TargetNode = GetNode;
}
}
}
}
}
if(!bCanConvert)
{
// CHECK ENUM TO NAME CAST
const bool bInoputMatch = InputPin && !InputPin->PinType.IsContainer() && ((PC_Name == InputPin->PinType.PinCategory) || (PC_String == InputPin->PinType.PinCategory));
const bool bOutputMatch = OutputPin && !OutputPin->PinType.IsContainer() && (PC_Byte == OutputPin->PinType.PinCategory) && (NULL != Cast<UEnum>(OutputPin->PinType.PinSubCategoryObject.Get()));
if(bOutputMatch && bInoputMatch)
{
bCanConvert = true;
if(bCreateNode)
{
check(NULL == TargetNode);
if(PC_Name == InputPin->PinType.PinCategory)
{
TargetNode = NewObject<UK2Node_GetEnumeratorName>();
}
else if(PC_String == InputPin->PinType.PinCategory)
{
TargetNode = NewObject<UK2Node_GetEnumeratorNameAsString>();
}
}
}
}
if (!bCanConvert && InputPin && OutputPin)
{
FEdGraphPinType const& InputType = InputPin->PinType;
FEdGraphPinType const& OutputType = OutputPin->PinType;
// CHECK BYTE TO ENUM CAST
UEnum* Enum = Cast<UEnum>(InputType.PinSubCategoryObject.Get());
const bool bInputIsEnum = !InputType.bIsArray && (PC_Byte == InputType.PinCategory) && Enum;
const bool bOutputIsByte = !OutputType.bIsArray && (PC_Byte == OutputType.PinCategory);
if (bInputIsEnum && bOutputIsByte)
{
bCanConvert = true;
if(bCreateNode)
{
auto CastByteToEnum = NewObject<UK2Node_CastByteToEnum>();
CastByteToEnum->Enum = Enum;
CastByteToEnum->bSafe = true;
TargetNode = CastByteToEnum;
}
}
else
{
UClass* InputClass = Cast<UClass>(InputType.PinSubCategoryObject.Get());
UClass* OutputClass = Cast<UClass>(OutputType.PinSubCategoryObject.Get());
if ((OutputType.PinCategory == PC_Interface) && (InputType.PinCategory == PC_Object))
{
bCanConvert = (InputClass && OutputClass) && (InputClass->ImplementsInterface(OutputClass) || OutputClass->IsChildOf(InputClass));
}
else if (OutputType.PinCategory == PC_Object)
{
UBlueprintEditorSettings const* BlueprintSettings = GetDefault<UBlueprintEditorSettings>();
if ((InputType.PinCategory == PC_Object) && BlueprintSettings->bAutoCastObjectConnections)
{
bCanConvert = (InputClass && OutputClass) && InputClass->IsChildOf(OutputClass);
}
}
if (bCanConvert && bCreateNode)
{
UK2Node_DynamicCast* DynCastNode = NewObject<UK2Node_DynamicCast>();
DynCastNode->TargetType = InputClass;
DynCastNode->SetPurity(true);
TargetNode = DynCastNode;
}
if (!bCanConvert && InputClass && OutputClass && OutputClass->IsChildOf(InputClass))
{
const bool bConvertAsset = (OutputType.PinCategory == PC_Asset) && (InputType.PinCategory == PC_Object);
const bool bConvertAssetClass = (OutputType.PinCategory == PC_AssetClass) && (InputType.PinCategory == PC_Class);
if (bConvertAsset || bConvertAssetClass)
{
bCanConvert = true;
if (bCreateNode)
{
UK2Node_ConvertAsset* ConvertAssetNode = NewObject<UK2Node_ConvertAsset>();
TargetNode = ConvertAssetNode;
}
}
}
}
}
return bCanConvert;
}
void UEdGraphSchema_K2::AutowireConversionNode(UEdGraphPin* InputPin, UEdGraphPin* OutputPin, UEdGraphNode* ConversionNode) const
{
bool bAllowInputConnections = true;
bool bAllowOutputConnections = true;
for (int32 PinIndex = 0; PinIndex < ConversionNode->Pins.Num(); ++PinIndex)
{
UEdGraphPin* TestPin = ConversionNode->Pins[PinIndex];
UClass* Context = nullptr;
UK2Node* K2Node = Cast<UK2Node>(OutputPin->GetOwningNode());
if (K2Node != nullptr)
{
UBlueprint* Blueprint = K2Node->GetBlueprint();
if (Blueprint)
{
Context = Blueprint->GeneratedClass;
}
}
if ((TestPin->Direction == EGPD_Input) && (ArePinTypesCompatible(OutputPin->PinType, TestPin->PinType, Context)))
{
if(bAllowOutputConnections && TryCreateConnection(TestPin, OutputPin))
{
// Successful connection, do not allow more output connections
bAllowOutputConnections = false;
}
}
else if ((TestPin->Direction == EGPD_Output) && (ArePinTypesCompatible(TestPin->PinType, InputPin->PinType, Context)))
{
if(bAllowInputConnections && TryCreateConnection(TestPin, InputPin))
{
// Successful connection, do not allow more input connections
bAllowInputConnections = false;
}
}
}
}
bool UEdGraphSchema_K2::CreateAutomaticConversionNodeAndConnections(UEdGraphPin* PinA, UEdGraphPin* PinB) const
{
// Determine which pin is an input and which pin is an output
UEdGraphPin* InputPin = NULL;
UEdGraphPin* OutputPin = NULL;
if (!CategorizePinsByDirection(PinA, PinB, /*out*/ InputPin, /*out*/ OutputPin))
{
return false;
}
FName TargetFunctionName;
UClass* ClassContainingConversionFunction = nullptr;
TSubclassOf<UK2Node> ConversionNodeClass;
UK2Node* TemplateConversionNode = NULL;
if (SearchForAutocastFunction(OutputPin, InputPin, /*out*/ TargetFunctionName, /*out*/ClassContainingConversionFunction))
{
// Create a new call function node for the casting operator
UK2Node_CallFunction* TemplateNode = NewObject<UK2Node_CallFunction>();
TemplateNode->FunctionReference.SetExternalMember(TargetFunctionName, ClassContainingConversionFunction);
//TemplateNode->bIsBeadFunction = true;
TemplateConversionNode = TemplateNode;
}
else
{
FindSpecializedConversionNode(OutputPin, InputPin, true, /*out*/ TemplateConversionNode);
}
if (TemplateConversionNode != NULL)
{
// Determine where to position the new node (assuming it isn't going to get beaded)
FVector2D AverageLocation = CalculateAveragePositionBetweenNodes(InputPin, OutputPin);
UK2Node* ConversionNode = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node>(InputPin->GetOwningNode()->GetGraph(), TemplateConversionNode, AverageLocation);
// Connect the cast node up to the output/input pins
AutowireConversionNode(InputPin, OutputPin, ConversionNode);
return true;
}
return false;
}
FString UEdGraphSchema_K2::IsPinDefaultValid(const UEdGraphPin* Pin, const FString& NewDefaultValue, UObject* NewDefaultObject, const FText& InNewDefaultText) const
{
check(Pin);
FFormatNamedArguments MessageArgs;
MessageArgs.Add(TEXT("PinName"), Pin->GetDisplayName());
const UBlueprint* OwningBP = FBlueprintEditorUtils::FindBlueprintForNode(Pin->GetOwningNodeUnchecked());
if (!OwningBP)
{
FText MsgFormat = LOCTEXT("NoBlueprintFoundForPin", "No Blueprint was found for the pin '{PinName}'.");
return FText::Format(MsgFormat, MessageArgs).ToString();
}
const bool bIsArray = Pin->PinType.bIsArray;
const bool bIsSet = Pin->PinType.bIsSet;
const bool bIsMap = Pin->PinType.bIsMap;
const bool bIsReference = Pin->PinType.bIsReference;
const bool bIsAutoCreateRefTerm = IsAutoCreateRefTerm(Pin);
if (OwningBP->BlueprintType != BPTYPE_Interface)
{
if( !bIsAutoCreateRefTerm )
{
if( bIsArray )
{
FText MsgFormat = LOCTEXT("BadArrayDefaultVal", "Array inputs (like '{PinName}') must have an input wired into them (try connecting a MakeArray node).");
return FText::Format(MsgFormat, MessageArgs).ToString();
}
else if( bIsSet )
{
FText MsgFormat = LOCTEXT("BadSetDefaultVal", "Array inputs (like '{PinName}') must have an input wired into them (try connecting a MakeSet node).");
return FText::Format(MsgFormat, MessageArgs).ToString();
}
else if ( bIsMap )
{
FText MsgFormat = LOCTEXT("BadMapDefaultVal", "Array inputs (like '{PinName}') must have an input wired into them (try connecting a MakeMap node).");
return FText::Format(MsgFormat, MessageArgs).ToString();
}
else if( bIsReference )
{
FText MsgFormat = LOCTEXT("BadRefDefaultVal", "'{PinName}' must have an input wired into it (\"by ref\" params expect a valid input to operate on).");
return FText::Format(MsgFormat, MessageArgs).ToString();
}
}
}
FString ReturnMsg;
DefaultValueSimpleValidation(Pin->PinType, Pin->PinName, NewDefaultValue, NewDefaultObject, InNewDefaultText, &ReturnMsg);
return ReturnMsg;
}
bool UEdGraphSchema_K2::DoesSupportPinWatching() const
{
return true;
}
bool UEdGraphSchema_K2::IsPinBeingWatched(UEdGraphPin const* Pin) const
{
// Note: If you crash here; it is likely that you forgot to call Blueprint->OnBlueprintChanged.Broadcast(Blueprint) to invalidate the cached UI state
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(Pin ? Pin->GetOwningNodeUnchecked() : nullptr);
return ensure(Blueprint) ? FKismetDebugUtilities::IsPinBeingWatched(Blueprint, Pin) : false;
}
void UEdGraphSchema_K2::ClearPinWatch(UEdGraphPin const* Pin) const
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(Pin->GetOwningNode());
FKismetDebugUtilities::RemovePinWatch(Blueprint, Pin);
}
FLinearColor UEdGraphSchema_K2::GetPinTypeColor(const FEdGraphPinType& PinType) const
{
const FString& TypeString = PinType.PinCategory;
const UGraphEditorSettings* Settings = GetDefault<UGraphEditorSettings>();
if (TypeString == PC_Exec)
{
return Settings->ExecutionPinTypeColor;
}
else if (TypeString == PC_Object)
{
return Settings->ObjectPinTypeColor;
}
else if (TypeString == PC_Interface)
{
return Settings->InterfacePinTypeColor;
}
else if (TypeString == PC_Float)
{
return Settings->FloatPinTypeColor;
}
else if (TypeString == PC_Boolean)
{
return Settings->BooleanPinTypeColor;
}
else if (TypeString == PC_Byte)
{
return Settings->BytePinTypeColor;
}
else if (TypeString == PC_Int)
{
return Settings->IntPinTypeColor;
}
else if (TypeString == PC_Struct)
{
if (PinType.PinSubCategoryObject == VectorStruct)
{
// vector
return Settings->VectorPinTypeColor;
}
else if (PinType.PinSubCategoryObject == RotatorStruct)
{
// rotator
return Settings->RotatorPinTypeColor;
}
else if (PinType.PinSubCategoryObject == TransformStruct)
{
// transform
return Settings->TransformPinTypeColor;
}
else
{
return Settings->StructPinTypeColor;
}
}
else if (TypeString == PC_String)
{
return Settings->StringPinTypeColor;
}
else if (TypeString == PC_Text)
{
return Settings->TextPinTypeColor;
}
else if (TypeString == PC_Wildcard)
{
if (PinType.PinSubCategory == PSC_Index)
{
return Settings->IndexPinTypeColor;
}
else
{
return Settings->WildcardPinTypeColor;
}
}
else if (TypeString == PC_Name)
{
return Settings->NamePinTypeColor;
}
else if (TypeString == PC_Asset)
{
return Settings->AssetPinTypeColor;
}
else if (TypeString == PC_AssetClass)
{
return Settings->AssetClassPinTypeColor;
}
else if (TypeString == PC_Delegate)
{
return Settings->DelegatePinTypeColor;
}
else if (TypeString == PC_Class)
{
return Settings->ClassPinTypeColor;
}
// Type does not have a defined color!
return Settings->DefaultPinTypeColor;
}
FLinearColor UEdGraphSchema_K2::GetSecondaryPinTypeColor(const FEdGraphPinType& PinType) const
{
if (PinType.bIsMap)
{
FEdGraphPinType FakePrimary = PinType;
FakePrimary.PinCategory = FakePrimary.PinValueType.TerminalCategory;
FakePrimary.PinSubCategory = FakePrimary.PinValueType.TerminalSubCategory;
FakePrimary.PinSubCategoryObject = FakePrimary.PinValueType.TerminalSubCategoryObject;
return GetPinTypeColor(FakePrimary);
}
else
{
const FString& TypeString = PinType.PinCategory;
const UGraphEditorSettings* Settings = GetDefault<UGraphEditorSettings>();
return Settings->WildcardPinTypeColor;
}
}
FText UEdGraphSchema_K2::GetPinDisplayName(const UEdGraphPin* Pin) const
{
FText DisplayName = FText::GetEmpty();
if (Pin != NULL)
{
UEdGraphNode* Node = Pin->GetOwningNode();
if (Node->ShouldOverridePinNames())
{
DisplayName = Node->GetPinNameOverride(*Pin);
}
else
{
DisplayName = Super::GetPinDisplayName(Pin);
// bit of a hack to hide 'execute' and 'then' pin names
if ((Pin->PinType.PinCategory == PC_Exec) &&
((DisplayName.ToString() == PN_Execute) || (DisplayName.ToString() == PN_Then)))
{
DisplayName = FText::GetEmpty();
}
}
if( GEditor && GetDefault<UEditorStyleSettings>()->bShowFriendlyNames )
{
DisplayName = FText::FromString(FName::NameToDisplayString(DisplayName.ToString(), Pin->PinType.PinCategory == PC_Boolean));
}
}
return DisplayName;
}
void UEdGraphSchema_K2::ConstructBasicPinTooltip(const UEdGraphPin& Pin, const FText& PinDescription, FString& TooltipOut) const
{
if (Pin.bWasTrashed)
{
return;
}
if (bGeneratingDocumentation)
{
TooltipOut = PinDescription.ToString();
}
else
{
FFormatNamedArguments Args;
Args.Add(TEXT("PinType"), TypeToText(Pin.PinType));
if (UEdGraphNode* PinNode = Pin.GetOwningNode())
{
UEdGraphSchema_K2 const* const K2Schema = Cast<const UEdGraphSchema_K2>(PinNode->GetSchema());
if (ensure(K2Schema != NULL)) // ensure that this node belongs to this schema
{
Args.Add(TEXT("DisplayName"), GetPinDisplayName(&Pin));
Args.Add(TEXT("LineFeed1"), FText::FromString(TEXT("\n")));
}
}
else
{
Args.Add(TEXT("DisplayName"), FText::GetEmpty());
Args.Add(TEXT("LineFeed1"), FText::GetEmpty());
}
if (!PinDescription.IsEmpty())
{
Args.Add(TEXT("Description"), PinDescription);
Args.Add(TEXT("LineFeed2"), FText::FromString(TEXT("\n\n")));
}
else
{
Args.Add(TEXT("Description"), FText::GetEmpty());
Args.Add(TEXT("LineFeed2"), FText::GetEmpty());
}
TooltipOut = FText::Format(LOCTEXT("PinTooltip", "{DisplayName}{LineFeed1}{PinType}{LineFeed2}{Description}"), Args).ToString();
}
}
EGraphType UEdGraphSchema_K2::GetGraphType(const UEdGraph* TestEdGraph) const
{
if (TestEdGraph)
{
//@TODO: Should there be a GT_Subgraph type?
UEdGraph* GraphToTest = const_cast<UEdGraph*>(TestEdGraph);
for (UObject* TestOuter = GraphToTest; TestOuter; TestOuter = TestOuter->GetOuter())
{
// reached up to the blueprint for the graph
if (UBlueprint* Blueprint = Cast<UBlueprint>(TestOuter))
{
if (Blueprint->BlueprintType == BPTYPE_MacroLibrary ||
Blueprint->MacroGraphs.Contains(GraphToTest))
{
return GT_Macro;
}
else if (Blueprint->UbergraphPages.Contains(GraphToTest))
{
return GT_Ubergraph;
}
else if (Blueprint->FunctionGraphs.Contains(GraphToTest))
{
return GT_Function;
}
}
else
{
GraphToTest = Cast<UEdGraph>(TestOuter);
}
}
}
return Super::GetGraphType(TestEdGraph);
}
bool UEdGraphSchema_K2::IsTitleBarPin(const UEdGraphPin& Pin) const
{
return IsExecPin(Pin);
}
void UEdGraphSchema_K2::CreateMacroGraphTerminators(UEdGraph& Graph, UClass* Class) const
{
const FName GraphName = Graph.GetFName();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(&Graph);
// Create the entry/exit tunnels
{
FGraphNodeCreator<UK2Node_Tunnel> EntryNodeCreator(Graph);
UK2Node_Tunnel* EntryNode = EntryNodeCreator.CreateNode();
EntryNode->bCanHaveOutputs = true;
EntryNodeCreator.Finalize();
SetNodeMetaData(EntryNode, FNodeMetadata::DefaultGraphNode);
}
{
FGraphNodeCreator<UK2Node_Tunnel> ExitNodeCreator(Graph);
UK2Node_Tunnel* ExitNode = ExitNodeCreator.CreateNode();
ExitNode->bCanHaveInputs = true;
ExitNode->NodePosX = 240;
ExitNodeCreator.Finalize();
SetNodeMetaData(ExitNode, FNodeMetadata::DefaultGraphNode);
}
}
void UEdGraphSchema_K2::LinkDataPinFromOutputToInput(UEdGraphNode* InOutputNode, UEdGraphNode* InInputNode) const
{
for (auto PinIter = InOutputNode->Pins.CreateIterator(); PinIter; ++PinIter)
{
UEdGraphPin* const OutputPin = *PinIter;
if ((OutputPin->Direction == EGPD_Output) && (!IsExecPin(*OutputPin)))
{
UEdGraphPin* const InputPin = InInputNode->FindPinChecked(OutputPin->PinName);
OutputPin->MakeLinkTo(InputPin);
}
}
}
void UEdGraphSchema_K2::CreateFunctionGraphTerminators(UEdGraph& Graph, UClass* Class) const
{
const FName GraphName = Graph.GetFName();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(&Graph);
check(Blueprint->BlueprintType != BPTYPE_MacroLibrary);
// Create a function entry node
FGraphNodeCreator<UK2Node_FunctionEntry> FunctionEntryCreator(Graph);
UK2Node_FunctionEntry* EntryNode = FunctionEntryCreator.CreateNode();
EntryNode->SignatureClass = Class;
EntryNode->SignatureName = GraphName;
FunctionEntryCreator.Finalize();
SetNodeMetaData(EntryNode, FNodeMetadata::DefaultGraphNode);
// See if we need to implement a return node
UFunction* InterfaceToImplement = FindField<UFunction>(Class, GraphName);
if (InterfaceToImplement)
{
// Add modifier flags from the declaration
EntryNode->AddExtraFlags(InterfaceToImplement->FunctionFlags & (FUNC_Const | FUNC_Static | FUNC_BlueprintPure));
UK2Node* NextNode = EntryNode;
UEdGraphPin* NextExec = FindExecutionPin(*EntryNode, EGPD_Output);
bool bHasParentNode = false;
// Create node for call parent function
if (((Class->GetClassFlags() & CLASS_Interface) == 0) &&
(InterfaceToImplement->FunctionFlags & FUNC_BlueprintCallable))
{
FGraphNodeCreator<UK2Node_CallParentFunction> FunctionParentCreator(Graph);
UK2Node_CallParentFunction* ParentNode = FunctionParentCreator.CreateNode();
ParentNode->SetFromFunction(InterfaceToImplement);
ParentNode->NodePosX = EntryNode->NodePosX + EntryNode->NodeWidth + 256;
ParentNode->NodePosY = EntryNode->NodePosY;
FunctionParentCreator.Finalize();
UEdGraphPin* ParentNodeExec = FindExecutionPin(*ParentNode, EGPD_Input);
// If the parent node has an execution pin, then we should as well (we're overriding them, after all)
// but perhaps this assumption is not valid in the case where a function becomes pure after being
// initially declared impure - for that reason I'm checking for validity on both ParentNodeExec and NextExec
if (ParentNodeExec && NextExec)
{
NextExec->MakeLinkTo(ParentNodeExec);
NextExec = FindExecutionPin(*ParentNode, EGPD_Output);
}
NextNode = ParentNode;
bHasParentNode = true;
}
// See if any function params are marked as out
bool bHasOutParam = false;
for( TFieldIterator<UProperty> It(InterfaceToImplement); It && (It->PropertyFlags & CPF_Parm); ++It )
{
if( It->PropertyFlags & CPF_OutParm )
{
bHasOutParam = true;
break;
}
}
if( bHasOutParam )
{
FGraphNodeCreator<UK2Node_FunctionResult> NodeCreator(Graph);
UK2Node_FunctionResult* ReturnNode = NodeCreator.CreateNode();
ReturnNode->SignatureClass = Class;
ReturnNode->SignatureName = GraphName;
ReturnNode->NodePosX = NextNode->NodePosX + NextNode->NodeWidth + 256;
ReturnNode->NodePosY = EntryNode->NodePosY;
NodeCreator.Finalize();
SetNodeMetaData(ReturnNode, FNodeMetadata::DefaultGraphNode);
// Auto-connect the pins for entry and exit, so that by default the signature is properly generated
UEdGraphPin* ResultNodeExec = FindExecutionPin(*ReturnNode, EGPD_Input);
if (ResultNodeExec && NextExec)
{
NextExec->MakeLinkTo(ResultNodeExec);
}
if (bHasParentNode)
{
LinkDataPinFromOutputToInput(NextNode, ReturnNode);
}
}
}
}
void UEdGraphSchema_K2::CreateFunctionGraphTerminators(UEdGraph& Graph, UFunction* FunctionSignature) const
{
const FName GraphName = Graph.GetFName();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(&Graph);
check(Blueprint->BlueprintType != BPTYPE_MacroLibrary);
// Create a function entry node
FGraphNodeCreator<UK2Node_FunctionEntry> FunctionEntryCreator(Graph);
UK2Node_FunctionEntry* EntryNode = FunctionEntryCreator.CreateNode();
EntryNode->SignatureClass = NULL;
EntryNode->SignatureName = GraphName;
FunctionEntryCreator.Finalize();
SetNodeMetaData(EntryNode, FNodeMetadata::DefaultGraphNode);
// We don't have a signature class to base this on permanently, because it's not an override function.
// so we need to define the pins as user defined so that they are serialized.
EntryNode->CreateUserDefinedPinsForFunctionEntryExit(FunctionSignature, /*bIsFunctionEntry=*/ true);
// See if any function params are marked as out
bool bHasOutParam = false;
for ( TFieldIterator<UProperty> It(FunctionSignature); It && ( It->PropertyFlags & CPF_Parm ); ++It )
{
if ( It->PropertyFlags & CPF_OutParm )
{
bHasOutParam = true;
break;
}
}
if ( bHasOutParam )
{
FGraphNodeCreator<UK2Node_FunctionResult> NodeCreator(Graph);
UK2Node_FunctionResult* ReturnNode = NodeCreator.CreateNode();
ReturnNode->SignatureClass = NULL;
ReturnNode->SignatureName = GraphName;
ReturnNode->NodePosX = EntryNode->NodePosX + EntryNode->NodeWidth + 256;
ReturnNode->NodePosY = EntryNode->NodePosY;
NodeCreator.Finalize();
SetNodeMetaData(ReturnNode, FNodeMetadata::DefaultGraphNode);
ReturnNode->CreateUserDefinedPinsForFunctionEntryExit(FunctionSignature, /*bIsFunctionEntry=*/ false);
// Auto-connect the pins for entry and exit, so that by default the signature is properly generated
UEdGraphPin* EntryNodeExec = FindExecutionPin(*EntryNode, EGPD_Output);
UEdGraphPin* ResultNodeExec = FindExecutionPin(*ReturnNode, EGPD_Input);
EntryNodeExec->MakeLinkTo(ResultNodeExec);
}
}
bool UEdGraphSchema_K2::GetPropertyCategoryInfo(const UProperty* TestProperty, FString& OutCategory, FString& OutSubCategory, UObject*& OutSubCategoryObject, bool& bOutIsWeakPointer)
{
if (const UInterfaceProperty* InterfaceProperty = Cast<const UInterfaceProperty>(TestProperty))
{
OutCategory = PC_Interface;
OutSubCategoryObject = InterfaceProperty->InterfaceClass;
}
else if (const UClassProperty* ClassProperty = Cast<const UClassProperty>(TestProperty))
{
OutCategory = PC_Class;
OutSubCategoryObject = ClassProperty->MetaClass;
}
else if (const UAssetClassProperty* AssetClassProperty = Cast<const UAssetClassProperty>(TestProperty))
{
OutCategory = PC_AssetClass;
OutSubCategoryObject = AssetClassProperty->MetaClass;
}
else if (const UAssetObjectProperty* AssetObjectProperty = Cast<const UAssetObjectProperty>(TestProperty))
{
OutCategory = PC_Asset;
OutSubCategoryObject = AssetObjectProperty->PropertyClass;
}
else if (const UObjectPropertyBase* ObjectProperty = Cast<const UObjectPropertyBase>(TestProperty))
{
OutCategory = PC_Object;
OutSubCategoryObject = ObjectProperty->PropertyClass;
bOutIsWeakPointer = TestProperty->IsA(UWeakObjectProperty::StaticClass());
}
else if (const UStructProperty* StructProperty = Cast<const UStructProperty>(TestProperty))
{
OutCategory = PC_Struct;
OutSubCategoryObject = StructProperty->Struct;
}
else if (Cast<const UFloatProperty>(TestProperty) != NULL)
{
OutCategory = PC_Float;
}
else if (Cast<const UIntProperty>(TestProperty) != NULL)
{
OutCategory = PC_Int;
if (TestProperty->HasMetaData(FBlueprintMetadata::MD_Bitmask))
{
OutSubCategory = PSC_Bitmask;
}
}
else if (const UByteProperty* ByteProperty = Cast<const UByteProperty>(TestProperty))
{
OutCategory = PC_Byte;
if (TestProperty->HasMetaData(FBlueprintMetadata::MD_Bitmask))
{
OutSubCategory = PSC_Bitmask;
}
else
{
OutSubCategoryObject = ByteProperty->Enum;
}
}
else if (Cast<const UNameProperty>(TestProperty) != NULL)
{
OutCategory = PC_Name;
}
else if (Cast<const UBoolProperty>(TestProperty) != NULL)
{
OutCategory = PC_Boolean;
}
else if (Cast<const UStrProperty>(TestProperty) != NULL)
{
OutCategory = PC_String;
}
else if (Cast<const UTextProperty>(TestProperty) != NULL)
{
OutCategory = PC_Text;
}
else
{
OutCategory = TEXT("bad_type");
return false;
}
return true;
}
bool UEdGraphSchema_K2::ConvertPropertyToPinType(const UProperty* Property, /*out*/ FEdGraphPinType& TypeOut) const
{
if (Property == NULL)
{
TypeOut.PinCategory = TEXT("bad_type");
return false;
}
TypeOut.PinSubCategory = TEXT("");
// Handle whether or not this is an array property
const UMapProperty* MapProperty = Cast<const UMapProperty>(Property);
const USetProperty* SetProperty = Cast<const USetProperty>(Property);
const UArrayProperty* ArrayProperty = Cast<const UArrayProperty>(Property);
const UProperty* TestProperty = Property;
if (MapProperty)
{
TestProperty = MapProperty->KeyProp;
// set up value property:
UObject* SubCategoryObject = nullptr;
bool bIsWeakPtr = false;
bool bResult = GetPropertyCategoryInfo(MapProperty->ValueProp, TypeOut.PinValueType.TerminalCategory, TypeOut.PinValueType.TerminalSubCategory, SubCategoryObject, bIsWeakPtr);
TypeOut.PinValueType.TerminalSubCategoryObject = SubCategoryObject;
if (bIsWeakPtr)
{
return false;
}
if (!bResult)
{
return false;
}
}
else if (SetProperty)
{
TestProperty = SetProperty->ElementProp;
}
else if (ArrayProperty)
{
TestProperty = ArrayProperty->Inner;
}
TypeOut.bIsMap = (MapProperty != NULL);
TypeOut.bIsSet = (SetProperty != NULL);
TypeOut.bIsArray = (ArrayProperty != NULL);
TypeOut.bIsReference = Property->HasAllPropertyFlags(CPF_OutParm|CPF_ReferenceParm);
TypeOut.bIsConst = Property->HasAllPropertyFlags(CPF_ConstParm);
// Check to see if this is the wildcard property for the target array type
UFunction* Function = Cast<UFunction>(Property->GetOuter());
if( UK2Node_CallArrayFunction::IsWildcardProperty(Function, Property)
|| UK2Node_CallFunction::IsStructureWildcardProperty(Function, Property->GetName())
|| UK2Node_CallFunction::IsWildcardProperty(Function, Property))
{
TypeOut.PinCategory = PC_Wildcard;
}
else if (const UMulticastDelegateProperty* MulticastDelegateProperty = Cast<const UMulticastDelegateProperty>(TestProperty))
{
TypeOut.PinCategory = PC_MCDelegate;
FMemberReference::FillSimpleMemberReference<UFunction>(MulticastDelegateProperty->SignatureFunction, TypeOut.PinSubCategoryMemberReference);
}
else if (const UDelegateProperty* DelegateProperty = Cast<const UDelegateProperty>(TestProperty))
{
TypeOut.PinCategory = PC_Delegate;
FMemberReference::FillSimpleMemberReference<UFunction>(DelegateProperty->SignatureFunction, TypeOut.PinSubCategoryMemberReference);
}
else
{
UObject* SubCategoryObject = nullptr;
bool bResult = GetPropertyCategoryInfo(TestProperty, TypeOut.PinCategory, TypeOut.PinSubCategory, SubCategoryObject, TypeOut.bIsWeakPointer);
TypeOut.PinSubCategoryObject = SubCategoryObject;
if (!bResult)
{
return false;
}
}
if (TypeOut.PinSubCategory == PSC_Bitmask)
{
FString BitmaskEnumName = TestProperty->GetMetaData(TEXT("BitmaskEnum"));
if(!BitmaskEnumName.IsEmpty())
{
// @TODO: Potentially replace this with a serialized UEnum reference on the UProperty (e.g. UByteProperty::Enum)
TypeOut.PinSubCategoryObject = FindObject<UEnum>(ANY_PACKAGE, *BitmaskEnumName);
}
}
return true;
}
FString UEdGraphSchema_K2::TypeToString(const FEdGraphPinType& Type)
{
return TypeToText(Type).ToString();
}
FString UEdGraphSchema_K2::TypeToString(UProperty* const Property)
{
return TypeToText(Property).ToString();
}
FText UEdGraphSchema_K2::TypeToText(UProperty* const Property)
{
if (UStructProperty* Struct = Cast<UStructProperty>(Property))
{
if (Struct->Struct)
{
FEdGraphPinType PinType;
PinType.PinCategory = PC_Struct;
PinType.PinSubCategoryObject = Struct->Struct;
return TypeToText(PinType);
}
}
else if (UClassProperty* Class = Cast<UClassProperty>(Property))
{
if (Class->MetaClass)
{
FEdGraphPinType PinType;
PinType.PinCategory = PC_Class;
PinType.PinSubCategoryObject = Class->MetaClass;
return TypeToText(PinType);
}
}
else if (UInterfaceProperty* Interface = Cast<UInterfaceProperty>(Property))
{
if (Interface->InterfaceClass != nullptr)
{
FEdGraphPinType PinType;
PinType.PinCategory = PC_Interface;
PinType.PinSubCategoryObject = Interface->InterfaceClass;
return TypeToText(PinType);
}
}
else if (UObjectPropertyBase* Obj = Cast<UObjectPropertyBase>(Property))
{
if( Obj->PropertyClass )
{
FEdGraphPinType PinType;
PinType.PinCategory = PC_Object;
PinType.PinSubCategoryObject = Obj->PropertyClass;
PinType.bIsWeakPointer = Property->IsA(UWeakObjectProperty::StaticClass());
return TypeToText(PinType);
}
return FText::GetEmpty();
}
else if (UArrayProperty* Array = Cast<UArrayProperty>(Property))
{
if (Array->Inner)
{
FFormatNamedArguments Args;
Args.Add(TEXT("ArrayType"), TypeToText(Array->Inner));
return FText::Format(LOCTEXT("ArrayPropertyText", "Array of {ArrayType}"), Args);
}
}
return FText::FromString(Property->GetClass()->GetName());
}
FText UEdGraphSchema_K2::GetCategoryText(const FString& Category, const bool bForMenu)
{
if (Category.IsEmpty())
{
return FText::GetEmpty();
}
static TMap<FString, FText> CategoryDescriptions;
if (CategoryDescriptions.Num() == 0)
{
CategoryDescriptions.Add(PC_Exec, LOCTEXT("Exec", "Exec"));
CategoryDescriptions.Add(PC_Boolean, LOCTEXT("BoolCategory","Boolean"));
CategoryDescriptions.Add(PC_Byte, LOCTEXT("ByteCategory","Byte"));
CategoryDescriptions.Add(PC_Class, LOCTEXT("ClassCategory","Class"));
CategoryDescriptions.Add(PC_Int, LOCTEXT("IntCategory","Integer"));
CategoryDescriptions.Add(PC_Float, LOCTEXT("FloatCategory","Float"));
CategoryDescriptions.Add(PC_Name, LOCTEXT("NameCategory","Name"));
CategoryDescriptions.Add(PC_Delegate, LOCTEXT("DelegateCategory","Delegate"));
CategoryDescriptions.Add(PC_MCDelegate, LOCTEXT("MulticastDelegateCategory","Multicast Delegate"));
CategoryDescriptions.Add(PC_Object, LOCTEXT("ObjectCategory","Reference"));
CategoryDescriptions.Add(PC_Interface, LOCTEXT("InterfaceCategory","Interface"));
CategoryDescriptions.Add(PC_String, LOCTEXT("StringCategory","String"));
CategoryDescriptions.Add(PC_Text, LOCTEXT("TextCategory","Text"));
CategoryDescriptions.Add(PC_Struct, LOCTEXT("StructCategory","Structure"));
CategoryDescriptions.Add(PC_Wildcard, LOCTEXT("WildcardCategory","Wildcard"));
CategoryDescriptions.Add(PC_Enum, LOCTEXT("EnumCategory","Enum"));
CategoryDescriptions.Add(PC_Asset, LOCTEXT("AssetCategory", "Asset ID"));
CategoryDescriptions.Add(PC_AssetClass, LOCTEXT("AssetClassCategory", "Class Asset ID"));
CategoryDescriptions.Add(AllObjectTypes, LOCTEXT("AllObjectTypes", "Object Types"));
}
if (bForMenu)
{
if (Category == PC_Object)
{
return LOCTEXT("ObjectCategoryForMenu", "Object Reference");
}
}
if (FText const* TypeDesc = CategoryDescriptions.Find(Category))
{
return *TypeDesc;
}
else
{
return FText::FromString(Category);
}
}
FText UEdGraphSchema_K2::TerminalTypeToText(const FString& Category, const FString& SubCategory, UObject* SubCategoryObject, bool bIsWeakPtr)
{
FText PropertyText;
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if (SubCategory != Schema->PSC_Bitmask && SubCategoryObject != NULL)
{
if (Category == Schema->PC_Byte)
{
FFormatNamedArguments Args;
Args.Add(TEXT("EnumName"), FText::FromString(SubCategoryObject->GetName()));
PropertyText = FText::Format(LOCTEXT("EnumAsText", "{EnumName} Enum"), Args);
}
else
{
FString SubCategoryObjName = SubCategoryObject->GetName();
if (UField* SubCategoryField = Cast<UField>(SubCategoryObject))
{
SubCategoryObjName = SubCategoryField->GetDisplayNameText().ToString();
}
if (!bIsWeakPtr)
{
UClass* PSCOAsClass = Cast<UClass>(SubCategoryObject);
const bool bIsInterface = PSCOAsClass && PSCOAsClass->HasAnyClassFlags(CLASS_Interface);
FFormatNamedArguments Args;
// Don't display the category for "well-known" struct types
if (Category == UEdGraphSchema_K2::PC_Struct && (SubCategoryObject == UEdGraphSchema_K2::VectorStruct || SubCategoryObject == UEdGraphSchema_K2::RotatorStruct || SubCategoryObject == UEdGraphSchema_K2::TransformStruct))
{
Args.Add(TEXT("Category"), FText::GetEmpty());
}
else
{
Args.Add(TEXT("Category"), (!bIsInterface ? UEdGraphSchema_K2::GetCategoryText(Category) : UEdGraphSchema_K2::GetCategoryText(PC_Interface)));
}
Args.Add(TEXT("ObjectName"), FText::FromString(FName::NameToDisplayString(SubCategoryObjName, /*bIsBool =*/false)));
PropertyText = FText::Format(LOCTEXT("ObjectAsText", "{ObjectName} {Category}"), Args);
}
else
{
FFormatNamedArguments Args;
Args.Add(TEXT("Category"), FText::FromString(Category));
Args.Add(TEXT("ObjectName"), FText::FromString(SubCategoryObjName));
PropertyText = FText::Format(LOCTEXT("WeakPtrAsText", "{ObjectName} Weak {Category}"), Args);
}
}
}
else if (SubCategory != TEXT(""))
{
FFormatNamedArguments Args;
Args.Add(TEXT("Category"), UEdGraphSchema_K2::GetCategoryText(Category));
Args.Add(TEXT("ObjectName"), FText::FromString(FName::NameToDisplayString(SubCategory, false)));
PropertyText = FText::Format(LOCTEXT("ObjectAsText", "{ObjectName} {Category}"), Args);
}
else
{
PropertyText = UEdGraphSchema_K2::GetCategoryText(Category);
}
return PropertyText;
}
FText UEdGraphSchema_K2::TypeToText(const FEdGraphPinType& Type)
{
FText PropertyText = TerminalTypeToText(Type.PinCategory, Type.PinSubCategory, Type.PinSubCategoryObject.Get(), Type.bIsWeakPointer);
if ( Type.bIsMap )
{
FFormatNamedArguments Args;
Args.Add(TEXT("KeyTitle"), PropertyText);
FText ValueText = TerminalTypeToText(Type.PinValueType.TerminalCategory, Type.PinValueType.TerminalSubCategory, Type.PinValueType.TerminalSubCategoryObject.Get(), Type.PinValueType.bTerminalIsWeakPointer);
Args.Add(TEXT("ValueTitle"), ValueText);
PropertyText = FText::Format(LOCTEXT("MapAsText", "Map of {PropertyTitle}s to {ValueTitle}s"), Args);
}
else if ( Type.bIsSet )
{
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyTitle"), PropertyText);
PropertyText = FText::Format(LOCTEXT("SetAsText", "Set of {PropertyTitle}s"), Args);
}
else if (Type.bIsArray)
{
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyTitle"), PropertyText);
PropertyText = FText::Format(LOCTEXT("ArrayAsText", "Array of {PropertyTitle}s"), Args);
}
else if (Type.bIsReference)
{
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyTitle"), PropertyText);
PropertyText = FText::Format(LOCTEXT("PropertyByRef", "{PropertyTitle} (by ref)"), Args);
}
return PropertyText;
}
void UEdGraphSchema_K2::GetVariableTypeTree(TArray< TSharedPtr<FPinTypeTreeInfo> >& TypeTree, ETypeTreeFilter TypeTreeFilter) const
{
bool bAllowExec = (TypeTreeFilter & ETypeTreeFilter::AllowExec) == ETypeTreeFilter::AllowExec;
bool bAllowWildCard = (TypeTreeFilter & ETypeTreeFilter::AllowWildcard) == ETypeTreeFilter::AllowWildcard;
bool bIndexTypesOnly = (TypeTreeFilter & ETypeTreeFilter::IndexTypesOnly) == ETypeTreeFilter::IndexTypesOnly;
bool bRootTypesOnly = (TypeTreeFilter & ETypeTreeFilter::RootTypesOnly) == ETypeTreeFilter::RootTypesOnly;
#ifdef SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
static_assert(false, "Macro redefinition.");
#endif
#define SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME 0
#if SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
const auto StartTime = FPlatformTime::Seconds();
#endif //SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
FTypesDatabase TypesDatabase;
FTypesDatabase* TypesDatabasePtr = nullptr;
if (!bRootTypesOnly)
{
TypesDatabasePtr = &TypesDatabase;
FGatherTypesHelper::FillLoadedTypesDatabase(TypesDatabase, bIndexTypesOnly);
}
#if SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
const auto DatabaseLoadedTime = FPlatformTime::Seconds();
#endif //SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
if (!bRootTypesOnly)
{
FGatherTypesHelper::FillUnLoadedTypesDatabase(TypesDatabase, bIndexTypesOnly);
}
#if SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
const auto DatabaseUnLoadedTime = FPlatformTime::Seconds();
#endif //SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
// Clear the list
TypeTree.Empty();
if( bAllowExec )
{
TypeTree.Add( MakeShareable( new FPinTypeTreeInfo(GetCategoryText(PC_Exec, true), PC_Exec, this, LOCTEXT("ExecType", "Execution pin")) ) );
}
TypeTree.Add( MakeShareable( new FPinTypeTreeInfo(GetCategoryText(PC_Boolean, true), PC_Boolean, this, LOCTEXT("BooleanType", "True or false value")) ) );
TypeTree.Add( MakeShareable( new FPinTypeTreeInfo(GetCategoryText(PC_Byte, true), PC_Byte, this, LOCTEXT("ByteType", "8 bit number")) ) );
TypeTree.Add( MakeShareable( new FPinTypeTreeInfo(GetCategoryText(PC_Int, true), PC_Int, this, LOCTEXT("IntegerType", "Integer number")) ) );
if (!bIndexTypesOnly)
{
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Float, true), PC_Float, this, LOCTEXT("FloatType", "Floating point number"))));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Name, true), PC_Name, this, LOCTEXT("NameType", "A text name"))));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_String, true), PC_String, this, LOCTEXT("StringType", "A text string"))));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Text, true), PC_Text, this, LOCTEXT("TextType", "A localizable text string"))));
// Add in special first-class struct types
if (!bRootTypesOnly)
{
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(PC_Struct, TBaseStructure<FVector>::Get(), LOCTEXT("VectorType", "A 3D vector"))));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(PC_Struct, TBaseStructure<FRotator>::Get(), LOCTEXT("RotatorType", "A 3D rotation"))));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(PC_Struct, TBaseStructure<FTransform>::Get(), LOCTEXT("TransformType", "A 3D transformation, including translation, rotation and 3D scale."))));
}
}
// Add wildcard type
if (bAllowWildCard)
{
TypeTree.Add( MakeShareable( new FPinTypeTreeInfo(GetCategoryText(PC_Wildcard, true), PC_Wildcard, this, LOCTEXT("WildcardType", "Wildcard type (unspecified).")) ) );
}
// Add the types that have subtrees
if (!bIndexTypesOnly)
{
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Struct, true), PC_Struct, this, LOCTEXT("StructType", "Struct (value) types."), true, TypesDatabasePtr)));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Interface, true), PC_Interface, this, LOCTEXT("InterfaceType", "Interface pointer."), true, TypesDatabasePtr)));
if (!bRootTypesOnly)
{
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(AllObjectTypes, true), AllObjectTypes, this, LOCTEXT("ObjectType", "Object pointer."), true, TypesDatabasePtr)));
}
else
{
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Object, true), PC_Object, this, LOCTEXT("ObjectType", "Object pointer."), true, TypesDatabasePtr)));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Class, true), PC_Class, this, LOCTEXT("ClassType", "Class pointer."), true, TypesDatabasePtr)));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_AssetClass, true), PC_AssetClass, this, LOCTEXT("AssetClassType", "Class ID."), true, TypesDatabasePtr)));
TypeTree.Add(MakeShareable(new FPinTypeTreeInfo(GetCategoryText(PC_Asset, true), PC_Asset, this, LOCTEXT("AssetType", "Asset ID."), true, TypesDatabasePtr)));
}
}
TypeTree.Add( MakeShareable( new FPinTypeTreeInfo(GetCategoryText(PC_Enum, true), PC_Enum, this, LOCTEXT("EnumType", "Enumeration types."), true, TypesDatabasePtr) ) );
#if SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
const auto EndTime = FPlatformTime::Seconds();
UE_LOG(LogBlueprint, Log, TEXT("UEdGraphSchema_K2::GetVariableTypeTree times - LoadedTypesDatabase: %f UnLoadedTypesDatabase: %f FPinTypeTreeInfo: %f"), DatabaseLoadedTime - StartTime, DatabaseUnLoadedTime - DatabaseLoadedTime, EndTime - DatabaseUnLoadedTime);
#endif //SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
#undef SCHEMA_K2_GETVARIABLETYPETREE_LOG_TIME
}
bool UEdGraphSchema_K2::DoesTypeHaveSubtypes(const FString& Category) const
{
return (Category == PC_Struct) || (Category == PC_Object) || (Category == PC_Asset) || (Category == PC_AssetClass) || (Category == PC_Interface) || (Category == PC_Class) || (Category == PC_Enum) || (Category == AllObjectTypes);
}
struct FWildcardArrayPinHelper
{
static bool CheckArrayCompatibility(const UEdGraphPin* OutputPin, const UEdGraphPin* InputPin, bool bIgnoreArray)
{
if (bIgnoreArray)
{
return true;
}
const UK2Node* OwningNode = InputPin ? Cast<UK2Node>(InputPin->GetOwningNode()) : NULL;
const bool bInputWildcardPinAcceptsArray = !OwningNode || OwningNode->DoesInputWildcardPinAcceptArray(InputPin);
if (bInputWildcardPinAcceptsArray)
{
return true;
}
const bool bCheckInputPin = (InputPin->PinType.PinCategory == GetDefault<UEdGraphSchema_K2>()->PC_Wildcard) && !InputPin->PinType.bIsArray;
const bool bArrayOutputPin = OutputPin && OutputPin->PinType.bIsArray;
return !(bCheckInputPin && bArrayOutputPin);
}
};
bool UEdGraphSchema_K2::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const
{
if ((PinA->Direction == EGPD_Input) && (PinB->Direction == EGPD_Output))
{
return FWildcardArrayPinHelper::CheckArrayCompatibility(PinB, PinA, bIgnoreArray)
&& ArePinTypesCompatible(PinB->PinType, PinA->PinType, CallingContext, bIgnoreArray);
}
else if ((PinB->Direction == EGPD_Input) && (PinA->Direction == EGPD_Output))
{
return FWildcardArrayPinHelper::CheckArrayCompatibility(PinA, PinB, bIgnoreArray)
&& ArePinTypesCompatible(PinA->PinType, PinB->PinType, CallingContext, bIgnoreArray);
}
else
{
return false;
}
}
namespace
{
static UClass* GetOriginalClassToFixCompatibilit(const UClass* InClass)
{
const UBlueprint* BP = InClass ? Cast<const UBlueprint>(InClass->ClassGeneratedBy) : nullptr;
return BP ? Cast<UClass>(BP->OriginalClass) : nullptr;
}
// During compilation, pins are moved around for node expansion and the Blueprints may still inherit from REINST_ classes
// which causes problems for IsChildOf. Because we do not want to modify IsChildOf we must use a separate function
// that can check to see if classes have an AuthoritativeClass that IsChildOf a Target class.
static bool IsAuthoritativeChildOf(const UStruct* InSourceStruct, const UStruct* InTargetStruct)
{
bool bResult = false;
bool bIsNonNativeClass = false;
if (UClass* SourceAsClass = const_cast<UClass*>(Cast<UClass>(InSourceStruct)))
{
if (SourceAsClass->ClassGeneratedBy)
{
// We have a non-native (Blueprint) class which means it can exist in a semi-compiled state and inherit from a REINST_ class.
bIsNonNativeClass = true;
while (SourceAsClass)
{
if (SourceAsClass->GetAuthoritativeClass() == InTargetStruct)
{
bResult = true;
break;
}
SourceAsClass = SourceAsClass->GetSuperClass();
}
}
}
// We have a native (C++) class, do a normal IsChildOf check
if (!bIsNonNativeClass)
{
bResult = InSourceStruct->IsChildOf(InTargetStruct);
}
return bResult;
}
static bool ExtendedIsChildOf(const UClass* Child, const UClass* Parent)
{
if (Child->IsChildOf(Parent))
{
return true;
}
const UClass* OriginalChild = GetOriginalClassToFixCompatibilit(Child);
if (OriginalChild && OriginalChild->IsChildOf(Parent))
{
return true;
}
const UClass* OriginalParent = GetOriginalClassToFixCompatibilit(Parent);
if (OriginalParent && Child->IsChildOf(OriginalParent))
{
return true;
}
return false;
}
static bool ExtendedImplementsInterface(const UClass* Class, const UClass* Interface)
{
if (Class->ImplementsInterface(Interface))
{
return true;
}
const UClass* OriginalClass = GetOriginalClassToFixCompatibilit(Class);
if (OriginalClass && OriginalClass->ImplementsInterface(Interface))
{
return true;
}
const UClass* OriginalInterface = GetOriginalClassToFixCompatibilit(Interface);
if (OriginalInterface && Class->ImplementsInterface(OriginalInterface))
{
return true;
}
return false;
}
};
bool UEdGraphSchema_K2::DefaultValueSimpleValidation(const FEdGraphPinType& PinType, const FString& PinName, const FString& NewDefaultValue, UObject* NewDefaultObject, const FText& InNewDefaultText, FString* OutMsg /*= NULL*/) const
{
#ifdef DVSV_RETURN_MSG
static_assert(false, "Macro redefinition.");
#endif
#define DVSV_RETURN_MSG(Str) if(NULL != OutMsg) { *OutMsg = Str; } return false;
const FString& PinCategory = PinType.PinCategory;
const FString& PinSubCategory = PinType.PinSubCategory;
const UObject* PinSubCategoryObject = PinType.PinSubCategoryObject.Get();
if (PinType.IsContainer())
{
// containers are validated separately
}
//@TODO: FCString::Atoi, FCString::Atof, and appStringToBool will 'accept' any input, but we should probably catch and warn
// about invalid input (non numeric for int/byte/float, and non 0/1 or yes/no/true/false for bool)
else if (PinCategory == PC_Boolean)
{
// All input is acceptable to some degree
}
else if (PinCategory == PC_Byte)
{
const UEnum* EnumPtr = Cast<const UEnum>(PinSubCategoryObject);
if (EnumPtr)
{
if (EnumPtr->FindEnumIndex(*NewDefaultValue) == INDEX_NONE)
{
DVSV_RETURN_MSG(FString::Printf(TEXT("'%s' is not a valid enumerant of '<%s>'"), *NewDefaultValue, *(EnumPtr->GetName())));
}
}
else if (!NewDefaultValue.IsEmpty())
{
int32 Value;
if (!FDefaultValueHelper::ParseInt(NewDefaultValue, Value))
{
DVSV_RETURN_MSG(TEXT("Expected a valid unsigned number for a byte property"));
}
if ((Value < 0) || (Value > 255))
{
DVSV_RETURN_MSG(TEXT("Expected a value between 0 and 255 for a byte property"));
}
}
}
else if ((PinCategory == PC_Class) || (PinCategory == PC_AssetClass))
{
// Should have an object set but no string
if (!NewDefaultValue.IsEmpty())
{
DVSV_RETURN_MSG(FString::Printf(TEXT("String NewDefaultValue '%s' specified on class pin '%s'"), *NewDefaultValue, *(PinName)));
}
if (NewDefaultObject == NULL)
{
// Valid self-reference or empty reference
}
else
{
// Otherwise, we expect to be able to resolve the type at least
const UClass* DefaultClassType = Cast<const UClass>(NewDefaultObject);
if (DefaultClassType == NULL)
{
DVSV_RETURN_MSG(FString::Printf(TEXT("Literal on pin %s is not a class."), *(PinName)));
}
else
{
// @TODO support PinSubCategory == 'self'
const UClass* PinClassType = Cast<const UClass>(PinSubCategoryObject);
if (PinClassType == NULL)
{
DVSV_RETURN_MSG(FString::Printf(TEXT("Failed to find class for pin %s"), *(PinName)));
}
else
{
// Have both types, make sure the specified type is a valid subtype
if (!IsAuthoritativeChildOf(DefaultClassType, PinClassType))
{
DVSV_RETURN_MSG(FString::Printf(TEXT("%s isn't a valid subclass of %s (specified on pin %s)"), *NewDefaultObject->GetPathName(), *PinClassType->GetName(), *(PinName)));
}
}
}
}
}
else if (PinCategory == PC_Float)
{
if (!NewDefaultValue.IsEmpty())
{
if (!FDefaultValueHelper::IsStringValidFloat(NewDefaultValue))
{
DVSV_RETURN_MSG(TEXT("Expected a valid number for an float property"));
}
}
}
else if (PinCategory == PC_Int)
{
if (!NewDefaultValue.IsEmpty())
{
if (!FDefaultValueHelper::IsStringValidInteger(NewDefaultValue))
{
DVSV_RETURN_MSG(TEXT("Expected a valid number for an integer property"));
}
}
}
else if (PinCategory == PC_Name)
{
// Anything is allowed
}
else if ((PinCategory == PC_Object) || (PinCategory == PC_Interface) || (PinCategory == PC_Asset))
{
if (PinSubCategoryObject == NULL && (PinSubCategory != PSC_Self))
{
DVSV_RETURN_MSG(FString::Printf(TEXT("PinSubCategoryObject on pin '%s' is NULL and PinSubCategory is '%s' not 'self'"), *(PinName), *PinSubCategory));
}
if (PinSubCategoryObject != NULL && PinSubCategory != TEXT(""))
{
DVSV_RETURN_MSG(FString::Printf(TEXT("PinSubCategoryObject on pin '%s' is non-NULL but PinSubCategory is '%s', should be empty"), *(PinName), *PinSubCategory));
}
// Should have an object set but no string - 'self' is not a valid NewDefaultValue for PC_Object pins
if (!NewDefaultValue.IsEmpty())
{
DVSV_RETURN_MSG(FString::Printf(TEXT("String NewDefaultValue '%s' specified on object pin '%s'"), *NewDefaultValue, *(PinName)));
}
// Check that the object that is set is of the correct class
const UClass* ObjectClass = Cast<const UClass>(PinSubCategoryObject);
if (NewDefaultObject != NULL && ObjectClass != NULL && !NewDefaultObject->IsA(ObjectClass))
{
DVSV_RETURN_MSG(FString::Printf(TEXT("%s isn't a %s (specified on pin %s)"), *NewDefaultObject->GetPathName(), *ObjectClass->GetName(), *(PinName)));
}
if ((PinCategory == PC_Asset) && NewDefaultObject && !NewDefaultObject->IsAsset())
{
DVSV_RETURN_MSG(FString::Printf(TEXT("%s is not an asset (specified on pin %s)"), *NewDefaultObject->GetPathName(), *(PinName)));
}
}
else if (PinCategory == PC_String)
{
// All strings are valid
}
else if (PinCategory == PC_Text)
{
// Neither of these should ever be true
if (InNewDefaultText.IsTransient())
{
DVSV_RETURN_MSG(TEXT("Invalid text literal, text is transient!"));
}
}
else if (PinCategory == PC_Struct)
{
if (PinSubCategory != TEXT(""))
{
DVSV_RETURN_MSG(FString::Printf(TEXT("Invalid PinSubCategory value '%s' (it should be empty)"), *PinSubCategory));
}
// Only FRotator and FVector properties are currently allowed to have a valid default value
const UScriptStruct* StructType = Cast<const UScriptStruct>(PinSubCategoryObject);
if (StructType == NULL)
{
//@TODO: MessageLog.Error(*FString::Printf(TEXT("Failed to find struct named %s (passed thru @@)"), *PinSubCategory), SourceObject);
DVSV_RETURN_MSG(FString::Printf(TEXT("No struct specified for pin '%s'"), *(PinName)));
}
else if (!NewDefaultValue.IsEmpty())
{
if (StructType == VectorStruct)
{
if (!FDefaultValueHelper::IsStringValidVector(NewDefaultValue))
{
DVSV_RETURN_MSG(TEXT("Invalid value for an FVector"));
}
}
else if (StructType == RotatorStruct)
{
FRotator Rot;
if (!FDefaultValueHelper::IsStringValidRotator(NewDefaultValue))
{
DVSV_RETURN_MSG(TEXT("Invalid value for an FRotator"));
}
}
else if (StructType == TransformStruct)
{
FTransform Transform;
if (!Transform.InitFromString(NewDefaultValue))
{
DVSV_RETURN_MSG(TEXT("Invalid value for an FTransform"));
}
}
else if (StructType == LinearColorStruct)
{
FLinearColor Color;
// Color form: "(R=%f,G=%f,B=%f,A=%f)"
if (!Color.InitFromString(NewDefaultValue))
{
DVSV_RETURN_MSG(TEXT("Invalid value for an FLinearColor"));
}
}
else
{
// Structs must pass validation at this point, because we need a UStructProperty to run ImportText
// They'll be verified in FKCHandler_CallFunction::CreateFunctionCallStatement()
}
}
}
else if (PinCategory == TEXT("CommentType"))
{
// Anything is allowed
}
else
{
//@TODO: MessageLog.Error(*FString::Printf(TEXT("Unsupported type %s on @@"), *UEdGraphSchema_K2::TypeToText(Type).ToString()), SourceObject);
DVSV_RETURN_MSG(FString::Printf(TEXT("Unsupported type %s on pin %s"), *UEdGraphSchema_K2::TypeToText(PinType).ToString(), *(PinName)));
}
#undef DVSV_RETURN_MSG
return true;
}
bool UEdGraphSchema_K2::ArePinTypesCompatible(const FEdGraphPinType& Output, const FEdGraphPinType& Input, const UClass* CallingContext, bool bIgnoreArray /*= false*/) const
{
if( !bIgnoreArray && ( Output.bIsMap != Input.bIsMap || Output.bIsSet != Input.bIsSet || Output.bIsArray != Input.bIsArray ) && (Input.PinCategory != PC_Wildcard || Input.IsContainer()) )
{
return false;
}
else if (Output.PinCategory == Input.PinCategory)
{
if ((Output.PinSubCategory == Input.PinSubCategory)
&& (Output.PinSubCategoryObject == Input.PinSubCategoryObject)
&& (Output.PinSubCategoryMemberReference == Input.PinSubCategoryMemberReference))
{
return true;
}
else if (Output.PinCategory == PC_Interface)
{
UClass const* OutputClass = Cast<UClass const>(Output.PinSubCategoryObject.Get());
UClass const* InputClass = Cast<UClass const>(Input.PinSubCategoryObject.Get());
if (!OutputClass || !InputClass
|| !OutputClass->IsChildOf(UInterface::StaticClass())
|| !InputClass->IsChildOf(UInterface::StaticClass()))
{
UE_LOG(LogBlueprint, Error,
TEXT("UEdGraphSchema_K2::ArePinTypesCompatible invalid interface types - OutputClass: %s, InputClass: %s, CallingContext: %s"),
*GetPathNameSafe(OutputClass), *GetPathNameSafe(InputClass), *GetPathNameSafe(CallingContext));
return false;
}
return ExtendedIsChildOf(OutputClass, InputClass);
}
else if (((Output.PinCategory == PC_Asset) && (Input.PinCategory == PC_Asset))
|| ((Output.PinCategory == PC_AssetClass) && (Input.PinCategory == PC_AssetClass)))
{
const UClass* OutputObject = (Output.PinSubCategory == PSC_Self) ? CallingContext : Cast<const UClass>(Output.PinSubCategoryObject.Get());
const UClass* InputObject = (Input.PinSubCategory == PSC_Self) ? CallingContext : Cast<const UClass>(Input.PinSubCategoryObject.Get());
if ((OutputObject != NULL) && (InputObject != NULL))
{
return ExtendedIsChildOf(OutputObject ,InputObject);
}
}
else if ((Output.PinCategory == PC_Object) || (Output.PinCategory == PC_Struct) || (Output.PinCategory == PC_Class))
{
// Subcategory mismatch, but the two could be castable
// Only allow a match if the input is a superclass of the output
UStruct const* OutputObject = (Output.PinSubCategory == PSC_Self) ? CallingContext : Cast<UStruct>(Output.PinSubCategoryObject.Get());
UStruct const* InputObject = (Input.PinSubCategory == PSC_Self) ? CallingContext : Cast<UStruct>(Input.PinSubCategoryObject.Get());
if ((OutputObject != NULL) && (InputObject != NULL))
{
if (Output.PinCategory == PC_Struct)
{
return OutputObject->IsChildOf(InputObject) && FStructUtils::TheSameLayout(OutputObject, InputObject);
}
// Special Case: Cannot mix interface and non-interface calls, because the pointer size is different under the hood
const bool bInputIsInterface = InputObject->IsChildOf(UInterface::StaticClass());
const bool bOutputIsInterface = OutputObject->IsChildOf(UInterface::StaticClass());
UClass const* OutputClass = Cast<const UClass>(OutputObject);
UClass const* InputClass = Cast<const UClass>(InputObject);
if (bInputIsInterface != bOutputIsInterface)
{
if (bInputIsInterface && (OutputClass != NULL))
{
return ExtendedImplementsInterface(OutputClass, InputClass);
}
else if (bOutputIsInterface && (InputClass != NULL))
{
return ExtendedImplementsInterface(InputClass, OutputClass);
}
}
return (IsAuthoritativeChildOf(OutputObject, InputObject) || (OutputClass && InputClass && ExtendedIsChildOf(OutputClass, InputClass)))
&& (bInputIsInterface == bOutputIsInterface);
}
}
else if ((Output.PinCategory == PC_Byte) && (Output.PinSubCategory == Input.PinSubCategory))
{
// NOTE: This allows enums to be converted to bytes. Long-term we don't want to allow that, but we need it
// for now until we have == for enums in order to be able to compare them.
if (Input.PinSubCategoryObject == NULL)
{
return true;
}
}
else if (PC_Byte == Output.PinCategory || PC_Int == Output.PinCategory)
{
// Bitmask integral types are compatible with non-bitmask integral types (of the same word size).
return Output.PinSubCategory.StartsWith(PSC_Bitmask) || Input.PinSubCategory.StartsWith(PSC_Bitmask);
}
else if (PC_Delegate == Output.PinCategory || PC_MCDelegate == Output.PinCategory)
{
auto CanUseFunction = [](const UFunction* Func) -> bool
{
return Func && (Func->HasAllFlags(RF_LoadCompleted) || !Func->HasAnyFlags(RF_NeedLoad | RF_WasLoaded));
};
const UFunction* OutFunction = FMemberReference::ResolveSimpleMemberReference<UFunction>(Output.PinSubCategoryMemberReference);
if (!CanUseFunction(OutFunction))
{
OutFunction = NULL;
}
if (!OutFunction && Output.PinSubCategoryMemberReference.GetMemberParentClass())
{
const UClass* ParentClass = Output.PinSubCategoryMemberReference.GetMemberParentClass();
const UBlueprint* BPOwner = Cast<UBlueprint>(ParentClass->ClassGeneratedBy);
if (BPOwner && BPOwner->SkeletonGeneratedClass && (BPOwner->SkeletonGeneratedClass != ParentClass))
{
OutFunction = BPOwner->SkeletonGeneratedClass->FindFunctionByName(Output.PinSubCategoryMemberReference.MemberName);
}
}
const UFunction* InFunction = FMemberReference::ResolveSimpleMemberReference<UFunction>(Input.PinSubCategoryMemberReference);
if (!CanUseFunction(InFunction))
{
InFunction = NULL;
}
if (!InFunction && Input.PinSubCategoryMemberReference.GetMemberParentClass())
{
const UClass* ParentClass = Input.PinSubCategoryMemberReference.GetMemberParentClass();
const UBlueprint* BPOwner = Cast<UBlueprint>(ParentClass->ClassGeneratedBy);
if (BPOwner && BPOwner->SkeletonGeneratedClass && (BPOwner->SkeletonGeneratedClass != ParentClass))
{
InFunction = BPOwner->SkeletonGeneratedClass->FindFunctionByName(Input.PinSubCategoryMemberReference.MemberName);
}
}
return !OutFunction || !InFunction || OutFunction->IsSignatureCompatibleWith(InFunction);
}
}
else if (Output.PinCategory == PC_Wildcard || Input.PinCategory == PC_Wildcard)
{
// If this is an Index Wildcard we have to check compatibility for indexing types
if (Output.PinSubCategory == PSC_Index)
{
return IsIndexWildcardCompatible(Input);
}
else if (Input.PinSubCategory == PSC_Index)
{
return IsIndexWildcardCompatible(Output);
}
return true;
}
else if ((Output.PinCategory == PC_Object) && (Input.PinCategory == PC_Interface))
{
UClass const* OutputClass = Cast<UClass const>(Output.PinSubCategoryObject.Get());
UClass const* InterfaceClass = Cast<UClass const>(Input.PinSubCategoryObject.Get());
if ((OutputClass == nullptr) && (Output.PinSubCategory == PSC_Self))
{
OutputClass = CallingContext;
}
return OutputClass && (ExtendedImplementsInterface(OutputClass, InterfaceClass) || ExtendedIsChildOf(OutputClass, InterfaceClass));
}
// Pins representing BLueprint objects and subclass of UObject can match when EditoronlyBP.bAllowClassAndBlueprintPinMatching=true (BaseEngine.ini)
// It's required for converting all UBlueprint references into UClass.
struct FObjClassAndBlueprintHelper
{
private:
bool bAllow;
public:
FObjClassAndBlueprintHelper() : bAllow(false)
{
GConfig->GetBool(TEXT("EditoronlyBP"), TEXT("bAllowClassAndBlueprintPinMatching"), bAllow, GEditorIni);
}
bool Match(const FEdGraphPinType& A, const FEdGraphPinType& B, const UEdGraphSchema_K2& Schema) const
{
if (bAllow && (B.PinCategory == Schema.PC_Object) && (A.PinCategory == Schema.PC_Class))
{
const bool bAIsObjectClass = (UObject::StaticClass() == A.PinSubCategoryObject.Get());
const UClass* BClass = Cast<UClass>(B.PinSubCategoryObject.Get());
const bool bBIsBlueprintObj = BClass && BClass->IsChildOf(UBlueprint::StaticClass());
return bAIsObjectClass && bBIsBlueprintObj;
}
return false;
}
};
static FObjClassAndBlueprintHelper MatchHelper;
if (MatchHelper.Match(Input, Output, *this) || MatchHelper.Match(Output, Input, *this))
{
return true;
}
return false;
}
void UEdGraphSchema_K2::BreakNodeLinks(UEdGraphNode& TargetNode) const
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(&TargetNode);
Super::BreakNodeLinks(TargetNode);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
void UEdGraphSchema_K2::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const
{
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "GraphEd_BreakPinLinks", "Break Pin Links") );
// cache this here, as BreakPinLinks can trigger a node reconstruction invalidating the TargetPin referenceS
UBlueprint* const Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(TargetPin.GetOwningNode());
Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
void UEdGraphSchema_K2::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin)
{
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "GraphEd_BreakSinglePinLink", "Break Pin Link") );
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(TargetPin->GetOwningNode());
Super::BreakSinglePinLink(SourcePin, TargetPin);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
void UEdGraphSchema_K2::ReconstructNode(UEdGraphNode& TargetNode, bool bIsBatchRequest/*=false*/) const
{
Super::ReconstructNode(TargetNode, bIsBatchRequest);
// If the reconstruction is being handled by something doing a batch (i.e. the blueprint autoregenerating itself), defer marking the blueprint as modified to prevent multiple recompiles
if (!bIsBatchRequest)
{
const UK2Node* K2Node = Cast<UK2Node>(&TargetNode);
if (K2Node && K2Node->NodeCausesStructuralBlueprintChange())
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(&TargetNode);
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
}
}
}
bool UEdGraphSchema_K2::CanEncapuslateNode(UEdGraphNode const& TestNode) const
{
// Can't encapsulate entry points (may relax this restriction in the future, but it makes sense for now)
return !TestNode.IsA(UK2Node_FunctionTerminator::StaticClass()) &&
TestNode.GetClass() != UK2Node_Tunnel::StaticClass(); //Tunnel nodes getting sucked into collapsed graphs fails badly, want to allow derived types though(composite node/Macroinstances)
}
void UEdGraphSchema_K2::HandleGraphBeingDeleted(UEdGraph& GraphBeingRemoved) const
{
if (UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(&GraphBeingRemoved))
{
// Look for collapsed graph nodes that reference this graph
TArray<UK2Node_Composite*> CompositeNodes;
FBlueprintEditorUtils::GetAllNodesOfClass<UK2Node_Composite>(Blueprint, /*out*/ CompositeNodes);
TSet<UK2Node_Composite*> NodesToDelete;
for (int32 i = 0; i < CompositeNodes.Num(); ++i)
{
UK2Node_Composite* CompositeNode = CompositeNodes[i];
if (CompositeNode->BoundGraph == &GraphBeingRemoved)
{
NodesToDelete.Add(CompositeNode);
}
}
// Delete the node that owns us
ensure(NodesToDelete.Num() <= 1);
for (TSet<UK2Node_Composite*>::TIterator It(NodesToDelete); It; ++It)
{
UK2Node_Composite* NodeToDelete = *It;
// Prevent re-entrancy here
NodeToDelete->BoundGraph = NULL;
NodeToDelete->Modify();
NodeToDelete->DestroyNode();
}
}
}
void UEdGraphSchema_K2::TrySetDefaultValue(UEdGraphPin& Pin, const FString& NewDefaultValue) const
{
FString UseDefaultValue;
UObject* UseDefaultObject = nullptr;
FText UseDefaultText;
if ((Pin.PinType.PinCategory == PC_Object)
|| (Pin.PinType.PinCategory == PC_Class)
|| (Pin.PinType.PinCategory == PC_Interface)
|| (Pin.PinType.PinCategory == PC_Asset)
|| (Pin.PinType.PinCategory == PC_AssetClass))
{
FString ObjectPathLocal = NewDefaultValue;
ConstructorHelpers::StripObjectClass(ObjectPathLocal);
UseDefaultObject = FindObject<UObject>(ANY_PACKAGE, *ObjectPathLocal);
UseDefaultValue.Empty();
}
else if(Pin.PinType.PinCategory == PC_Text)
{
FString PackageNamespace;
#if USE_STABLE_LOCALIZATION_KEYS
if (GIsEditor)
{
PackageNamespace = TextNamespaceUtil::EnsurePackageNamespace(Pin.GetOwningNodeUnchecked());
}
#endif // USE_STABLE_LOCALIZATION_KEYS
FText NewTextValue;
if (!FTextStringHelper::ReadFromString(*NewDefaultValue, NewTextValue, nullptr, *PackageNamespace))
{
NewTextValue = FText::FromString(NewDefaultValue);
}
TrySetDefaultText(Pin, NewTextValue);
UseDefaultObject = nullptr;
UseDefaultValue.Empty();
return;
}
else
{
UseDefaultObject = nullptr;
UseDefaultValue = NewDefaultValue;
}
// Check the default value and make it an error if it's bogus
if (IsPinDefaultValid(&Pin, UseDefaultValue, UseDefaultObject, UseDefaultText) == TEXT(""))
{
Pin.DefaultObject = UseDefaultObject;
Pin.DefaultValue = UseDefaultValue;
Pin.DefaultTextValue = UseDefaultText;
}
UEdGraphNode* Node = Pin.GetOwningNode();
check(Node);
Node->PinDefaultValueChanged(&Pin);
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(Node);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
void UEdGraphSchema_K2::TrySetDefaultObject(UEdGraphPin& Pin, UObject* NewDefaultObject) const
{
FText UseDefaultText;
// Check the default value and make it an error if it's bogus
if (IsPinDefaultValid(&Pin, FString(TEXT("")), NewDefaultObject, UseDefaultText) == TEXT(""))
{
Pin.DefaultObject = NewDefaultObject;
Pin.DefaultValue = NULL;
Pin.DefaultTextValue = UseDefaultText;
}
UEdGraphNode* Node = Pin.GetOwningNode();
check(Node);
Node->PinDefaultValueChanged(&Pin);
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(Node);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
void UEdGraphSchema_K2::TrySetDefaultText(UEdGraphPin& InPin, const FText& InNewDefaultText) const
{
// No reason to set the FText if it is not a PC_Text.
if(InPin.PinType.PinCategory == PC_Text)
{
// Check the default value and make it an error if it's bogus
if (IsPinDefaultValid(&InPin, TEXT(""), NULL, InNewDefaultText) == TEXT(""))
{
InPin.DefaultObject = NULL;
InPin.DefaultValue = NULL;
InPin.DefaultTextValue = InNewDefaultText;
}
UEdGraphNode* Node = InPin.GetOwningNode();
check(Node);
Node->PinDefaultValueChanged(&InPin);
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(Node);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
}
bool UEdGraphSchema_K2::IsAutoCreateRefTerm(const UEdGraphPin* Pin) const
{
check(Pin != NULL);
bool bIsAutoCreateRefTerm = false;
UEdGraphNode* OwningNode = Pin->GetOwningNode();
UK2Node_CallFunction* FuncNode = Cast<UK2Node_CallFunction>(OwningNode);
if (FuncNode)
{
UFunction* TargetFunction = FuncNode->GetTargetFunction();
if (TargetFunction && !Pin->PinName.IsEmpty())
{
TArray<FString> AutoCreateParameterNames;
GetAutoEmitTermParameters(TargetFunction, AutoCreateParameterNames);
bIsAutoCreateRefTerm = AutoCreateParameterNames.Contains(Pin->PinName);
}
}
return bIsAutoCreateRefTerm;
}
bool UEdGraphSchema_K2::ShouldHidePinDefaultValue(UEdGraphPin* Pin) const
{
check(Pin != NULL);
if (Pin->bDefaultValueIsIgnored || Pin->PinType.IsContainer() || (Pin->PinName == PN_Self && Pin->LinkedTo.Num() > 0) || (Pin->PinType.PinCategory == PC_Exec) || (Pin->PinType.bIsReference && !IsAutoCreateRefTerm(Pin)))
{
return true;
}
return false;
}
bool UEdGraphSchema_K2::ShouldShowAssetPickerForPin(UEdGraphPin* Pin) const
{
bool bShow = true;
if (Pin->PinType.PinCategory == PC_Object)
{
UClass* ObjectClass = Cast<UClass>(Pin->PinType.PinSubCategoryObject.Get());
if (ObjectClass)
{
// Don't show literal buttons for component type objects
bShow = !ObjectClass->IsChildOf(UActorComponent::StaticClass());
if (bShow && ObjectClass->IsChildOf(AActor::StaticClass()))
{
// Only show the picker for Actor classes if the class is placeable and we are in the level script
bShow = !ObjectClass->HasAllClassFlags(CLASS_NotPlaceable)
&& FBlueprintEditorUtils::IsLevelScriptBlueprint(FBlueprintEditorUtils::FindBlueprintForNode(Pin->GetOwningNode()));
}
if (bShow)
{
if (UK2Node_CallFunction* CallFunctionNode = Cast<UK2Node_CallFunction>(Pin->GetOwningNode()))
{
if ( UFunction* FunctionRef = CallFunctionNode->GetTargetFunction() )
{
const UEdGraphPin* WorldContextPin = CallFunctionNode->FindPin(FunctionRef->GetMetaData(FBlueprintMetadata::MD_WorldContext));
bShow = ( WorldContextPin != Pin );
}
}
else if (Cast<UK2Node_CreateDelegate>( Pin->GetOwningNode()))
{
bShow = false;
}
}
}
}
return bShow;
}
void UEdGraphSchema_K2::SetPinDefaultValue(UEdGraphPin* Pin, const UFunction* Function, const UProperty* Param) const
{
if ((Function != nullptr) && (Param != nullptr))
{
bool bHasAutomaticValue = false;
const FString MetadataDefaultValue = Function->GetMetaData(*Param->GetName());
if (!MetadataDefaultValue.IsEmpty())
{
// Specified default value in the metadata
Pin->AutogeneratedDefaultValue = MetadataDefaultValue;
bHasAutomaticValue = true;
}
else
{
const FName MetadataCppDefaultValueKey( *(FString(TEXT("CPP_Default_")) + Param->GetName()) );
const FString MetadataCppDefaultValue = Function->GetMetaData(MetadataCppDefaultValueKey);
if (!MetadataCppDefaultValue.IsEmpty())
{
Pin->AutogeneratedDefaultValue = MetadataCppDefaultValue;
bHasAutomaticValue = true;
}
}
if (bHasAutomaticValue)
{
if (Pin->PinType.PinCategory == PC_Text)
{
FString PackageNamespace;
#if USE_STABLE_LOCALIZATION_KEYS
if (GIsEditor)
{
PackageNamespace = TextNamespaceUtil::EnsurePackageNamespace(Pin->GetOwningNodeUnchecked());
}
#endif // USE_STABLE_LOCALIZATION_KEYS
if (!FTextStringHelper::ReadFromString(*Pin->AutogeneratedDefaultValue, Pin->DefaultTextValue, nullptr, *PackageNamespace))
{
Pin->DefaultTextValue = FText::FromString(Pin->AutogeneratedDefaultValue);
}
}
else
{
Pin->DefaultValue = Pin->AutogeneratedDefaultValue;
}
}
}
if (Pin->DefaultValue.Len() == 0)
{
// Set the default value to (T)0
SetPinDefaultValueBasedOnType(Pin);
}
}
void UEdGraphSchema_K2::SetPinDefaultValueBasedOnType(UEdGraphPin* Pin) const
{
// Create a useful default value based on the pin type
if(Pin->PinType.IsContainer() )
{
Pin->AutogeneratedDefaultValue = TEXT("");
}
else if (Pin->PinType.PinCategory == PC_Int)
{
Pin->AutogeneratedDefaultValue = TEXT("0");
}
else if(Pin->PinType.PinCategory == PC_Byte)
{
UEnum* EnumPtr = Cast<UEnum>(Pin->PinType.PinSubCategoryObject.Get());
if(EnumPtr)
{
// First element of enum can change. If the enum is { A, B, C } and the default value is A,
// the defult value should not change when enum will be changed into { N, A, B, C }
Pin->AutogeneratedDefaultValue = TEXT("");
Pin->DefaultValue = EnumPtr->GetEnumName(0);
return;
}
else
{
Pin->AutogeneratedDefaultValue = TEXT("0");
}
}
else if (Pin->PinType.PinCategory == PC_Float)
{
Pin->AutogeneratedDefaultValue = TEXT("0.0");
}
else if (Pin->PinType.PinCategory == PC_Boolean)
{
Pin->AutogeneratedDefaultValue = TEXT("false");
}
else if (Pin->PinType.PinCategory == PC_Name)
{
Pin->AutogeneratedDefaultValue = TEXT("None");
}
else if ((Pin->PinType.PinCategory == PC_Struct) && ((Pin->PinType.PinSubCategoryObject == VectorStruct) || (Pin->PinType.PinSubCategoryObject == RotatorStruct)))
{
Pin->AutogeneratedDefaultValue = TEXT("0, 0, 0");
}
else
{
Pin->AutogeneratedDefaultValue = TEXT("");
}
Pin->DefaultValue = Pin->AutogeneratedDefaultValue;
}
void UEdGraphSchema_K2::ValidateExistingConnections(UEdGraphPin* Pin)
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
const UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(Pin->GetOwningNodeUnchecked());
const UClass* CallingContext = Blueprint
? ((Blueprint->GeneratedClass != nullptr) ? Blueprint->GeneratedClass : Blueprint->ParentClass)
: nullptr;
// Break any newly invalid links
TArray<UEdGraphPin*> BrokenLinks;
for (int32 Index = 0; Index < Pin->LinkedTo.Num();)
{
UEdGraphPin* OtherPin = Pin->LinkedTo[Index];
if (K2Schema->ArePinsCompatible(Pin, OtherPin, CallingContext))
{
++Index;
}
else
{
OtherPin->LinkedTo.Remove(Pin);
Pin->LinkedTo.RemoveAtSwap(Index);
BrokenLinks.Add(OtherPin);
}
}
// Cascade the check for changed pin types
for (TArray<UEdGraphPin*>::TIterator PinIt(BrokenLinks); PinIt; ++PinIt)
{
UEdGraphPin* OtherPin = *PinIt;
OtherPin->GetOwningNode()->PinConnectionListChanged(OtherPin);
}
}
UFunction* UEdGraphSchema_K2::FindSetVariableByNameFunction(const FEdGraphPinType& PinType)
{
//!!!! Keep this function synced with FExposeOnSpawnValidator::IsSupported !!!!
struct FIsCustomStructureParamHelper
{
static bool Is(const UObject* Obj)
{
static const FName BlueprintTypeName(TEXT("BlueprintType"));
const auto Struct = Cast<const UScriptStruct>(Obj);
return Struct ? Struct->GetBoolMetaData(BlueprintTypeName) : false;
}
};
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
FName SetFunctionName = NAME_None;
if(PinType.PinCategory == K2Schema->PC_Int)
{
static FName SetIntName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetIntPropertyByName));
SetFunctionName = SetIntName;
}
else if(PinType.PinCategory == K2Schema->PC_Byte)
{
static FName SetByteName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetBytePropertyByName));
SetFunctionName = SetByteName;
}
else if(PinType.PinCategory == K2Schema->PC_Float)
{
static FName SetFloatName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetFloatPropertyByName));
SetFunctionName = SetFloatName;
}
else if(PinType.PinCategory == K2Schema->PC_Boolean)
{
static FName SetBoolName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetBoolPropertyByName));
SetFunctionName = SetBoolName;
}
else if(PinType.PinCategory == K2Schema->PC_Object)
{
static FName SetObjectName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetObjectPropertyByName));
SetFunctionName = SetObjectName;
}
else if (PinType.PinCategory == K2Schema->PC_Class)
{
static FName SetObjectName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetClassPropertyByName));
SetFunctionName = SetObjectName;
}
else if(PinType.PinCategory == K2Schema->PC_Interface)
{
static FName SetObjectName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetInterfacePropertyByName));
SetFunctionName = SetObjectName;
}
else if(PinType.PinCategory == K2Schema->PC_String)
{
static FName SetStringName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetStringPropertyByName));
SetFunctionName = SetStringName;
}
else if ( PinType.PinCategory == K2Schema->PC_Text)
{
static FName SetTextName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetTextPropertyByName));
SetFunctionName = SetTextName;
}
else if (PinType.PinCategory == K2Schema->PC_Asset)
{
static FName SetAssetName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetAssetPropertyByName));
SetFunctionName = SetAssetName;
}
else if (PinType.PinCategory == K2Schema->PC_AssetClass)
{
static FName SetAssetClassName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetAssetClassPropertyByName));
SetFunctionName = SetAssetClassName;
}
else if(PinType.PinCategory == K2Schema->PC_Name)
{
static FName SetNameName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetNamePropertyByName));
SetFunctionName = SetNameName;
}
else if(PinType.PinCategory == K2Schema->PC_Struct && PinType.PinSubCategoryObject == VectorStruct)
{
static FName SetVectorName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetVectorPropertyByName));
SetFunctionName = SetVectorName;
}
else if(PinType.PinCategory == K2Schema->PC_Struct && PinType.PinSubCategoryObject == RotatorStruct)
{
static FName SetRotatorName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetRotatorPropertyByName));
SetFunctionName = SetRotatorName;
}
else if(PinType.PinCategory == K2Schema->PC_Struct && PinType.PinSubCategoryObject == ColorStruct)
{
static FName SetLinearColorName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetLinearColorPropertyByName));
SetFunctionName = SetLinearColorName;
}
else if(PinType.PinCategory == K2Schema->PC_Struct && PinType.PinSubCategoryObject == TransformStruct)
{
static FName SetTransformName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetTransformPropertyByName));
SetFunctionName = SetTransformName;
}
else if (PinType.PinCategory == K2Schema->PC_Struct && PinType.PinSubCategoryObject == FCollisionProfileName::StaticStruct())
{
static FName SetStructureName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetCollisionProfileNameProperty));
SetFunctionName = SetStructureName;
}
else if (PinType.PinCategory == K2Schema->PC_Struct && FIsCustomStructureParamHelper::Is(PinType.PinSubCategoryObject.Get()))
{
static FName SetStructureName(GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, SetStructurePropertyByName));
SetFunctionName = SetStructureName;
}
UFunction* Function = NULL;
if(SetFunctionName != NAME_None)
{
if(PinType.bIsArray)
{
static FName SetArrayName(GET_FUNCTION_NAME_CHECKED(UKismetArrayLibrary, SetArrayPropertyByName));
Function = FindField<UFunction>(UKismetArrayLibrary::StaticClass(), SetArrayName);
}
else
{
Function = FindField<UFunction>(UKismetSystemLibrary::StaticClass(), SetFunctionName);
}
}
return Function;
}
bool UEdGraphSchema_K2::CanPromotePinToVariable( const UEdGraphPin& Pin ) const
{
const FEdGraphPinType& PinType = Pin.PinType;
bool bCanPromote = (PinType.PinCategory != PC_Wildcard && PinType.PinCategory != PC_Exec ) ? true : false;
const UK2Node* Node = Cast<UK2Node>(Pin.GetOwningNode());
const UBlueprint* OwningBlueprint = Node->GetBlueprint();
if (Pin.bNotConnectable)
{
bCanPromote = false;
}
else if (!OwningBlueprint || (OwningBlueprint->BlueprintType == BPTYPE_MacroLibrary) || (OwningBlueprint->BlueprintType == BPTYPE_FunctionLibrary) || IsStaticFunctionGraph(Node->GetGraph()))
{
// Never allow promotion in macros, because there's not a scope to define them in
bCanPromote = false;
}
else
{
if (PinType.PinCategory == PC_Delegate)
{
bCanPromote = false;
}
else if ((PinType.PinCategory == PC_Object) || (PinType.PinCategory == PC_Interface))
{
if (PinType.PinSubCategoryObject != NULL)
{
if (UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get()))
{
bCanPromote = UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Class);
}
}
}
else if ((PinType.PinCategory == PC_Struct) && (PinType.PinSubCategoryObject != NULL))
{
if (UScriptStruct* Struct = Cast<UScriptStruct>(PinType.PinSubCategoryObject.Get()))
{
bCanPromote = UEdGraphSchema_K2::IsAllowableBlueprintVariableType(Struct);
}
}
}
return bCanPromote;
}
bool UEdGraphSchema_K2::CanSplitStructPin( const UEdGraphPin& Pin ) const
{
return Pin.GetOwningNode()->CanSplitPin(&Pin) && PinHasSplittableStructType(&Pin);
}
bool UEdGraphSchema_K2::CanRecombineStructPin( const UEdGraphPin& Pin ) const
{
bool bCanRecombine = (Pin.ParentPin != NULL && Pin.LinkedTo.Num() == 0);
if (bCanRecombine)
{
// Go through all the other subpins and ensure they also are not connected to anything
TArray<UEdGraphPin*> PinsToExamine = Pin.ParentPin->SubPins;
int32 PinIndex = 0;
while (bCanRecombine && PinIndex < PinsToExamine.Num())
{
UEdGraphPin* SubPin = PinsToExamine[PinIndex];
if (SubPin->LinkedTo.Num() > 0)
{
bCanRecombine = false;
}
else if (SubPin->SubPins.Num() > 0)
{
PinsToExamine.Append(SubPin->SubPins);
}
++PinIndex;
}
}
return bCanRecombine;
}
void UEdGraphSchema_K2::GetGraphDisplayInformation(const UEdGraph& Graph, /*out*/ FGraphDisplayInfo& DisplayInfo) const
{
DisplayInfo.DocLink = TEXT("Shared/Editors/BlueprintEditor/GraphTypes");
DisplayInfo.PlainName = FText::FromString( Graph.GetName() ); // Fallback is graph name
UFunction* Function = NULL;
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(&Graph);
if (Blueprint && Blueprint->SkeletonGeneratedClass)
{
Function = Blueprint->SkeletonGeneratedClass->FindFunctionByName(Graph.GetFName());
}
const EGraphType GraphType = GetGraphType(&Graph);
if (GraphType == GT_Ubergraph)
{
DisplayInfo.DocExcerptName = TEXT("EventGraph");
if (Graph.GetFName() == GN_EventGraph)
{
// localized name for the first event graph
DisplayInfo.PlainName = LOCTEXT("GraphDisplayName_EventGraph", "EventGraph");
DisplayInfo.Tooltip = DisplayInfo.PlainName.ToString();
}
else
{
DisplayInfo.Tooltip = Graph.GetName();
}
}
else if (GraphType == GT_Function)
{
if ( Graph.GetFName() == FN_UserConstructionScript )
{
DisplayInfo.PlainName = LOCTEXT("GraphDisplayName_ConstructionScript", "ConstructionScript");
DisplayInfo.Tooltip = LOCTEXT("GraphTooltip_ConstructionScript", "Function executed when Blueprint is placed or modified.").ToString();
DisplayInfo.DocExcerptName = TEXT("ConstructionScript");
}
else
{
// If we found a function from this graph..
if (Function)
{
DisplayInfo.PlainName = FText::FromString(Function->GetName());
DisplayInfo.Tooltip = UK2Node_CallFunction::GetDefaultTooltipForFunction(Function); // grab its tooltip
}
else
{
DisplayInfo.Tooltip = Graph.GetName();
}
DisplayInfo.DocExcerptName = TEXT("FunctionGraph");
}
}
else if (GraphType == GT_Macro)
{
// Show macro description if set
FKismetUserDeclaredFunctionMetadata* MetaData = UK2Node_MacroInstance::GetAssociatedGraphMetadata(&Graph);
DisplayInfo.Tooltip = (MetaData && MetaData->ToolTip.Len() > 0) ? MetaData->ToolTip : Graph.GetName();
DisplayInfo.DocExcerptName = TEXT("MacroGraph");
}
else if (GraphType == GT_Animation)
{
DisplayInfo.PlainName = LOCTEXT("GraphDisplayName_AnimGraph", "AnimGraph");
DisplayInfo.Tooltip = LOCTEXT("GraphTooltip_AnimGraph", "Graph used to blend together different animations.").ToString();
DisplayInfo.DocExcerptName = TEXT("AnimGraph");
}
else if (GraphType == GT_StateMachine)
{
DisplayInfo.Tooltip = Graph.GetName();
DisplayInfo.DocExcerptName = TEXT("StateMachine");
}
// Add pure/static/const to notes if set
if (Function)
{
if(Function->HasAnyFunctionFlags(FUNC_BlueprintPure))
{
DisplayInfo.Notes.Add(TEXT("pure"));
}
// since 'static' is implied in a function library, not going to display it (to be consistent with previous behavior)
if(Function->HasAnyFunctionFlags(FUNC_Static) && Blueprint->BlueprintType != BPTYPE_FunctionLibrary)
{
DisplayInfo.Notes.Add(TEXT("static"));
}
else if(Function->HasAnyFunctionFlags(FUNC_Const))
{
DisplayInfo.Notes.Add(TEXT("const"));
}
}
// Mark transient graphs as obviously so
if (Graph.HasAllFlags(RF_Transient))
{
DisplayInfo.PlainName = FText::FromString( FString::Printf(TEXT("$$ %s $$"), *DisplayInfo.PlainName.ToString()) );
DisplayInfo.Notes.Add(TEXT("intermediate build product"));
}
if( GEditor && GetDefault<UEditorStyleSettings>()->bShowFriendlyNames )
{
if (GraphType == GT_Function && Function)
{
DisplayInfo.DisplayName = GetFriendlySignatureName(Function);
}
else
{
DisplayInfo.DisplayName = FText::FromString(FName::NameToDisplayString(DisplayInfo.PlainName.ToString(), false));
}
}
else
{
DisplayInfo.DisplayName = DisplayInfo.PlainName;
}
}
bool UEdGraphSchema_K2::IsSelfPin(const UEdGraphPin& Pin) const
{
return (Pin.PinName == PN_Self);
}
bool UEdGraphSchema_K2::IsDelegateCategory(const FString& Category) const
{
return (Category == PC_Delegate);
}
FVector2D UEdGraphSchema_K2::CalculateAveragePositionBetweenNodes(UEdGraphPin* InputPin, UEdGraphPin* OutputPin)
{
UEdGraphNode* InputNode = InputPin->GetOwningNode();
UEdGraphNode* OutputNode = OutputPin->GetOwningNode();
const FVector2D InputCorner(InputNode->NodePosX, InputNode->NodePosY);
const FVector2D OutputCorner(OutputNode->NodePosX, OutputNode->NodePosY);
return (InputCorner + OutputCorner) * 0.5f;
}
bool UEdGraphSchema_K2::IsConstructionScript(const UEdGraph* TestEdGraph) const
{
TArray<class UK2Node_FunctionEntry*> EntryNodes;
TestEdGraph->GetNodesOfClass<UK2Node_FunctionEntry>(EntryNodes);
bool bIsConstructionScript = false;
if (EntryNodes.Num() > 0)
{
UK2Node_FunctionEntry const* const EntryNode = EntryNodes[0];
bIsConstructionScript = (EntryNode->SignatureName == FN_UserConstructionScript);
}
return bIsConstructionScript;
}
bool UEdGraphSchema_K2::IsCompositeGraph( const UEdGraph* TestEdGraph ) const
{
check(TestEdGraph);
const EGraphType GraphType = GetGraphType(TestEdGraph);
if(GraphType == GT_Function)
{
//Find the Tunnel node for composite graph and see if its output is a composite node
for(auto I = TestEdGraph->Nodes.CreateConstIterator();I;++I)
{
UEdGraphNode* Node = *I;
if(auto Tunnel = Cast<UK2Node_Tunnel>(Node))
{
if(auto OutNode = Tunnel->OutputSourceNode)
{
if(Cast<UK2Node_Composite>(OutNode))
{
return true;
}
}
}
}
}
return false;
}
bool UEdGraphSchema_K2::IsConstFunctionGraph( const UEdGraph* TestEdGraph, bool* bOutIsEnforcingConstCorrectness ) const
{
check(TestEdGraph);
const EGraphType GraphType = GetGraphType(TestEdGraph);
if(GraphType == GT_Function)
{
// Find the entry node for the function graph and see if the 'const' flag is set
for(auto I = TestEdGraph->Nodes.CreateConstIterator(); I; ++I)
{
UEdGraphNode* Node = *I;
if(auto EntryNode = Cast<UK2Node_FunctionEntry>(Node))
{
if(bOutIsEnforcingConstCorrectness != nullptr)
{
*bOutIsEnforcingConstCorrectness = EntryNode->bEnforceConstCorrectness;
}
return (EntryNode->GetFunctionFlags() & FUNC_Const) != 0;
}
}
}
if(bOutIsEnforcingConstCorrectness != nullptr)
{
*bOutIsEnforcingConstCorrectness = false;
}
return false;
}
bool UEdGraphSchema_K2::IsStaticFunctionGraph( const UEdGraph* TestEdGraph ) const
{
check(TestEdGraph);
const auto Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TestEdGraph);
if (Blueprint && (EBlueprintType::BPTYPE_FunctionLibrary == Blueprint->BlueprintType))
{
return true;
}
const EGraphType GraphType = GetGraphType(TestEdGraph);
if(GraphType == GT_Function)
{
// Find the entry node for the function graph and see if the 'static' flag is set
for(auto I = TestEdGraph->Nodes.CreateConstIterator(); I; ++I)
{
UEdGraphNode* Node = *I;
if(auto EntryNode = Cast<UK2Node_FunctionEntry>(Node))
{
return (EntryNode->GetFunctionFlags() & FUNC_Static) != 0;
}
}
}
return false;
}
void UEdGraphSchema_K2::DroppedAssetsOnGraph(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
if ((Blueprint != NULL) && FBlueprintEditorUtils::IsActorBased(Blueprint))
{
float XOffset = 0.0f;
for(int32 AssetIdx=0; AssetIdx < Assets.Num(); AssetIdx++)
{
FVector2D Position = GraphPosition + (AssetIdx * FVector2D(XOffset, 0.0f));
UObject* Asset = Assets[AssetIdx].GetAsset();
UClass* AssetClass = Asset->GetClass();
if (UBlueprint* BlueprintAsset = Cast<UBlueprint>(Asset))
{
AssetClass = BlueprintAsset->GeneratedClass;
}
TSubclassOf<UActorComponent> DestinationComponentType = FComponentAssetBrokerage::GetPrimaryComponentForAsset(AssetClass);
if ((DestinationComponentType == NULL) && AssetClass->IsChildOf(AActor::StaticClass()))
{
DestinationComponentType = UChildActorComponent::StaticClass();
}
// Make sure we have an asset type that's registered with the component list
if (DestinationComponentType != NULL)
{
UEdGraph* TempOuter = NewObject<UEdGraph>((UObject*)Blueprint);
TempOuter->SetFlags(RF_Transient);
FComponentTypeEntry ComponentType = { FString(), FString(), DestinationComponentType };
IBlueprintNodeBinder::FBindingSet Bindings;
Bindings.Add(Asset);
UBlueprintComponentNodeSpawner::Create(ComponentType)->Invoke(Graph, Bindings, GraphPosition);
}
}
}
}
void UEdGraphSchema_K2::DroppedAssetsOnNode(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphNode* Node) const
{
// @TODO: Should dropping on component node change the component?
}
void UEdGraphSchema_K2::DroppedAssetsOnPin(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphPin* Pin) const
{
// If dropping onto an 'object' pin, try and set the literal
if ((Pin->PinType.PinCategory == PC_Object) || (Pin->PinType.PinCategory == PC_Interface))
{
UClass* PinClass = Cast<UClass>(Pin->PinType.PinSubCategoryObject.Get());
if(PinClass != NULL)
{
// Find first asset of type of the pin
UObject* Asset = FAssetData::GetFirstAssetDataOfClass(Assets, PinClass).GetAsset();
if(Asset != NULL)
{
TrySetDefaultObject(*Pin, Asset);
}
}
}
}
void UEdGraphSchema_K2::GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const
{
// No comment at the moment because this doesn't do anything
OutTooltipText = TEXT("");
OutOkIcon = false;
}
void UEdGraphSchema_K2::GetAssetsPinHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphPin* HoverPin, FString& OutTooltipText, bool& OutOkIcon) const
{
OutTooltipText = TEXT("");
OutOkIcon = false;
// If dropping onto an 'object' pin, try and set the literal
if ((HoverPin->PinType.PinCategory == PC_Object) || (HoverPin->PinType.PinCategory == PC_Interface))
{
UClass* PinClass = Cast<UClass>(HoverPin->PinType.PinSubCategoryObject.Get());
if(PinClass != NULL)
{
// Find first asset of type of the pin
FAssetData AssetData = FAssetData::GetFirstAssetDataOfClass(Assets, PinClass);
if(AssetData.IsValid())
{
OutOkIcon = true;
OutTooltipText = FString::Printf(TEXT("Assign %s to this pin"), *(AssetData.AssetName.ToString()));
}
else
{
OutOkIcon = false;
OutTooltipText = FString::Printf(TEXT("Not compatible with this pin"));
}
}
}
}
bool UEdGraphSchema_K2::FadeNodeWhenDraggingOffPin(const UEdGraphNode* Node, const UEdGraphPin* Pin) const
{
if(Node && Pin && (PC_Delegate == Pin->PinType.PinCategory) && (EGPD_Input == Pin->Direction))
{
//When dragging off a delegate pin, we should duck the alpha of all nodes except the Custom Event nodes that are compatible with the delegate signature
//This would help reinforce the connection between delegates and their matching events, and make it easier to see at a glance what could be matched up.
if(const UK2Node_Event* EventNode = Cast<const UK2Node_Event>(Node))
{
const UEdGraphPin* DelegateOutPin = EventNode->FindPin(UK2Node_Event::DelegateOutputName);
if ((NULL != DelegateOutPin) &&
(ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW != CanCreateConnection(DelegateOutPin, Pin).Response))
{
return false;
}
}
if(const UK2Node_CreateDelegate* CreateDelegateNode = Cast<const UK2Node_CreateDelegate>(Node))
{
const UEdGraphPin* DelegateOutPin = CreateDelegateNode->GetDelegateOutPin();
if ((NULL != DelegateOutPin) &&
(ECanCreateConnectionResponse::CONNECT_RESPONSE_DISALLOW != CanCreateConnection(DelegateOutPin, Pin).Response))
{
return false;
}
}
return true;
}
return false;
}
struct FBackwardCompatibilityConversionHelper
{
static bool ConvertNode(
UK2Node* OldNode,
const FString& BlueprintPinName,
UK2Node* NewNode,
const FString& ClassPinName,
const UEdGraphSchema_K2& Schema,
bool bOnlyWithDefaultBlueprint)
{
check(OldNode && NewNode);
const auto Blueprint = OldNode->GetBlueprint();
auto Graph = OldNode->GetGraph();
if (!Graph)
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error bp: '%s' node: '%s'. No graph containing the node."),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*OldNode->GetName(),
*BlueprintPinName);
return false;
}
auto OldBlueprintPin = OldNode->FindPin(BlueprintPinName);
if (!OldBlueprintPin)
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error bp: '%s' node: '%s'. No bp pin found '%s'"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*OldNode->GetName(),
*BlueprintPinName);
return false;
}
const bool bNondefaultBPConnected = (OldBlueprintPin->LinkedTo.Num() > 0);
const bool bTryConvert = !bNondefaultBPConnected || !bOnlyWithDefaultBlueprint;
if (bTryConvert)
{
// CREATE NEW NODE
NewNode->SetFlags(RF_Transactional);
Graph->AddNode(NewNode, false, false);
NewNode->CreateNewGuid();
NewNode->PostPlacedNewNode();
NewNode->AllocateDefaultPins();
NewNode->NodePosX = OldNode->NodePosX;
NewNode->NodePosY = OldNode->NodePosY;
const auto ClassPin = NewNode->FindPin(ClassPinName);
if (!ClassPin)
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error bp: '%s' node: '%s'. No class pin found '%s'"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*NewNode->GetName(),
*ClassPinName);
return false;
}
auto TargetClass = Cast<UClass>(ClassPin->PinType.PinSubCategoryObject.Get());
if (!TargetClass)
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error bp: '%s' node: '%s'. No class found '%s'"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*NewNode->GetName(),
*ClassPinName);
return false;
}
// REPLACE BLUEPRINT WITH CLASS
if (!bNondefaultBPConnected)
{
// DEFAULT VALUE
const auto UsedBlueprint = Cast<UBlueprint>(OldBlueprintPin->DefaultObject);
ensure(!OldBlueprintPin->DefaultObject || UsedBlueprint);
ensure(!UsedBlueprint || *UsedBlueprint->GeneratedClass);
UClass* UsedClass = UsedBlueprint ? *UsedBlueprint->GeneratedClass : NULL;
Schema.TrySetDefaultObject(*ClassPin, UsedClass);
if (ClassPin->DefaultObject != UsedClass)
{
auto ErrorStr = Schema.IsPinDefaultValid(ClassPin, FString(), UsedClass, FText());
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot set class' in blueprint: %s node: '%s' actor bp: %s, reason: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*OldNode->GetName(),
UsedBlueprint ? *UsedBlueprint->GetName() : TEXT("Unknown"),
ErrorStr.IsEmpty() ? TEXT("Unknown") : *ErrorStr);
return false;
}
}
else
{
// LINK
auto CastNode = NewObject<UK2Node_ClassDynamicCast>(Graph);
CastNode->SetFlags(RF_Transactional);
CastNode->TargetType = TargetClass;
Graph->AddNode(CastNode, false, false);
CastNode->CreateNewGuid();
CastNode->PostPlacedNewNode();
CastNode->AllocateDefaultPins();
const int32 OffsetOnGraph = 200;
CastNode->NodePosX = OldNode->NodePosX - OffsetOnGraph;
CastNode->NodePosY = OldNode->NodePosY;
auto ExecPin = OldNode->GetExecPin();
auto ExecCastPin = CastNode->GetExecPin();
check(ExecCastPin);
if (!ExecPin || !Schema.MovePinLinks(*ExecPin, *ExecCastPin).CanSafeConnect())
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot connect' in blueprint: %s, pin: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*ExecCastPin->PinName);
return false;
}
auto ValidCastPin = CastNode->GetValidCastPin();
check(ValidCastPin);
if (!Schema.TryCreateConnection(ValidCastPin, ExecPin))
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot connect' in blueprint: %s, pin: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*ValidCastPin->PinName);
return false;
}
auto InValidCastPin = CastNode->GetInvalidCastPin();
check(InValidCastPin);
if (!Schema.TryCreateConnection(InValidCastPin, ExecPin))
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot connect' in blueprint: %s, pin: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*InValidCastPin->PinName);
return false;
}
auto CastSourcePin = CastNode->GetCastSourcePin();
check(CastSourcePin);
if (!Schema.MovePinLinks(*OldBlueprintPin, *CastSourcePin).CanSafeConnect())
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot connect' in blueprint: %s, pin: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*CastSourcePin->PinName);
return false;
}
auto CastResultPin = CastNode->GetCastResultPin();
check(CastResultPin);
if (!Schema.TryCreateConnection(CastResultPin, ClassPin))
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot connect' in blueprint: %s, pin: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*CastResultPin->PinName);
return false;
}
}
// MOVE OTHER PINS
TArray<UEdGraphPin*> OldPins;
OldPins.Add(OldBlueprintPin);
for (auto PinIter = NewNode->Pins.CreateIterator(); PinIter; ++PinIter)
{
UEdGraphPin* const Pin = *PinIter;
check(Pin);
if (ClassPin != Pin)
{
const auto OldPin = OldNode->FindPin(Pin->PinName);
if(NULL != OldPin)
{
OldPins.Add(OldPin);
if (!Schema.MovePinLinks(*OldPin, *Pin).CanSafeConnect())
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot connect' in blueprint: %s, pin: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
*Pin->PinName);
}
}
else
{
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'missing old pin' in blueprint: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
Pin ? *Pin->PinName : TEXT("Unknown"));
}
}
}
OldNode->BreakAllNodeLinks();
for (auto PinIter = OldNode->Pins.CreateIterator(); PinIter; ++PinIter)
{
if(!OldPins.Contains(*PinIter))
{
UEdGraphPin* Pin = *PinIter;
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'missing new pin' in blueprint: %s"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"),
Pin ? *Pin->PinName : TEXT("Unknown"));
}
}
Graph->RemoveNode(OldNode);
return true;
}
return false;
}
struct FFunctionCallParams
{
const FName OldFuncName;
const FName NewFuncName;
const FString& BlueprintPinName;
const FString& ClassPinName;
const UClass* FuncScope;
FFunctionCallParams(FName InOldFunc, FName InNewFunc, const FString& InBlueprintPinName, const FString& InClassPinName, const UClass* InFuncScope)
: OldFuncName(InOldFunc), NewFuncName(InNewFunc), BlueprintPinName(InBlueprintPinName), ClassPinName(InClassPinName), FuncScope(InFuncScope)
{
check(FuncScope);
}
FFunctionCallParams(const FBlueprintCallableFunctionRedirect& FunctionRedirect)
: OldFuncName(*FunctionRedirect.OldFunctionName)
, NewFuncName(*FunctionRedirect.NewFunctionName)
, BlueprintPinName(FunctionRedirect.BlueprintParamName)
, ClassPinName(FunctionRedirect.ClassParamName)
, FuncScope(NULL)
{
FuncScope = FindObject<UClass>(ANY_PACKAGE, *FunctionRedirect.ClassName);
}
};
static void ConvertFunctionCallNodes(const FFunctionCallParams& ConversionParams, TArray<UK2Node_CallFunction*>& Nodes, UEdGraph* Graph, const UEdGraphSchema_K2& Schema, bool bOnlyWithDefaultBlueprint)
{
if (ConversionParams.FuncScope)
{
const UFunction* OldFunc = ConversionParams.FuncScope->FindFunctionByName(ConversionParams.OldFuncName);
check(OldFunc);
const UFunction* NewFunc = ConversionParams.FuncScope->FindFunctionByName(ConversionParams.NewFuncName);
check(NewFunc);
for (auto It = Nodes.CreateIterator(); It; ++It)
{
if (OldFunc == (*It)->GetTargetFunction())
{
auto NewNode = NewObject<UK2Node_CallFunction>(Graph);
NewNode->SetFromFunction(NewFunc);
ConvertNode(*It, ConversionParams.BlueprintPinName, NewNode,
ConversionParams.ClassPinName, Schema, bOnlyWithDefaultBlueprint);
}
}
}
}
};
void UEdGraphSchema_K2::BackwardCompatibilityNodeConversion(UEdGraph* Graph, bool bOnlySafeChanges) const
{
if (Graph)
{
{
static const FString BlueprintPinName(TEXT("Blueprint"));
static const FString ClassPinName(TEXT("Class"));
TArray<UK2Node_SpawnActor*> SpawnActorNodes;
Graph->GetNodesOfClass(SpawnActorNodes);
for (auto It = SpawnActorNodes.CreateIterator(); It; ++It)
{
FBackwardCompatibilityConversionHelper::ConvertNode(
*It, BlueprintPinName, NewObject<UK2Node_SpawnActorFromClass>(Graph),
ClassPinName, *this, bOnlySafeChanges);
}
}
{
auto Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
if (Blueprint && *Blueprint->SkeletonGeneratedClass)
{
TArray<UK2Node_CallFunction*> Nodes;
Graph->GetNodesOfClass(Nodes);
for (const auto& FunctionRedirect : EditoronlyBPFunctionRedirects)
{
FBackwardCompatibilityConversionHelper::ConvertFunctionCallNodes(
FBackwardCompatibilityConversionHelper::FFunctionCallParams(FunctionRedirect),
Nodes, Graph, *this, bOnlySafeChanges);
}
}
else
{
UE_LOG(LogBlueprint, Log, TEXT("BackwardCompatibilityNodeConversion: Blueprint '%s' cannot be fully converted. It has no skeleton class!"),
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"));
}
}
/** Fix the old Make/Break Vector, Make/Break Vector 2D, and Make/Break Rotator nodes to use the native function call versions */
TArray<UK2Node_MakeStruct*> MakeStructNodes;
Graph->GetNodesOfClass<UK2Node_MakeStruct>(MakeStructNodes);
for(auto It = MakeStructNodes.CreateIterator(); It; ++It)
{
UK2Node_MakeStruct* OldMakeStructNode = *It;
check(NULL != OldMakeStructNode);
// user may have since deleted the struct type
if (OldMakeStructNode->StructType == NULL)
{
continue;
}
// Check to see if the struct has a native make/break that we should try to convert to.
if (OldMakeStructNode->StructType && OldMakeStructNode->StructType->HasMetaData(TEXT("HasNativeMake")))
{
UFunction* MakeNodeFunction = NULL;
// If any pins need to change their names during the conversion, add them to the map.
TMap<FString, FString> OldPinToNewPinMap;
if(OldMakeStructNode->StructType->GetName() == TEXT("Rotator"))
{
MakeNodeFunction = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, MakeRotator));
OldPinToNewPinMap.Add(TEXT("Rotator"), TEXT("ReturnValue"));
}
else if(OldMakeStructNode->StructType->GetName() == TEXT("Vector"))
{
MakeNodeFunction = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, MakeVector));
OldPinToNewPinMap.Add(TEXT("Vector"), TEXT("ReturnValue"));
}
else if(OldMakeStructNode->StructType->GetName() == TEXT("Vector2D"))
{
MakeNodeFunction = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, MakeVector2D));
OldPinToNewPinMap.Add(TEXT("Vector2D"), TEXT("ReturnValue"));
}
if(MakeNodeFunction)
{
UK2Node_CallFunction* CallFunctionNode = NewObject<UK2Node_CallFunction>(Graph);
check(CallFunctionNode);
CallFunctionNode->SetFlags(RF_Transactional);
Graph->AddNode(CallFunctionNode, false, false);
CallFunctionNode->SetFromFunction(MakeNodeFunction);
CallFunctionNode->CreateNewGuid();
CallFunctionNode->PostPlacedNewNode();
CallFunctionNode->AllocateDefaultPins();
CallFunctionNode->NodePosX = OldMakeStructNode->NodePosX;
CallFunctionNode->NodePosY = OldMakeStructNode->NodePosY;
for(int32 PinIdx = 0; PinIdx < OldMakeStructNode->Pins.Num(); ++PinIdx)
{
UEdGraphPin* OldPin = OldMakeStructNode->Pins[PinIdx];
UEdGraphPin* NewPin = NULL;
// Check to see if the pin name is mapped to a new one, if it is use it, otherwise just search for the pin under the old name
FString* NewPinNamePtr = OldPinToNewPinMap.Find(OldPin->PinName);
if(NewPinNamePtr)
{
NewPin = CallFunctionNode->FindPin(*NewPinNamePtr);
}
else
{
NewPin = CallFunctionNode->FindPin(OldPin->PinName);
}
check(NewPin);
if(!Graph->GetSchema()->MovePinLinks(*OldPin, *NewPin).CanSafeConnect())
{
const UBlueprint* Blueprint = OldMakeStructNode->GetBlueprint();
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot safetly move pin %s to %s' in blueprint: %s"),
*OldPin->PinName,
*NewPin->PinName,
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"));
}
}
OldMakeStructNode->DestroyNode();
}
}
}
TArray<UK2Node_BreakStruct*> BreakStructNodes;
Graph->GetNodesOfClass<UK2Node_BreakStruct>(BreakStructNodes);
for(auto It = BreakStructNodes.CreateIterator(); It; ++It)
{
UK2Node_BreakStruct* OldBreakStructNode = *It;
check(NULL != OldBreakStructNode);
// user may have since deleted the struct type
if (OldBreakStructNode->StructType == NULL)
{
continue;
}
// Check to see if the struct has a native make/break that we should try to convert to.
if (OldBreakStructNode->StructType && OldBreakStructNode->StructType->HasMetaData(TEXT("HasNativeBreak")))
{
UFunction* BreakNodeFunction = NULL;
// If any pins need to change their names during the conversion, add them to the map.
TMap<FString, FString> OldPinToNewPinMap;
if(OldBreakStructNode->StructType->GetName() == TEXT("Rotator"))
{
BreakNodeFunction = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, BreakRotator));
OldPinToNewPinMap.Add(TEXT("Rotator"), TEXT("InRot"));
}
else if(OldBreakStructNode->StructType->GetName() == TEXT("Vector"))
{
BreakNodeFunction = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, BreakVector));
OldPinToNewPinMap.Add(TEXT("Vector"), TEXT("InVec"));
}
else if(OldBreakStructNode->StructType->GetName() == TEXT("Vector2D"))
{
BreakNodeFunction = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, BreakVector2D));
OldPinToNewPinMap.Add(TEXT("Vector2D"), TEXT("InVec"));
}
if(BreakNodeFunction)
{
UK2Node_CallFunction* CallFunctionNode = NewObject<UK2Node_CallFunction>(Graph);
check(CallFunctionNode);
CallFunctionNode->SetFlags(RF_Transactional);
Graph->AddNode(CallFunctionNode, false, false);
CallFunctionNode->SetFromFunction(BreakNodeFunction);
CallFunctionNode->CreateNewGuid();
CallFunctionNode->PostPlacedNewNode();
CallFunctionNode->AllocateDefaultPins();
CallFunctionNode->NodePosX = OldBreakStructNode->NodePosX;
CallFunctionNode->NodePosY = OldBreakStructNode->NodePosY;
for(int32 PinIdx = 0; PinIdx < OldBreakStructNode->Pins.Num(); ++PinIdx)
{
UEdGraphPin* OldPin = OldBreakStructNode->Pins[PinIdx];
UEdGraphPin* NewPin = NULL;
// Check to see if the pin name is mapped to a new one, if it is use it, otherwise just search for the pin under the old name
FString* NewPinNamePtr = OldPinToNewPinMap.Find(OldPin->PinName);
if(NewPinNamePtr)
{
NewPin = CallFunctionNode->FindPin(*NewPinNamePtr);
}
else
{
NewPin = CallFunctionNode->FindPin(OldPin->PinName);
}
check(NewPin);
if(!Graph->GetSchema()->MovePinLinks(*OldPin, *NewPin).CanSafeConnect())
{
const UBlueprint* Blueprint = OldBreakStructNode->GetBlueprint();
UE_LOG(LogBlueprint, Warning, TEXT("BackwardCompatibilityNodeConversion Error 'cannot safetly move pin %s to %s' in blueprint: %s"),
*OldPin->PinName,
*NewPin->PinName,
Blueprint ? *Blueprint->GetName() : TEXT("Unknown"));
}
}
OldBreakStructNode->DestroyNode();
}
}
}
if (Graph->GetLinker() == nullptr)
{
return;
}
}
}
UEdGraphNode* UEdGraphSchema_K2::CreateSubstituteNode(UEdGraphNode* Node, const UEdGraph* Graph, FObjectInstancingGraph* InstanceGraph, TArray<FName>& InOutExtraNames) const
{
// If this is an event node, create a unique custom event node as a substitute
UK2Node_Event* EventNode = Cast<UK2Node_Event>(Node);
if(EventNode)
{
if(!Graph)
{
// Use the node's graph (outer) if an explicit graph was not specified
Graph = Node->GetGraph();
}
// Can only place events in ubergraphs
if (GetGraphType(Graph) != EGraphType::GT_Ubergraph)
{
return NULL;
}
// Find the Blueprint that owns the graph
UBlueprint* Blueprint = Graph ? FBlueprintEditorUtils::FindBlueprintForGraph(Graph) : NULL;
if(Blueprint && Blueprint->SkeletonGeneratedClass)
{
// Gather all names in use by the Blueprint class
TArray<FName> ExistingNamesInUse = InOutExtraNames;
FBlueprintEditorUtils::GetFunctionNameList(Blueprint, ExistingNamesInUse);
FBlueprintEditorUtils::GetClassVariableList(Blueprint, ExistingNamesInUse);
const ERenameFlags RenameFlags = (Blueprint->bIsRegeneratingOnLoad ? REN_ForceNoResetLoaders : 0);
// Allow the old object name to be used in the graph
FName ObjName = EventNode->GetFName();
UObject* Found = FindObject<UObject>(EventNode->GetOuter(), *ObjName.ToString());
if(Found)
{
Found->Rename(NULL, NULL, REN_DontCreateRedirectors | RenameFlags | (Found->HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects) ? REN_ForceNoResetLoaders : RF_NoFlags));
}
// Create a custom event node to replace the original event node imported from text
UK2Node_CustomEvent* CustomEventNode = NewObject<UK2Node_CustomEvent>(EventNode->GetOuter(), ObjName, EventNode->GetFlags(), nullptr, true, InstanceGraph);
// Ensure that it is editable
CustomEventNode->bIsEditable = true;
// Set grid position to match that of the target node
CustomEventNode->NodePosX = EventNode->NodePosX;
CustomEventNode->NodePosY = EventNode->NodePosY;
// Build a function name that is appropriate for the event we're replacing
FString FunctionName;
const UK2Node_ActorBoundEvent* ActorBoundEventNode = Cast<const UK2Node_ActorBoundEvent>(EventNode);
const UK2Node_ComponentBoundEvent* CompBoundEventNode = Cast<const UK2Node_ComponentBoundEvent>(EventNode);
const UEdGraphNode* PreExistingNode = nullptr;
if (InstanceGraph)
{
// Use a generic name for the new custom event
FunctionName = TEXT("CustomEvent");
}
else
{
// Create a name for the custom event based off the original function
if (ActorBoundEventNode)
{
FString TargetName = TEXT("None");
if (ActorBoundEventNode->EventOwner)
{
TargetName = ActorBoundEventNode->EventOwner->GetActorLabel();
}
FunctionName = FString::Printf(TEXT("%s_%s"), *ActorBoundEventNode->DelegatePropertyName.ToString(), *TargetName);
PreExistingNode = FKismetEditorUtilities::FindBoundEventForActor(ActorBoundEventNode->GetReferencedLevelActor(), ActorBoundEventNode->DelegatePropertyName);
}
else if (CompBoundEventNode)
{
FunctionName = FString::Printf(TEXT("%s_%s"), *CompBoundEventNode->DelegatePropertyName.ToString(), *CompBoundEventNode->ComponentPropertyName.ToString());
PreExistingNode = FKismetEditorUtilities::FindBoundEventForComponent(Blueprint, CompBoundEventNode->DelegatePropertyName, CompBoundEventNode->ComponentPropertyName);
}
else if (EventNode->CustomFunctionName != NAME_None)
{
FunctionName = EventNode->CustomFunctionName.ToString();
}
else if (EventNode->bOverrideFunction)
{
FunctionName = EventNode->EventReference.GetMemberName().ToString();
}
else
{
FunctionName = CustomEventNode->GetName().Replace(TEXT("K2Node_"), TEXT(""), ESearchCase::CaseSensitive);
}
}
// Ensure the name does not overlap with other names
CustomEventNode->CustomFunctionName = FName(*FunctionName, FNAME_Find);
if (CustomEventNode->CustomFunctionName != NAME_None
&& ExistingNamesInUse.Contains(CustomEventNode->CustomFunctionName))
{
int32 i = 0;
FString TempFuncName;
do
{
TempFuncName = FString::Printf(TEXT("%s_%d"), *FunctionName, ++i);
CustomEventNode->CustomFunctionName = FName(*TempFuncName, FNAME_Find);
} while (CustomEventNode->CustomFunctionName != NAME_None
&& ExistingNamesInUse.Contains(CustomEventNode->CustomFunctionName));
FunctionName = TempFuncName;
}
if (ActorBoundEventNode)
{
PreExistingNode = FKismetEditorUtilities::FindBoundEventForActor(ActorBoundEventNode->GetReferencedLevelActor(), ActorBoundEventNode->DelegatePropertyName);
}
else if (CompBoundEventNode)
{
PreExistingNode = FKismetEditorUtilities::FindBoundEventForComponent(Blueprint, CompBoundEventNode->DelegatePropertyName, CompBoundEventNode->ComponentPropertyName);
}
else
{
if (Cast<UK2Node_CustomEvent>(EventNode))
{
PreExistingNode = FBlueprintEditorUtils::FindCustomEventNode(Blueprint, EventNode->CustomFunctionName);
}
else
{
UFunction* EventSignature = EventNode->FindEventSignatureFunction();
if (ensure(EventSignature))
{
// EventNode::FindEventSignatureFunction will return null if it is deleted (for instance, someone declared a
// BlueprintImplementableEvent, and some blueprint implements it, but then the declaration is deleted)
UClass* ClassOwner = EventSignature->GetOwnerClass();
if (ensureMsgf(ClassOwner, TEXT("Wrong class owner of signature %s in node %s"), *GetPathNameSafe(EventSignature), *GetPathNameSafe(EventNode)))
{
PreExistingNode = FBlueprintEditorUtils::FindOverrideForFunction(Blueprint, ClassOwner->GetAuthoritativeClass(), EventSignature->GetFName());
}
}
}
}
// Should be a unique name now, go ahead and assign it
CustomEventNode->CustomFunctionName = FName(*FunctionName);
InOutExtraNames.Add(CustomEventNode->CustomFunctionName);
// Copy the pins from the old node to the new one that's replacing it
CustomEventNode->Pins = EventNode->Pins;
CustomEventNode->UserDefinedPins = EventNode->UserDefinedPins;
// Clear out the pins from the old node so that links aren't broken later when it's destroyed
EventNode->Pins.Empty();
EventNode->UserDefinedPins.Empty();
bool bOriginalWasCustomEvent = Cast<UK2Node_CustomEvent>(Node) != nullptr;
// Fixup pins
for(int32 PinIndex = 0; PinIndex < CustomEventNode->Pins.Num(); ++PinIndex)
{
UEdGraphPin* Pin = CustomEventNode->Pins[PinIndex];
check(Pin);
// Reparent the pin to the new custom event node
Pin->SetOwningNode(CustomEventNode);
// Don't include execution or delegate output pins as user-defined pins
if(!bOriginalWasCustomEvent && !IsExecPin(*Pin) && !IsDelegateCategory(Pin->PinType.PinCategory))
{
// Check to see if this pin already exists as a user-defined pin on the custom event node
bool bFoundUserDefinedPin = false;
for(int32 UserDefinedPinIndex = 0; UserDefinedPinIndex < CustomEventNode->UserDefinedPins.Num() && !bFoundUserDefinedPin; ++UserDefinedPinIndex)
{
const FUserPinInfo& UserDefinedPinInfo = *CustomEventNode->UserDefinedPins[UserDefinedPinIndex].Get();
bFoundUserDefinedPin = Pin->PinName == UserDefinedPinInfo.PinName && Pin->PinType == UserDefinedPinInfo.PinType;
}
if(!bFoundUserDefinedPin)
{
// Add a new entry into the user-defined pin array for the custom event node
TSharedPtr<FUserPinInfo> UserPinInfo = MakeShareable(new FUserPinInfo());
UserPinInfo->PinName = Pin->PinName;
UserPinInfo->PinType = Pin->PinType;
CustomEventNode->UserDefinedPins.Add(UserPinInfo);
}
}
}
if (PreExistingNode)
{
if (!Blueprint->PreCompileLog.IsValid())
{
Blueprint->PreCompileLog = TSharedPtr<FCompilerResultsLog>(new FCompilerResultsLog(false));
Blueprint->PreCompileLog->bSilentMode = false;
Blueprint->PreCompileLog->bAnnotateMentionedNodes = false;
}
// Append a warning to the node and to the logs
CustomEventNode->bHasCompilerMessage = true;
CustomEventNode->ErrorType = EMessageSeverity::Warning;
FFormatNamedArguments Args;
Args.Add(TEXT("NodeName"), CustomEventNode->GetNodeTitle(ENodeTitleType::ListView));
Args.Add(TEXT("OriginalNodeName"), FText::FromString(PreExistingNode->GetName()));
CustomEventNode->ErrorMsg = FText::Format(LOCTEXT("ReverseUpgradeWarning", "Conflicted with {OriginalNodeName} and was replaced as a Custom Event!"), Args).ToString();
Blueprint->PreCompileLog->Warning(*LOCTEXT("ReverseUpgradeWarning_Log", "Pasted node @@ conflicted with @@ and was replaced as a Custom Event!").ToString(), CustomEventNode, PreExistingNode);
}
// Return the new custom event node that we just created as a substitute for the original event node
return CustomEventNode;
}
}
// Use the default logic in all other cases
return UEdGraphSchema::CreateSubstituteNode(Node, Graph, InstanceGraph, InOutExtraNames);
}
int32 UEdGraphSchema_K2::GetNodeSelectionCount(const UEdGraph* Graph) const
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
int32 SelectionCount = 0;
if( Blueprint )
{
SelectionCount = FKismetEditorUtilities::GetNumberOfSelectedNodes(Blueprint);
}
return SelectionCount;
}
TSharedPtr<FEdGraphSchemaAction> UEdGraphSchema_K2::GetCreateCommentAction() const
{
return TSharedPtr<FEdGraphSchemaAction>(static_cast<FEdGraphSchemaAction*>(new FEdGraphSchemaAction_K2AddComment));
}
bool UEdGraphSchema_K2::CanDuplicateGraph(UEdGraph* InSourceGraph) const
{
EGraphType GraphType = GetGraphType(InSourceGraph);
return GraphType == GT_Function || GraphType == GT_Macro;
}
UEdGraph* UEdGraphSchema_K2::DuplicateGraph(UEdGraph* GraphToDuplicate) const
{
UEdGraph* NewGraph = NULL;
if (CanDuplicateGraph(GraphToDuplicate))
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(GraphToDuplicate);
NewGraph = FEdGraphUtilities::CloneGraph(GraphToDuplicate, Blueprint);
if (NewGraph)
{
bool bIsOverrideGraph = false;
if (Blueprint->BlueprintType == BPTYPE_Interface)
{
bIsOverrideGraph = true;
}
else if (FBlueprintEditorUtils::FindFunctionInImplementedInterfaces(Blueprint, GraphToDuplicate->GetFName()))
{
bIsOverrideGraph = true;
}
else if (FindField<UFunction>(Blueprint->ParentClass, GraphToDuplicate->GetFName()))
{
bIsOverrideGraph = true;
}
// When duplicating an override function, we must put the graph through some extra work to properly own the data being duplicated, instead of expecting pin information will come from a parent
if (bIsOverrideGraph)
{
FBlueprintEditorUtils::PromoteGraphFromInterfaceOverride(Blueprint, NewGraph);
// Remove all calls to the parent function, fix any exec pin links to pass through
TArray< UK2Node_CallParentFunction* > ParentFunctionCalls;
NewGraph->GetNodesOfClass(ParentFunctionCalls);
for (UK2Node_CallParentFunction* ParentFunctionCall : ParentFunctionCalls)
{
UEdGraphPin* ExecPin = ParentFunctionCall->GetExecPin();
UEdGraphPin* ThenPin = ParentFunctionCall->GetThenPin();
if (ExecPin->LinkedTo.Num() && ThenPin->LinkedTo.Num())
{
MovePinLinks(*ExecPin, *ThenPin->LinkedTo[0]);
}
NewGraph->RemoveNode(ParentFunctionCall);
}
}
FName NewGraphName = FBlueprintEditorUtils::FindUniqueKismetName(Blueprint, GraphToDuplicate->GetFName().GetPlainNameString());
FEdGraphUtilities::RenameGraphCloseToName(NewGraph,NewGraphName.ToString());
// can't have two graphs with the same guid... that'd be silly!
NewGraph->GraphGuid = FGuid::NewGuid();
//Rename the entry node or any further renames will not update the entry node, also fixes a duplicate node issue on compile
for (int32 NodeIndex = 0; NodeIndex < NewGraph->Nodes.Num(); ++NodeIndex)
{
UEdGraphNode* Node = NewGraph->Nodes[NodeIndex];
if (UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(Node))
{
if (EntryNode->SignatureName == GraphToDuplicate->GetFName())
{
EntryNode->Modify();
EntryNode->SignatureName = NewGraph->GetFName();
break;
}
}
// Rename any custom events to be unique
else if (Node->GetClass()->GetFName() == TEXT("K2Node_CustomEvent"))
{
UK2Node_CustomEvent* CustomEvent = Cast<UK2Node_CustomEvent>(Node);
CustomEvent->RenameCustomEventCloseToName();
}
}
// Potentially adjust variable names for any child blueprints
FBlueprintEditorUtils::ValidateBlueprintChildVariables(Blueprint, NewGraph->GetFName());
}
}
return NewGraph;
}
/**
* Attempts to best-guess the height of the node. This is necessary because we don't know the actual
* size of the node until the next Slate tick
*
* @param Node The node to guess the height of
* @return The estimated height of the specified node
*/
float UEdGraphSchema_K2::EstimateNodeHeight( UEdGraphNode* Node )
{
float HeightEstimate = 0.0f;
if ( Node != NULL )
{
float BaseNodeHeight = 48.0f;
bool bConsiderNodePins = false;
float HeightPerPin = 18.0f;
if ( Node->IsA( UK2Node_CallFunction::StaticClass() ) )
{
BaseNodeHeight = 80.0f;
bConsiderNodePins = true;
HeightPerPin = 18.0f;
}
else if ( Node->IsA( UK2Node_Event::StaticClass() ) )
{
BaseNodeHeight = 48.0f;
bConsiderNodePins = true;
HeightPerPin = 16.0f;
}
HeightEstimate = BaseNodeHeight;
if ( bConsiderNodePins )
{
int32 NumInputPins = 0;
int32 NumOutputPins = 0;
for ( int32 PinIndex = 0; PinIndex < Node->Pins.Num(); PinIndex++ )
{
UEdGraphPin* CurrentPin = Node->Pins[PinIndex];
if ( CurrentPin != NULL && !CurrentPin->bHidden )
{
switch ( CurrentPin->Direction )
{
case EGPD_Input:
{
NumInputPins++;
}
break;
case EGPD_Output:
{
NumOutputPins++;
}
break;
}
}
}
float MaxNumPins = float(FMath::Max<int32>( NumInputPins, NumOutputPins ));
HeightEstimate += MaxNumPins * HeightPerPin;
}
}
return HeightEstimate;
}
bool UEdGraphSchema_K2::CollapseGatewayNode(UK2Node* InNode, UEdGraphNode* InEntryNode, UEdGraphNode* InResultNode, FKismetCompilerContext* CompilerContext) const
{
bool bSuccessful = true;
// We iterate the array in reverse so we can both remove the subpins safely after we've read them and
// so we have split nested structs we combine them back together in the right order
for (int32 BoundaryPinIndex = InNode->Pins.Num() - 1; BoundaryPinIndex >= 0; --BoundaryPinIndex)
{
UEdGraphPin* const BoundaryPin = InNode->Pins[BoundaryPinIndex];
bool bFunctionNode = InNode->IsA(UK2Node_CallFunction::StaticClass());
// For each pin in the gateway node, find the associated pin in the entry or result node.
UEdGraphNode* const GatewayNode = (BoundaryPin->Direction == EGPD_Input) ? InEntryNode : InResultNode;
UEdGraphPin* GatewayPin = NULL;
if (GatewayNode)
{
// First handle struct combining if necessary
if (BoundaryPin->SubPins.Num() > 0)
{
InNode->ExpandSplitPin(CompilerContext, InNode->GetGraph(), BoundaryPin);
}
for (int32 PinIdx = GatewayNode->Pins.Num() - 1; PinIdx >= 0; --PinIdx)
{
UEdGraphPin* const Pin = GatewayNode->Pins[PinIdx];
// Expand any gateway pins as needed
if (Pin->SubPins.Num() > 0)
{
InNode->ExpandSplitPin(CompilerContext, GatewayNode->GetGraph(), Pin);
}
// Function graphs have a single exec path through them, so only one exec pin for input and another for output. In this fashion, they must not be handled by name.
if(InNode->GetClass() == UK2Node_CallFunction::StaticClass() && Pin->PinType.PinCategory == PC_Exec && BoundaryPin->PinType.PinCategory == PC_Exec && (Pin->Direction != BoundaryPin->Direction))
{
GatewayPin = Pin;
break;
}
else if ((Pin->PinName == BoundaryPin->PinName) && (Pin->Direction != BoundaryPin->Direction))
{
GatewayPin = Pin;
break;
}
}
}
if (GatewayPin)
{
CombineTwoPinNetsAndRemoveOldPins(BoundaryPin, GatewayPin);
}
else
{
if (BoundaryPin->LinkedTo.Num() > 0 && BoundaryPin->ParentPin == NULL)
{
UBlueprint* OwningBP = InNode->GetBlueprint();
if( OwningBP )
{
// We had an input/output with a connection that wasn't twinned
bSuccessful = false;
OwningBP->Message_Warn( FString::Printf(*NSLOCTEXT("K2Node", "PinOnBoundryNode_Warning", "Warning: Pin '%s' on boundary node '%s' could not be found in the composite node '%s'").ToString(),
*(BoundaryPin->PinName),
(GatewayNode != NULL) ? *(GatewayNode->GetName()) : TEXT("(null)"),
*(GetName()))
);
}
else
{
UE_LOG(LogBlueprint, Warning, TEXT("%s"), *FString::Printf(*NSLOCTEXT("K2Node", "PinOnBoundryNode_Warning", "Warning: Pin '%s' on boundary node '%s' could not be found in the composite node '%s'").ToString(),
*(BoundaryPin->PinName),
(GatewayNode != NULL) ? *(GatewayNode->GetName()) : TEXT("(null)"),
*(GetName()))
);
}
}
else
{
// Associated pin was not found but there were no links on this side either, so no harm no foul
}
}
}
return bSuccessful;
}
void UEdGraphSchema_K2::CombineTwoPinNetsAndRemoveOldPins(UEdGraphPin* InPinA, UEdGraphPin* InPinB) const
{
check(InPinA != NULL);
check(InPinB != NULL);
ensure(InPinA->Direction != InPinB->Direction);
if ((InPinA->LinkedTo.Num() == 0) && (InPinA->Direction == EGPD_Input))
{
// Push the literal value of A to InPinB's connections
for (int32 IndexB = 0; IndexB < InPinB->LinkedTo.Num(); ++IndexB)
{
UEdGraphPin* FarB = InPinB->LinkedTo[IndexB];
// TODO: Michael N. says this if check should be unnecessary once the underlying issue is fixed.
// (Probably should use a check() instead once it's removed though. See additional cases below.
if (FarB != NULL)
{
FarB->DefaultValue = InPinA->DefaultValue;
FarB->DefaultObject = InPinA->DefaultObject;
FarB->DefaultTextValue = InPinA->DefaultTextValue;
}
}
}
else if ((InPinB->LinkedTo.Num() == 0) && (InPinB->Direction == EGPD_Input))
{
// Push the literal value of B to InPinA's connections
for (int32 IndexA = 0; IndexA < InPinA->LinkedTo.Num(); ++IndexA)
{
UEdGraphPin* FarA = InPinA->LinkedTo[IndexA];
// TODO: Michael N. says this if check should be unnecessary once the underlying issue is fixed.
// (Probably should use a check() instead once it's removed though. See additional cases above and below.
if (FarA != NULL)
{
FarA->DefaultValue = InPinB->DefaultValue;
FarA->DefaultObject = InPinB->DefaultObject;
FarA->DefaultTextValue = InPinB->DefaultTextValue;
}
}
}
else
{
// Make direct connections between the things that connect to A or B, removing A and B from the picture
for (int32 IndexA = 0; IndexA < InPinA->LinkedTo.Num(); ++IndexA)
{
UEdGraphPin* FarA = InPinA->LinkedTo[IndexA];
// TODO: Michael N. says this if check should be unnecessary once the underlying issue is fixed.
// (Probably should use a check() instead once it's removed though. See additional cases above.
if (FarA != NULL)
{
for (int32 IndexB = 0; IndexB < InPinB->LinkedTo.Num(); ++IndexB)
{
UEdGraphPin* FarB = InPinB->LinkedTo[IndexB];
FarA->Modify();
FarB->Modify();
FarA->MakeLinkTo(FarB);
}
}
}
}
InPinA->BreakAllPinLinks();
InPinB->BreakAllPinLinks();
}
UK2Node* UEdGraphSchema_K2::CreateSplitPinNode(UEdGraphPin* Pin, FKismetCompilerContext* CompilerContext, UEdGraph* SourceGraph) const
{
UEdGraphNode* GraphNode = Pin->GetOwningNode();
UEdGraph* Graph = GraphNode->GetGraph();
UScriptStruct* StructType = CastChecked<UScriptStruct>(Pin->PinType.PinSubCategoryObject.Get(), ECastCheckedType::NullAllowed);
if (!StructType)
{
if (CompilerContext)
{
CompilerContext->MessageLog.Error(TEXT("No structure in SubCategoryObject in pin @@"), Pin);
}
StructType = GetFallbackStruct();
}
UK2Node* SplitPinNode = NULL;
if (Pin->Direction == EGPD_Input)
{
if (UK2Node_MakeStruct::CanBeMade(StructType))
{
UK2Node_MakeStruct* MakeStructNode = (CompilerContext ? CompilerContext->SpawnIntermediateNode<UK2Node_MakeStruct>(GraphNode, SourceGraph) : NewObject<UK2Node_MakeStruct>(Graph));
MakeStructNode->StructType = StructType;
MakeStructNode->bMadeAfterOverridePinRemoval = true;
SplitPinNode = MakeStructNode;
}
else
{
const FString& MetaData = StructType->GetMetaData(TEXT("HasNativeMake"));
const UFunction* Function = FindObject<UFunction>(NULL, *MetaData, true);
UK2Node_CallFunction* CallFunctionNode = (CompilerContext ? CompilerContext->SpawnIntermediateNode<UK2Node_CallFunction>(GraphNode, SourceGraph) : NewObject<UK2Node_CallFunction>(Graph));
CallFunctionNode->SetFromFunction(Function);
SplitPinNode = CallFunctionNode;
}
}
else
{
if (UK2Node_BreakStruct::CanBeBroken(StructType))
{
UK2Node_BreakStruct* BreakStructNode = (CompilerContext ? CompilerContext->SpawnIntermediateNode<UK2Node_BreakStruct>(GraphNode, SourceGraph) : NewObject<UK2Node_BreakStruct>(Graph));
BreakStructNode->StructType = StructType;
BreakStructNode->bMadeAfterOverridePinRemoval = true;
SplitPinNode = BreakStructNode;
}
else
{
const FString& MetaData = StructType->GetMetaData(TEXT("HasNativeBreak"));
const UFunction* Function = FindObject<UFunction>(NULL, *MetaData, true);
UK2Node_CallFunction* CallFunctionNode = (CompilerContext ? CompilerContext->SpawnIntermediateNode<UK2Node_CallFunction>(GraphNode, SourceGraph) : NewObject<UK2Node_CallFunction>(Graph));
CallFunctionNode->SetFromFunction(Function);
SplitPinNode = CallFunctionNode;
}
}
SplitPinNode->AllocateDefaultPins();
return SplitPinNode;
}
void UEdGraphSchema_K2::SplitPin(UEdGraphPin* Pin) const
{
// Under some circumstances we can get here when PinSubCategoryObject is not set, so we just can't split the pin in that case
UScriptStruct* StructType = Cast<UScriptStruct>(Pin->PinType.PinSubCategoryObject.Get());
if (StructType == nullptr)
{
return;
}
UEdGraphNode* GraphNode = Pin->GetOwningNode();
UK2Node* K2Node = Cast<UK2Node>(GraphNode);
UEdGraph* Graph = CastChecked<UEdGraph>(GraphNode->GetOuter());
GraphNode->Modify();
Pin->Modify();
Pin->bHidden = true;
UK2Node* ProtoExpandNode = CreateSplitPinNode(Pin);
for (UEdGraphPin* ProtoPin : ProtoExpandNode->Pins)
{
if (ProtoPin->Direction == Pin->Direction && !ProtoPin->bHidden)
{
const FString PinName = FString::Printf(TEXT("%s_%s"), *Pin->PinName, *ProtoPin->PinName);
const FEdGraphPinType& ProtoPinType = ProtoPin->PinType;
UEdGraphPin* SubPin = GraphNode->CreatePin(Pin->Direction, ProtoPinType.PinCategory, ProtoPinType.PinSubCategory, ProtoPinType.PinSubCategoryObject.Get(), ProtoPinType.bIsArray, false, PinName);
if (K2Node != nullptr && K2Node->ShouldDrawCompact() && !Pin->ParentPin)
{
SubPin->PinFriendlyName = ProtoPin->GetDisplayName();
}
else
{
FFormatNamedArguments Arguments;
Arguments.Add(TEXT("PinDisplayName"), Pin->GetDisplayName());
Arguments.Add(TEXT("ProtoPinDisplayName"), ProtoPin->GetDisplayName());
SubPin->PinFriendlyName = FText::Format(LOCTEXT("SplitPinFriendlyNameFormat", "{PinDisplayName} {ProtoPinDisplayName}"), Arguments);
}
SubPin->DefaultValue = ProtoPin->DefaultValue;
SubPin->AutogeneratedDefaultValue = ProtoPin->AutogeneratedDefaultValue;
SubPin->ParentPin = Pin;
// CreatePin puts the Pin in the array, but we are going to insert it later, so pop it back out
GraphNode->Pins.Pop(/*bAllowShrinking=*/ false);
Pin->SubPins.Add(SubPin);
}
}
ProtoExpandNode->DestroyNode();
if (Pin->Direction == EGPD_Input)
{
TArray<FString> OriginalDefaults;
if ( StructType == TBaseStructure<FVector>::Get()
|| StructType == TBaseStructure<FRotator>::Get())
{
Pin->DefaultValue.ParseIntoArray(OriginalDefaults, TEXT(","), false);
for (FString& Default : OriginalDefaults)
{
Default = FString::SanitizeFloat(FCString::Atof(*Default));
}
// In some cases (particularly wildcards) the default value may not accurately reflect the normal component elements
while (OriginalDefaults.Num() < 3)
{
OriginalDefaults.Add(TEXT("0.0"));
}
// Rotator OriginalDefaults are in the form of Y,Z,X but our pins are in the form of X,Y,Z
// so we have to change the OriginalDefaults order here to match our pins
if (StructType == TBaseStructure<FRotator>::Get())
{
OriginalDefaults.Swap(0, 2);
OriginalDefaults.Swap(1, 2);
}
}
else if (StructType == TBaseStructure<FVector2D>::Get())
{
FVector2D V2D;
V2D.InitFromString(Pin->DefaultValue);
OriginalDefaults.Add(FString::SanitizeFloat(V2D.X));
OriginalDefaults.Add(FString::SanitizeFloat(V2D.Y));
}
else if (StructType == TBaseStructure<FLinearColor>::Get())
{
FLinearColor LC;
LC.InitFromString(Pin->DefaultValue);
OriginalDefaults.Add(FString::SanitizeFloat(LC.R));
OriginalDefaults.Add(FString::SanitizeFloat(LC.G));
OriginalDefaults.Add(FString::SanitizeFloat(LC.B));
OriginalDefaults.Add(FString::SanitizeFloat(LC.A));
}
check(OriginalDefaults.Num() == 0 || OriginalDefaults.Num() == Pin->SubPins.Num());
for (int32 SubPinIndex = 0; SubPinIndex < OriginalDefaults.Num(); ++SubPinIndex)
{
UEdGraphPin* SubPin = Pin->SubPins[SubPinIndex];
SubPin->DefaultValue = OriginalDefaults[SubPinIndex];
}
}
GraphNode->Pins.Insert(Pin->SubPins, GraphNode->Pins.Find(Pin) + 1);
Graph->NotifyGraphChanged();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(Graph);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
void UEdGraphSchema_K2::RecombinePin(UEdGraphPin* Pin) const
{
UEdGraphNode* GraphNode = Pin->GetOwningNode();
UEdGraphPin* ParentPin = Pin->ParentPin;
GraphNode->Modify();
ParentPin->Modify();
ParentPin->bHidden = false;
UEdGraph* Graph = CastChecked<UEdGraph>(GraphNode->GetOuter());
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(Graph);
for (int32 SubPinIndex = 0; SubPinIndex < ParentPin->SubPins.Num(); ++SubPinIndex)
{
UEdGraphPin* SubPin = ParentPin->SubPins[SubPinIndex];
if (SubPin->SubPins.Num() > 0)
{
RecombinePin(SubPin->SubPins[0]);
}
GraphNode->Pins.Remove(SubPin);
Blueprint->WatchedPins.Remove(SubPin);
}
if (Pin->Direction == EGPD_Input)
{
UScriptStruct* StructType = CastChecked<UScriptStruct>(ParentPin->PinType.PinSubCategoryObject.Get());
TArray<FString> OriginalDefaults;
if (StructType == TBaseStructure<FVector>::Get())
{
ParentPin->DefaultValue = ParentPin->SubPins[0]->DefaultValue + TEXT(",")
+ ParentPin->SubPins[1]->DefaultValue + TEXT(",")
+ ParentPin->SubPins[2]->DefaultValue;
}
else if (StructType == TBaseStructure<FRotator>::Get())
{
// Our pins are in the form X,Y,Z but the Rotator pin type expects the form Y,Z,X
// so we need to make sure they are added in that order here
ParentPin->DefaultValue = ParentPin->SubPins[1]->DefaultValue + TEXT(",")
+ ParentPin->SubPins[2]->DefaultValue + TEXT(",")
+ ParentPin->SubPins[0]->DefaultValue;
}
else if (StructType == TBaseStructure<FVector2D>::Get())
{
FVector2D V2D;
V2D.X = FCString::Atof(*ParentPin->SubPins[0]->DefaultValue);
V2D.Y = FCString::Atof(*ParentPin->SubPins[1]->DefaultValue);
ParentPin->DefaultValue = V2D.ToString();
}
else if (StructType == TBaseStructure<FLinearColor>::Get())
{
FLinearColor LC;
LC.R = FCString::Atof(*ParentPin->SubPins[0]->DefaultValue);
LC.G = FCString::Atof(*ParentPin->SubPins[1]->DefaultValue);
LC.B = FCString::Atof(*ParentPin->SubPins[2]->DefaultValue);
LC.A = FCString::Atof(*ParentPin->SubPins[3]->DefaultValue);
ParentPin->DefaultValue = LC.ToString();
}
}
// Clear out subpins:
TArray<UEdGraphPin*>& ParentSubPins = ParentPin->SubPins;
while (ParentSubPins.Num())
{
// To ensure that MarkPendingKill does not mutate ParentSubPins, we null out the ParentPin
// if we assume that MarkPendingKill *will* mutate ParentSubPins we could introduce an infinite
// loop. No known case of this being possible, but it would be trivial to write bad node logic
// that introduces this problem:
ParentSubPins.Last()->ParentPin = nullptr;
ParentSubPins.Last()->MarkPendingKill();
ParentSubPins.RemoveAt(ParentSubPins.Num()-1);
}
Graph->NotifyGraphChanged();
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
void UEdGraphSchema_K2::OnPinConnectionDoubleCicked(UEdGraphPin* PinA, UEdGraphPin* PinB, const FVector2D& GraphPosition) const
{
const FScopedTransaction Transaction(LOCTEXT("CreateRerouteNodeOnWire", "Create Reroute Node"));
//@TODO: This constant is duplicated from inside of SGraphNodeKnot
const FVector2D NodeSpacerSize(42.0f, 24.0f);
const FVector2D KnotTopLeft = GraphPosition - (NodeSpacerSize * 0.5f);
// Create a new knot
UEdGraph* ParentGraph = PinA->GetOwningNode()->GetGraph();
if (!FBlueprintEditorUtils::IsGraphReadOnly(ParentGraph))
{
UK2Node_Knot* NewKnot = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node_Knot>(ParentGraph, NewObject<UK2Node_Knot>(), KnotTopLeft);
// Move the connections across (only notifying the knot, as the other two didn't really change)
PinA->BreakLinkTo(PinB);
PinA->MakeLinkTo((PinA->Direction == EGPD_Output) ? NewKnot->GetInputPin() : NewKnot->GetOutputPin());
PinB->MakeLinkTo((PinB->Direction == EGPD_Output) ? NewKnot->GetInputPin() : NewKnot->GetOutputPin());
NewKnot->PostReconstructNode();
// Dirty the blueprint
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(CastChecked<UEdGraph>(ParentGraph));
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
}
}
void UEdGraphSchema_K2::ConfigureVarNode(UK2Node_Variable* InVarNode, FName InVariableName, UStruct* InVariableSource, UBlueprint* InTargetBlueprint)
{
// See if this is a 'self context' (ie. blueprint class is owner (or child of owner) of dropped var class)
if ((InVariableSource == NULL) || InTargetBlueprint->SkeletonGeneratedClass->IsChildOf(InVariableSource))
{
FGuid Guid = FBlueprintEditorUtils::FindMemberVariableGuidByName(InTargetBlueprint, InVariableName);
InVarNode->VariableReference.SetSelfMember(InVariableName, Guid);
}
else if (InVariableSource->IsA(UClass::StaticClass()))
{
FGuid Guid;
if (UBlueprint* VariableOwnerBP = Cast<UBlueprint>(Cast<UClass>(InVariableSource)->ClassGeneratedBy))
{
Guid = FBlueprintEditorUtils::FindMemberVariableGuidByName(VariableOwnerBP, InVariableName);
}
InVarNode->VariableReference.SetExternalMember(InVariableName, CastChecked<UClass>(InVariableSource), Guid);
}
else
{
FGuid LocalVarGuid = FBlueprintEditorUtils::FindLocalVariableGuidByName(InTargetBlueprint, InVariableSource, InVariableName);
if (LocalVarGuid.IsValid())
{
InVarNode->VariableReference.SetLocalMember(InVariableName, InVariableSource, LocalVarGuid);
}
}
}
UK2Node_VariableGet* UEdGraphSchema_K2::SpawnVariableGetNode(const FVector2D GraphPosition, class UEdGraph* ParentGraph, FName VariableName, UStruct* Source) const
{
UK2Node_VariableGet* NodeTemplate = NewObject<UK2Node_VariableGet>();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(ParentGraph);
UEdGraphSchema_K2::ConfigureVarNode(NodeTemplate, VariableName, Source, Blueprint);
return FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node_VariableGet>(ParentGraph, NodeTemplate, GraphPosition);
}
UK2Node_VariableSet* UEdGraphSchema_K2::SpawnVariableSetNode(const FVector2D GraphPosition, class UEdGraph* ParentGraph, FName VariableName, UStruct* Source) const
{
UK2Node_VariableSet* NodeTemplate = NewObject<UK2Node_VariableSet>();
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(ParentGraph);
UEdGraphSchema_K2::ConfigureVarNode(NodeTemplate, VariableName, Source, Blueprint);
return FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node_VariableSet>(ParentGraph, NodeTemplate, GraphPosition);
}
UEdGraphPin* UEdGraphSchema_K2::DropPinOnNode(UEdGraphNode* InTargetNode, const FString& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const
{
UEdGraphPin* ResultPin = nullptr;
if (UK2Node_EditablePinBase* EditablePinNode = Cast<UK2Node_EditablePinBase>(InTargetNode))
{
TArray<UK2Node_EditablePinBase*> EditablePinNodes;
EditablePinNode->Modify();
if (InSourcePinDirection == EGPD_Output && Cast<UK2Node_FunctionEntry>(InTargetNode))
{
if (UK2Node_FunctionResult* ResultNode = FBlueprintEditorUtils::FindOrCreateFunctionResultNode(EditablePinNode))
{
EditablePinNodes.Add(ResultNode);
}
else
{
// If we did not successfully find or create a result node, just fail out
return nullptr;
}
}
else if (InSourcePinDirection == EGPD_Input && Cast<UK2Node_FunctionResult>(InTargetNode))
{
TArray<UK2Node_FunctionEntry*> FunctionEntryNode;
InTargetNode->GetGraph()->GetNodesOfClass(FunctionEntryNode);
if (FunctionEntryNode.Num() == 1)
{
EditablePinNodes.Add(FunctionEntryNode[0]);
}
else
{
// If we did not successfully find the entry node, just fail out
return nullptr;
}
}
else
{
if (UK2Node_FunctionResult* ResultNode = Cast<UK2Node_FunctionResult>(EditablePinNode))
{
EditablePinNodes.Append(ResultNode->GetAllResultNodes());
}
else
{
EditablePinNodes.Add(EditablePinNode);
}
}
FString NewPinName = InSourcePinName;
for (UK2Node_EditablePinBase* CurrentEditablePinNode : EditablePinNodes)
{
CurrentEditablePinNode->Modify();
UEdGraphPin* CreatedPin = nullptr;
CreatedPin = CurrentEditablePinNode->CreateUserDefinedPin(NewPinName, InSourcePinType, (InSourcePinDirection == EGPD_Input)? EGPD_Output : EGPD_Input);
// The final ResultPin is from the node the user dragged and dropped to
if (EditablePinNode == CurrentEditablePinNode)
{
ResultPin = CreatedPin;
}
}
FParamsChangedHelper ParamsChangedHelper;
ParamsChangedHelper.ModifiedBlueprints.Add(FBlueprintEditorUtils::FindBlueprintForNode(InTargetNode));
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(FBlueprintEditorUtils::FindBlueprintForNode(InTargetNode));
ParamsChangedHelper.Broadcast(FBlueprintEditorUtils::FindBlueprintForNode(InTargetNode), EditablePinNode, InTargetNode->GetGraph());
for (auto GraphIt = ParamsChangedHelper.ModifiedGraphs.CreateIterator(); GraphIt; ++GraphIt)
{
if(UEdGraph* ModifiedGraph = *GraphIt)
{
ModifiedGraph->NotifyGraphChanged();
}
}
// Now update all the blueprints that got modified
for (auto BlueprintIt = ParamsChangedHelper.ModifiedBlueprints.CreateIterator(); BlueprintIt; ++BlueprintIt)
{
if(UBlueprint* Blueprint = *BlueprintIt)
{
Blueprint->BroadcastChanged();
}
}
}
return ResultPin;
}
bool UEdGraphSchema_K2::SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const
{
bool bIsSupported = false;
if (UK2Node_EditablePinBase* EditablePinNode = Cast<UK2Node_EditablePinBase>(InTargetNode))
{
if (InSourcePinDirection == EGPD_Output && Cast<UK2Node_FunctionEntry>(InTargetNode))
{
// Just check with the Function Entry and see if it's legal, we'll create/use a result node if the user drops
bIsSupported = EditablePinNode->CanCreateUserDefinedPin(InSourcePinType, InSourcePinDirection, OutErrorMessage);
if (bIsSupported)
{
OutErrorMessage = LOCTEXT("AddConnectResultNode", "Add Pin to Result Node");
}
}
else if (InSourcePinDirection == EGPD_Input && Cast<UK2Node_FunctionResult>(InTargetNode))
{
// Just check with the Function Result and see if it's legal, we'll create/use a result node if the user drops
bIsSupported = EditablePinNode->CanCreateUserDefinedPin(InSourcePinType, InSourcePinDirection, OutErrorMessage);
if (bIsSupported)
{
OutErrorMessage = LOCTEXT("AddPinEntryNode", "Add Pin to Entry Node");
}
}
else
{
bIsSupported = EditablePinNode->CanCreateUserDefinedPin(InSourcePinType, (InSourcePinDirection == EGPD_Input)? EGPD_Output : EGPD_Input, OutErrorMessage);
if (bIsSupported)
{
OutErrorMessage = LOCTEXT("AddPinToNode", "Add Pin to Node");
}
}
}
return bIsSupported;
}
bool UEdGraphSchema_K2::IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const
{
return CurrentCacheRefreshID != InVisualizationCacheID;
}
int32 UEdGraphSchema_K2::GetCurrentVisualizationCacheID() const
{
return CurrentCacheRefreshID;
}
void UEdGraphSchema_K2::ForceVisualizationCacheClear() const
{
++CurrentCacheRefreshID;
}
/////////////////////////////////////////////////////
#undef LOCTEXT_NAMESPACE