Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_Select.cpp
Michael Schoell abb8124662 Keywords metadata is now localized.
Blueprint node searching now leverages localized keyword metadata for searching, so searches can now be done in the current langauge and English to find the same nodes.

#jira UE-12049 - Using translated editor, Blueprint node search returns differ for English search terms compared to search terms in the current language

#codereview justin.sargent

[CL 2517785 by Michael Schoell in Main branch]
2015-04-20 12:25:37 -04:00

877 lines
27 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "../../../Runtime/Engine/Classes/Kismet/KismetMathLibrary.h"
#include "../../../Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h"
#include "KismetCompiler.h"
#include "BlueprintNodeSpawner.h"
#include "EditorCategoryUtils.h"
#include "BlueprintActionDatabaseRegistrar.h"
#define LOCTEXT_NAMESPACE "K2Node_Select"
//////////////////////////////////////////////////////////////////////////
// FKCHandler_Select
class FKCHandler_Select : public FNodeHandlingFunctor
{
protected:
TMap<UEdGraphNode*, FBPTerminal*> BoolTermMap;
public:
FKCHandler_Select(FKismetCompilerContext& InCompilerContext)
: FNodeHandlingFunctor(InCompilerContext)
{
}
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
FNodeHandlingFunctor::RegisterNets(Context, Node);
// Create the net for the return value manually as it's a special case Output Direction pin
UK2Node_Select* SelectNode = Cast<UK2Node_Select>(Node);
UEdGraphPin* ReturnPin = SelectNode->GetReturnValuePin();
FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(ReturnPin, Context.NetNameMap->MakeValidName(ReturnPin));
Context.NetMap.Add(SelectNode->GetReturnValuePin(), Term);
// Create a term to determine if the compare was successful or not
FBPTerminal* BoolTerm = Context.CreateLocalTerminal();
BoolTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Boolean;
BoolTerm->Source = Node;
BoolTerm->Name = Context.NetNameMap->MakeValidName(Node) + TEXT("_CmpSuccess");
BoolTermMap.Add(Node, BoolTerm);
}
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
// Cast the node and get all the input pins
UK2Node_Select* SelectNode = Cast<UK2Node_Select>(Node);
TArray<UEdGraphPin*> OptionPins;
SelectNode->GetOptionPins(OptionPins);
UEdGraphPin* IndexPin = SelectNode->GetIndexPin();
// Get the kismet term for the (Condition or Index) that will determine which option to use
UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(IndexPin);
FBPTerminal** ConditionTerm = Context.NetMap.Find(PinToTry);
// Get the kismet term for the return value
UEdGraphPin* ReturnPin = SelectNode->GetReturnValuePin();
FBPTerminal** ReturnTerm = Context.NetMap.Find(ReturnPin);
// Don't proceed if there is no return value or there is no selection
if (ConditionTerm != NULL && ReturnTerm != NULL)
{
FName ConditionalFunctionName = "";
UClass* ConditionalFunctionClass = NULL;
SelectNode->GetConditionalFunction(ConditionalFunctionName, &ConditionalFunctionClass);
UFunction* ConditionFunction = FindField<UFunction>(ConditionalFunctionClass, ConditionalFunctionName);
// Find the local boolean for use in the equality call function below (BoolTerm = result of EqualEqual_IntInt or NotEqual_BoolBool)
FBPTerminal* BoolTerm = BoolTermMap.FindRef(SelectNode);
// We need to keep a pointer to the previous IfNot statement so it can be linked to the next conditional statement
FBlueprintCompiledStatement* PrevIfNotStatement = NULL;
// Keep an array of all the unconditional goto statements so we can clean up their jumps after the noop statement is created
TArray<FBlueprintCompiledStatement*> GotoStatementList;
// Loop through all the options
for (int32 OptionIdx = 0; OptionIdx < OptionPins.Num(); OptionIdx++)
{
// Create a CallFunction statement with the condition function from the Select class
FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);
Statement.Type = KCST_CallFunction;
Statement.FunctionToCall = ConditionFunction;
Statement.FunctionContext = NULL;
Statement.bIsParentContext = false;
// BoolTerm will be the return value of the condition statement
Statement.LHS = BoolTerm;
// The condition passed into the Select node
Statement.RHS.Add(*ConditionTerm);
// Create a local int for use in the equality call function below (LiteralTerm = the right hand side of the EqualEqual_IntInt or NotEqual_BoolBool statement)
FBPTerminal* LiteralTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
LiteralTerm->bIsLiteral = true;
LiteralTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int;
LiteralTerm->Name = FString::Printf(TEXT("%d"), OptionIdx);
Statement.RHS.Add(LiteralTerm);
// If there is a previous IfNot statement, hook this one to that one for jumping
if (PrevIfNotStatement)
{
Statement.bIsJumpTarget = true;
PrevIfNotStatement->TargetLabel = &Statement;
}
// Create a GotoIfNot statement using the BoolTerm from above as the condition
FBlueprintCompiledStatement* IfNotStatement = &Context.AppendStatementForNode(Node);
IfNotStatement->Type = KCST_GotoIfNot;
IfNotStatement->LHS = BoolTerm;
// Create an assignment statement
FBlueprintCompiledStatement& AssignStatement = Context.AppendStatementForNode(Node);
AssignStatement.Type = KCST_Assignment;
AssignStatement.LHS = *ReturnTerm;
// Get the kismet term from the option pin
UEdGraphPin* OptionPinToTry = FEdGraphUtilities::GetNetFromPin(OptionPins[OptionIdx]);
FBPTerminal** OptionTerm = Context.NetMap.Find(OptionPinToTry);
if (!OptionTerm)
{
Context.MessageLog.Error(*LOCTEXT("Error_UnregisterOptionPin", "Unregister option pin @@").ToString(), OptionPins[OptionIdx]);
return;
}
AssignStatement.RHS.Add(*OptionTerm);
// Create an unconditional goto to exit the node
FBlueprintCompiledStatement& GotoStatement = Context.AppendStatementForNode(Node);
GotoStatement.Type = KCST_UnconditionalGoto;
GotoStatementList.Add(&GotoStatement);
// If this is the last IfNot statement, hook the jump to an error message
if (OptionIdx == OptionPins.Num() - 1)
{
// Create a CallFunction statement for doing a print string of our error message
FBlueprintCompiledStatement& PrintStatement = Context.AppendStatementForNode(Node);
PrintStatement.Type = KCST_CallFunction;
PrintStatement.bIsJumpTarget = true;
FName PrintStringFunctionName = "";
UClass* PrintStringFunctionClass = NULL;
SelectNode->GetPrintStringFunction(PrintStringFunctionName, &PrintStringFunctionClass);
UFunction* PrintFunction = FindField<UFunction>(PrintStringFunctionClass, PrintStringFunctionName);
PrintStatement.FunctionToCall = PrintFunction;
PrintStatement.FunctionContext = NULL;
PrintStatement.bIsParentContext = false;
// Create a local int for use in the equality call function below (LiteralTerm = the right hand side of the EqualEqual_IntInt or NotEqual_BoolBool statement)
FBPTerminal* LiteralStringTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
LiteralStringTerm->bIsLiteral = true;
LiteralStringTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String;
FString SelectionNodeType(TEXT("NONE"));
if (IndexPin)
{
UEnum* EnumObject = Cast<UEnum>(IndexPin->PinType.PinSubCategoryObject.Get());
if (EnumObject != NULL)
{
SelectionNodeType = EnumObject->GetName();
}
else
{
// Not an enum, so just use the basic type
SelectionNodeType = IndexPin->PinType.PinCategory;
}
}
const UEdGraph* OwningGraph = Context.MessageLog.FindSourceObjectTypeChecked<UEdGraph>( SelectNode->GetGraph() );
LiteralStringTerm->Name =
FString::Printf(*LOCTEXT("SelectNodeIndexWarning", "Graph %s: Selection Node of type %s failed! Out of bounds indexing of the options. There are only %d options available.").ToString(),
(OwningGraph) ? *OwningGraph->GetFullName() : TEXT("NONE"),
*SelectionNodeType,
OptionPins.Num());
PrintStatement.RHS.Add(LiteralStringTerm);
// Hook the IfNot statement's jump target to this statement
IfNotStatement->TargetLabel = &PrintStatement;
}
PrevIfNotStatement = IfNotStatement;
}
// Create a noop to jump to so the unconditional goto statements can exit the node after successful assignment
FBlueprintCompiledStatement& NopStatement = Context.AppendStatementForNode(Node);
NopStatement.Type = KCST_Nop;
NopStatement.bIsJumpTarget = true;
// Loop through the unconditional goto statements and fix their jump targets
for (auto It = GotoStatementList.CreateConstIterator(); It; It++)
{
(*It)->TargetLabel = &NopStatement;
}
}
}
};
UK2Node_Select::UK2Node_Select(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
NumOptionPins = 2;
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
IndexPinType.PinCategory = Schema->PC_Wildcard;
IndexPinType.PinSubCategory = Schema->PSC_Index;
IndexPinType.PinSubCategoryObject = NULL;
}
void UK2Node_Select::AllocateDefaultPins()
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
// To refresh, just in case it changed
SetEnum(Enum, true);
if (Enum)
{
NumOptionPins = EnumEntries.Num();
}
// Create the option pins
for (int32 Idx = 0; Idx < NumOptionPins; Idx++)
{
UEdGraphPin* NewPin = NULL;
if (Enum)
{
const FString PinName = EnumEntries[Idx].ToString();
UEdGraphPin* TempPin = FindPin(PinName);
if (!TempPin)
{
NewPin = CreatePin(EGPD_Input, Schema->PC_Wildcard, TEXT(""), NULL, false, false, PinName);
}
}
else
{
const FString PinName = FString::Printf(TEXT("Option %d"), Idx);
NewPin = CreatePin(EGPD_Input, Schema->PC_Wildcard, TEXT(""), NULL, false, false, PinName);
}
if (NewPin)
{
if (IndexPinType.PinCategory == UEdGraphSchema_K2::PC_Boolean)
{
NewPin->PinFriendlyName = (Idx == 0 ? GFalse : GTrue);
}
else if (Idx < EnumEntryFriendlyNames.Num())
{
if (EnumEntryFriendlyNames[Idx] != NAME_None)
{
NewPin->PinFriendlyName = FText::FromName(EnumEntryFriendlyNames[Idx]);
}
else
{
NewPin->PinFriendlyName = FText::GetEmpty();
}
}
}
}
// Create the index wildcard pin
CreatePin(EGPD_Input, IndexPinType.PinCategory, IndexPinType.PinSubCategory, IndexPinType.PinSubCategoryObject.Get(), false, false, "Index");
// Create the return value
CreatePin(EGPD_Output, Schema->PC_Wildcard, TEXT(""), NULL, false, false, Schema->PN_ReturnValue);
Super::AllocateDefaultPins();
}
void UK2Node_Select::AutowireNewNode(UEdGraphPin* FromPin)
{
if (FromPin)
{
// Attempt to autowire to the index pin as users generally drag off of something intending to use
// it as an index in a select statement rather than an arbitrary entry:
const UEdGraphSchema_K2* K2Schema = CastChecked<UEdGraphSchema_K2>(GetSchema());
UEdGraphPin* IndexPin = GetIndexPin();
ECanCreateConnectionResponse ConnectResponse = K2Schema->CanCreateConnection(FromPin, IndexPin).Response;
if (ConnectResponse == ECanCreateConnectionResponse::CONNECT_RESPONSE_MAKE)
{
if (K2Schema->TryCreateConnection(FromPin, IndexPin))
{
FromPin->GetOwningNode()->NodeConnectionListChanged();
this->NodeConnectionListChanged();
return;
}
}
}
// No connection made, just use default autowire logic:
Super::AutowireNewNode(FromPin);
}
FText UK2Node_Select::GetTooltipText() const
{
return LOCTEXT("SelectNodeTooltip", "Return the option at Index, (first option is indexed at 0)");
}
FText UK2Node_Select::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("Select", "Select");
}
UK2Node::ERedirectType UK2Node_Select::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
{
// Check to see if the new pin name matches the old pin name (case insensitive - since the base uses Stricmp() to compare pin names, we also ignore case here).
if(Enum != nullptr && NewPinIndex < NumOptionPins && !NewPin->PinName.Equals(OldPin->PinName, ESearchCase::IgnoreCase))
{
// The names don't match, so check for an enum redirect from the old pin name.
int32 EnumIndex = UEnum::FindEnumRedirects(Enum, FName(*OldPin->PinName));
if(EnumIndex != INDEX_NONE)
{
// Found a redirect. Attempt to match it to the new pin name.
FString NewPinName = Enum->GetEnumName(EnumIndex);
if(NewPinName.Equals(NewPin->PinName, ESearchCase::IgnoreCase))
{
// The redirect is a match, so we can reconstruct this pin using the old pin's state.
return UK2Node::ERedirectType_Name;
}
}
}
// Fall back to base class functionality for all other cases.
return Super::DoPinsMatchForReconstruction(NewPin, NewPinIndex, OldPin, OldPinIndex);
}
void UK2Node_Select::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
Super::ReallocatePinsDuringReconstruction(OldPins);
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
// See if this node was saved in the old version with a boolean as the condition
UEdGraphPin* OldConditionPin = NULL;
UEdGraphPin* OldIndexPin = NULL;
for (auto It = OldPins.CreateConstIterator(); It; It++)
{
if ((*It)->PinName == TEXT("bPickOption0"))
{
OldConditionPin = (*It);
}
else if ((*It)->PinName == TEXT("Index"))
{
OldIndexPin = (*It);
}
}
UEdGraphPin* IndexPin = GetIndexPin();
// If we are fixing up an old bool node (swap the options and copy the condition links)
if (OldConditionPin)
{
// Set the index pin type
IndexPinType.PinCategory = Schema->PC_Boolean;
IndexPinType.PinSubCategory = TEXT("");
IndexPinType.PinSubCategoryObject = NULL;
// Set the pin type and Copy the pin
IndexPin->PinType = IndexPinType;
Schema->CopyPinLinks(*OldConditionPin, *IndexPin);
// If we copy links, we need to send a notification
if (IndexPin->LinkedTo.Num() > 0)
{
PinConnectionListChanged(IndexPin);
}
UEdGraphPin* OptionPin0 = FindPin("Option 0");
UEdGraphPin* OptionPin1 = FindPin("Option 1");
for (auto It = OldPins.CreateConstIterator(); It; It++)
{
UEdGraphPin* OldPin = (*It);
if (OldPin->PinName == OptionPin0->PinName)
{
Schema->MovePinLinks(*OldPin, *OptionPin1);
}
else if (OldPin->PinName == OptionPin1->PinName)
{
Schema->MovePinLinks(*OldPin, *OptionPin0);
}
}
}
// If the index pin has links or a default value but is a wildcard, this is an old int pin so convert it
if (OldIndexPin &&
IndexPinType.PinCategory == Schema->PC_Wildcard &&
(OldIndexPin->LinkedTo.Num() > 0 || OldIndexPin->DefaultValue != TEXT("")))
{
IndexPinType.PinCategory = Schema->PC_Int;
IndexPinType.PinSubCategory = TEXT("");
IndexPinType.PinSubCategoryObject = NULL;
IndexPin->PinType = IndexPinType;
}
}
void UK2Node_Select::PostReconstructNode()
{
bReconstructNode = false;
const UEdGraphSchema_K2* Schema = Cast<UEdGraphSchema_K2>(GetSchema());
UEdGraphPin* ReturnPin = GetReturnValuePin();
PinConnectionListChanged(ReturnPin);
const bool bFillTypeFromReturn = Schema && ReturnPin && (ReturnPin->PinType.PinCategory != Schema->PC_Wildcard);
TArray<UEdGraphPin*> OptionPins;
GetOptionPins(OptionPins);
for (auto It = OptionPins.CreateConstIterator(); It; It++)
{
UEdGraphPin* Pin = *It;
const bool bTypeShouldBeFilled = Schema && Pin && (Pin->PinType.PinCategory == Schema->PC_Wildcard);
if (bTypeShouldBeFilled && bFillTypeFromReturn)
{
Pin->Modify();
Pin->PinType = ReturnPin->PinType;
UEdGraphSchema_K2::ValidateExistingConnections(Pin);
}
PinConnectionListChanged(*It);
}
//After ReconstructNode we must be sure, that no additional reconstruction is required
bReconstructNode = false;
}
/** Determine if any pins are connected, if so make all the other pins the same type, if not, make sure pins are switched back to wildcards */
void UK2Node_Select::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{
Super::NotifyPinConnectionListChanged(Pin);
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
// If this is the Enum pin we need to set the enum and reconstruct the node
if (Pin == GetIndexPin())
{
// If the index pin was just linked to another pin
if (Pin->LinkedTo.Num() > 0)
{
UEdGraphPin* LinkPin = Pin->LinkedTo[0];
IndexPinType = LinkPin->PinType;
Pin->PinType = IndexPinType;
// See if it was an enum pin
if (LinkPin->PinType.PinCategory == Schema->PC_Byte &&
LinkPin->PinType.PinSubCategoryObject != NULL &&
LinkPin->PinType.PinSubCategoryObject->IsA(UEnum::StaticClass()))
{
UEnum* EnumPtr = Cast<UEnum>(LinkPin->PinType.PinSubCategoryObject.Get());
SetEnum(EnumPtr);
}
else
{
SetEnum(NULL);
}
Schema->SetPinDefaultValueBasedOnType(Pin);
GetGraph()->NotifyGraphChanged();
UBlueprint* Blueprint = GetBlueprint();
if(!Blueprint->bBeingCompiled)
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
Blueprint->BroadcastChanged();
}
// If the index pin is a boolean, we need to remove all but 2 options
if (IndexPinType.PinCategory == Schema->PC_Boolean &&
NumOptionPins != 2)
{
NumOptionPins = 2;
bReconstructNode = true;
}
}
}
else
{
// Grab references to all option pins and the return pin
TArray<UEdGraphPin*> OptionPins;
GetOptionPins(OptionPins);
UEdGraphPin* ReturnPin = FindPin(Schema->PN_ReturnValue);
// See if this pin is one of the wildcard pins
bool bIsWildcardPin = (Pin == ReturnPin || OptionPins.Find(Pin) != INDEX_NONE) && Pin->PinType.PinCategory == Schema->PC_Wildcard;
// If the pin was one of the wildcards we have to handle it specially
if (bIsWildcardPin)
{
// If the pin is linked, make sure the other wildcard pins match
if (Pin->LinkedTo.Num() > 0)
{
// Set pin type on the pin
Pin->PinType = Pin->LinkedTo[0]->PinType;
// Make sure the return pin is the same pin type
if (ReturnPin != Pin)
{
ReturnPin->Modify();
ReturnPin->PinType = Pin->PinType;
UEdGraphSchema_K2::ValidateExistingConnections(ReturnPin);
}
// Make sure all options are of the same pin type
for (auto It = OptionPins.CreateConstIterator(); It; It++)
{
UEdGraphPin* OptionPin = (*It);
if (*It && *It != Pin)
{
(*It)->Modify();
(*It)->PinType = Pin->PinType;
UEdGraphSchema_K2::ValidateExistingConnections(*It);
}
}
bReconstructNode = true;
}
}
}
}
UEdGraphPin* UK2Node_Select::GetReturnValuePin() const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraphPin* Pin = FindPin(K2Schema->PN_ReturnValue);
check(Pin != NULL);
return Pin;
}
UEdGraphPin* UK2Node_Select::GetIndexPin() const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraphPin* Pin = FindPin("Index");
check(Pin != NULL);
return Pin;
}
void UK2Node_Select::GetOptionPins(TArray<UEdGraphPin*>& OptionPins) const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
OptionPins.Empty();
// If the select node is currently dealing with an enum
if (IndexPinType.PinCategory == K2Schema->PC_Byte &&
IndexPinType.PinSubCategory == TEXT("") &&
IndexPinType.PinSubCategoryObject != NULL &&
IndexPinType.PinSubCategoryObject->IsA(UEnum::StaticClass()))
{
for (auto It = Pins.CreateConstIterator(); It; It++)
{
UEdGraphPin* Pin = (*It);
if (EnumEntries.Contains(FName(*Pin->PinName)))
{
OptionPins.Add(Pin);
}
}
}
else
{
for (auto It = Pins.CreateConstIterator(); It; It++)
{
UEdGraphPin* Pin = (*It);
if (Pin->PinName.Left(6) == "Option")
{
OptionPins.Add(Pin);
}
}
}
}
void UK2Node_Select::GetConditionalFunction(FName& FunctionName, UClass** FunctionClass)
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (IndexPinType.PinCategory == K2Schema->PC_Boolean)
{
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, EqualEqual_BoolBool);
}
else if (IndexPinType.PinCategory == K2Schema->PC_Byte)
{
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, EqualEqual_ByteByte);
}
else if (IndexPinType.PinCategory == K2Schema->PC_Int)
{
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, EqualEqual_IntInt);
}
*FunctionClass = UKismetMathLibrary::StaticClass();
}
void UK2Node_Select::GetPrintStringFunction(FName& FunctionName, UClass** FunctionClass)
{
FunctionName = GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, PrintWarning);
*FunctionClass = UKismetSystemLibrary::StaticClass();
}
void UK2Node_Select::AddOptionPinToNode()
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
// Increment the pin count
NumOptionPins++;
// We guarantee at least 2 options by default and since we just increased the count
// to more than 2, we need to make sure we're now dealing with an index for selection
// instead of the default boolean check
if (IndexPinType.PinCategory == K2Schema->PC_Boolean)
{
IndexPinType.PinCategory = K2Schema->PC_Int;
GetIndexPin()->BreakAllPinLinks();
}
// We will let the AllocateDefaultPins call handle the actual addition via ReconstructNode
ReconstructNode();
}
void UK2Node_Select::RemoveOptionPinToNode()
{
// Increment the pin count
NumOptionPins--;
// We will let the AllocateDefaultPins call handle the actual subtraction via ReconstructNode
ReconstructNode();
}
void UK2Node_Select::SetEnum(UEnum* InEnum, bool bForceRegenerate)
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
UEnum* PrevEnum = Enum;
Enum = InEnum;
if ((PrevEnum != Enum) || bForceRegenerate)
{
// regenerate enum name list
EnumEntries.Empty();
EnumEntryFriendlyNames.Empty();
if (Enum)
{
for (int32 EnumIndex = 0; EnumIndex < Enum->NumEnums() - 1; ++EnumIndex)
{
bool const bShouldBeHidden = Enum->HasMetaData(TEXT("Hidden"), EnumIndex ) || Enum->HasMetaData(TEXT("Spacer"), EnumIndex );
if (!bShouldBeHidden)
{
FString EnumValueName = Enum->GetEnumName(EnumIndex);
FString EnumFriendlyName = Enum->GetDisplayNameText(EnumIndex).ToString();
EnumEntries.Add(FName(*EnumValueName));
EnumEntryFriendlyNames.Add(FName(*EnumFriendlyName));
}
}
}
bReconstructNode = true;
}
}
void UK2Node_Select::NodeConnectionListChanged()
{
if (bReconstructNode)
{
ReconstructNode();
UBlueprint* Blueprint = GetBlueprint();
if(!Blueprint->bBeingCompiled)
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
Blueprint->BroadcastChanged();
}
}
}
bool UK2Node_Select::CanAddOptionPinToNode() const
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if (IndexPinType.PinCategory == Schema->PC_Byte &&
IndexPinType.PinSubCategoryObject.IsValid() &&
IndexPinType.PinSubCategoryObject.Get()->IsA(UEnum::StaticClass()))
{
return false;
}
else if (IndexPinType.PinCategory == Schema->PC_Boolean)
{
return false;
}
return true;
}
bool UK2Node_Select::CanRemoveOptionPinToNode() const
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if (IndexPinType.PinCategory == Schema->PC_Byte &&
(NULL != Cast<UEnum>(IndexPinType.PinSubCategoryObject.Get())))
{
return false;
}
else if (IndexPinType.PinCategory == Schema->PC_Boolean)
{
return false;
}
return true;
}
void UK2Node_Select::ChangePinType(UEdGraphPin* Pin)
{
PinTypeChanged(Pin);
}
bool UK2Node_Select::CanChangePinType(UEdGraphPin* Pin) const
{
// If this is the index pin, only allow type switching if nothing is linked to the pin
if (Pin == GetIndexPin())
{
if (Pin->LinkedTo.Num() > 0)
{
return false;
}
}
// Else it's one of the wildcard pins that share their type, so make sure none of them have a link
else
{
if (GetReturnValuePin()->LinkedTo.Num() > 0)
{
return false;
}
else
{
TArray<UEdGraphPin*> OptionPins;
GetOptionPins(OptionPins);
for (auto It = OptionPins.CreateConstIterator(); It; It++)
{
UEdGraphPin* OptionPin = (*It);
if (OptionPin && OptionPin->LinkedTo.Num() > 0)
{
return false;
}
}
}
}
return true;
}
void UK2Node_Select::PinTypeChanged(UEdGraphPin* Pin)
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if (Pin == GetIndexPin())
{
if (IndexPinType != Pin->PinType)
{
IndexPinType = Pin->PinType;
if (IndexPinType.PinSubCategoryObject.IsValid())
{
SetEnum(Cast<UEnum>(IndexPinType.PinSubCategoryObject.Get()));
}
else if (Enum)
{
SetEnum(NULL);
}
// Remove all but two options if we switched to a bool index
if (IndexPinType.PinCategory == Schema->PC_Boolean)
{
if (NumOptionPins > 2)
{
NumOptionPins = 2;
bReconstructNode = true;
}
}
// Reset the default value
Schema->SetPinDefaultValueBasedOnType(Pin);
}
}
else
{
// Set the return value
UEdGraphPin* ReturnPin = GetReturnValuePin();
if (ReturnPin->PinType != Pin->PinType)
{
ReturnPin->PinType = Pin->PinType;
Schema->SetPinDefaultValueBasedOnType(ReturnPin);
}
// Set the options
TArray<UEdGraphPin*> OptionPins;
GetOptionPins(OptionPins);
for (auto It = OptionPins.CreateConstIterator(); It; It++)
{
UEdGraphPin* OptionPin = (*It);
if (OptionPin->PinType != Pin->PinType ||
OptionPin == Pin)
{
OptionPin->PinType = Pin->PinType;
Schema->SetPinDefaultValueBasedOnType(OptionPin);
}
}
}
// Reconstruct the node since the options could change
if (bReconstructNode)
{
ReconstructNode();
}
// Let the graph know to refresh
GetGraph()->NotifyGraphChanged();
UBlueprint* Blueprint = GetBlueprint();
if(!Blueprint->bBeingCompiled)
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
Blueprint->BroadcastChanged();
}
}
void UK2Node_Select::PostPasteNode()
{
Super::PostPasteNode();
UEdGraphPin* IndexPin = GetIndexPin();
// This information will be cleared and we want to restore it
FString OldDefaultValue = IndexPin->DefaultValue;
// Corrects data in the index pin that is not valid after pasting
PinTypeChanged(GetIndexPin());
// Restore the default value of the index pin
IndexPin->DefaultValue = OldDefaultValue;
}
bool UK2Node_Select::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (OtherPin && (OtherPin->PinType.PinCategory == K2Schema->PC_Exec))
{
OutReason = LOCTEXT("ExecConnectionDisallowed", "Cannot connect with Exec pin.").ToString();
return true;
}
return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);
}
FNodeHandlingFunctor* UK2Node_Select::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_Select(CompilerContext);
}
void UK2Node_Select::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
// actions get registered under specific object-keys; the idea is that
// actions might have to be updated (or deleted) if their object-key is
// mutated (or removed)... here we use the node's class (so if the node
// type disappears, then the action should go with it)
UClass* ActionKey = GetClass();
// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
// check to make sure that the registrar is looking for actions of this type
// (could be regenerating actions for a specific asset, and therefore the
// registrar would only accept actions corresponding to that asset)
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
}
}
FText UK2Node_Select::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Utilities);
}
#undef LOCTEXT_NAMESPACE