// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. /*============================================================================= EdGraphSchema_K2.cpp =============================================================================*/ #include "BlueprintGraphPrivatePCH.h" #include "GraphEditorActions.h" #include "ScopedTransaction.h" #include "ComponentAssetBroker.h" #include "Kismet2/KismetEditorUtilities.h" #include "Editor/UnrealEd/Public/EdGraphUtilities.h" #include "EdGraph/EdGraphNode_Documentation.h" #define SNAP_GRID (16) // @todo ensure this is the same as SNodePanel::GetSnapGridSize() namespace { // Maximum distance a drag can be off a node edge to require 'push off' from node const int32 NodeDistance = 60; // The amount to offset a literal reference (to an actor) from the function node it is being connected to const float FunctionNodeLiteralReferencesXOffset = 224.0f; // The height of a literal reference node const float NodeLiteralHeight = 48.0f; } ///////////////////////////////////////////////////// // FK2SchemaActionUtils //------------------------------------------------------------------------------ UK2Node const* FK2SchemaActionUtils::ExtractNodeTemplateFromAction(TSharedPtr PaletteAction) { UK2Node const* TemplateNode = NULL; if (PaletteAction.IsValid()) { FName const ActionId = PaletteAction->GetTypeId(); // if this action inherits from FEdGraphSchemaAction_K2NewNode if (ActionId == FEdGraphSchemaAction_K2NewNode::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2AssignDelegate::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2AddComponent::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2AddTimeline::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2AddCustomEvent::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2AddCallOnActor::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2AddCallOnVariable::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2TargetNode::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2PasteHere::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2Event::StaticGetTypeId() || ActionId == FEdGraphSchemaAction_K2AddEvent::StaticGetTypeId()) { FEdGraphSchemaAction_K2NewNode* NewNodeAction = (FEdGraphSchemaAction_K2NewNode*)PaletteAction.Get(); TemplateNode = NewNodeAction->NodeTemplate; } else if (ActionId == FEdGraphSchemaAction_K2ViewNode::StaticGetTypeId()) { FEdGraphSchemaAction_K2ViewNode* FocusNodeAction = (FEdGraphSchemaAction_K2ViewNode*)PaletteAction.Get(); TemplateNode = FocusNodeAction->NodePtr; } } return TemplateNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2ViewNode UEdGraphNode* FEdGraphSchemaAction_K2NewNode::CreateNode(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, class UK2Node* NodeTemplate, bool bSelectNewNode/* = true*/) { // Smart pointer that handles fixup after potential node reconstruction FWeakGraphPinPtr FromPinPtr = FromPin; // Duplicate template node to create new node UEdGraphNode* ResultNode = DuplicateObject(NodeTemplate, ParentGraph); ResultNode->SetFlags(RF_Transactional); ParentGraph->AddNode(ResultNode, true, bSelectNewNode); ResultNode->CreateNewGuid(); ResultNode->PostPlacedNewNode(); ResultNode->AllocateDefaultPins(); // For input pins, new node will generally overlap node being dragged off // Work out if we want to visually push away from connected node int32 XLocation = Location.X; if (FromPinPtr.IsValid() && FromPinPtr->Direction == EGPD_Input) { UEdGraphNode* PinNode = FromPinPtr->GetOwningNode(); const float XDelta = FMath::Abs(PinNode->NodePosX - Location.X); if (XDelta < NodeDistance) { // Set location to edge of current node minus the max move distance // to force node to push off from connect node enough to give selection handle XLocation = PinNode->NodePosX - NodeDistance; } } ResultNode->NodePosX = XLocation; ResultNode->NodePosY = Location.Y; ResultNode->SnapToGrid(SNAP_GRID); // make sure to auto-wire after we position the new node (in case the // auto-wire creates a conversion node to put between them) ResultNode->AutowireNewNode(FromPinPtr); // Update Analytics for the new nodes FBlueprintEditorUtils::AnalyticsTrackNewNode( ResultNode ); // NOTE: At this point the node may have been reconstructed, depending on node type! return ResultNode; } UEdGraphNode* FEdGraphSchemaAction_K2NewNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { UEdGraphNode* ResultNode = NULL; // If there is a template, we actually use it if (NodeTemplate != NULL) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_AddNode", "Add Node") ); ParentGraph->Modify(); if (FromPin) { FromPin->Modify(); } ResultNode = CreateNode(ParentGraph, FromPin, Location, NodeTemplate, bSelectNewNode); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); // See if we need to recompile skeleton after adding this node, or just mark dirty UK2Node* K2Node = Cast(ResultNode); if(K2Node != NULL && K2Node->NodeCausesStructuralBlueprintChange()) { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); } else { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); } // Clear any error messages resulting from placing a node. They'll be flagged on the next compile K2Node->ErrorMsg.Empty(); K2Node->bHasCompilerMessage = false; } return ResultNode; } UEdGraphNode* FEdGraphSchemaAction_K2NewNode::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2D Location, bool bSelectNewNode/* = true*/) { UEdGraphNode* ResultNode = NULL; if (FromPins.Num() > 0) { ResultNode = PerformAction(ParentGraph, FromPins[0], Location, bSelectNewNode); // Try autowiring the rest of the pins for (int32 Index = 1; Index < FromPins.Num(); ++Index) { ResultNode->AutowireNewNode(FromPins[Index]); } } else { ResultNode = PerformAction(ParentGraph, NULL, Location, bSelectNewNode); } return ResultNode; } void FEdGraphSchemaAction_K2NewNode::AddReferencedObjects( FReferenceCollector& Collector ) { FEdGraphSchemaAction::AddReferencedObjects( Collector ); // These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around Collector.AddReferencedObject( NodeTemplate ); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2ViewNode UEdGraphNode* FEdGraphSchemaAction_K2ViewNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { // If the node is valid, select it if (NodePtr) { // Select existing node FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(NodePtr); } return NULL; } UEdGraphNode* FEdGraphSchemaAction_K2ViewNode::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2D Location, bool bSelectNewNode/* = true*/) { PerformAction(ParentGraph, NULL, Location, bSelectNewNode); return NULL; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AssignDelegate UEdGraphNode* FEdGraphSchemaAction_K2AssignDelegate::AssignDelegate(class UK2Node* NodeTemplate, class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) { UK2Node_AddDelegate* BindNode = NULL; if (UK2Node_AddDelegate* AddDelegateTemplate = Cast(NodeTemplate)) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_AddNode", "Add Node") ); ParentGraph->Modify(); if (FromPin) { FromPin->Modify(); } BindNode = Cast(CreateNode(ParentGraph, FromPin, Location, NodeTemplate, bSelectNewNode)); UMulticastDelegateProperty* DelegateProperty = BindNode ? Cast(BindNode->GetProperty()) : NULL; if(DelegateProperty) { const FString FunctionName = FString::Printf(TEXT("%s_Event"), *DelegateProperty->GetName()); UK2Node_CustomEvent* EventNode = UK2Node_CustomEvent::CreateFromFunction( FVector2D(Location.X - 150, Location.Y + 150), ParentGraph, FunctionName, DelegateProperty->SignatureFunction, bSelectNewNode); if(EventNode) { const UEdGraphSchema_K2* K2Schema = GetDefault(); UEdGraphPin* OutDelegatePin = EventNode->FindPinChecked(UK2Node_CustomEvent::DelegateOutputName); UEdGraphPin* InDelegatePin = BindNode->GetDelegatePin(); K2Schema->TryCreateConnection(OutDelegatePin, InDelegatePin); } } UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); } return BindNode; } UEdGraphNode* FEdGraphSchemaAction_K2AssignDelegate::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { return AssignDelegate(NodeTemplate, ParentGraph, FromPin, Location, bSelectNewNode); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_EventFromFunction UEdGraphNode* FEdGraphSchemaAction_EventFromFunction::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { UK2Node_CustomEvent* EventNode = NULL; if (SignatureFunction) { if (FromPin) { // Make sure, that function is latest, so the names of parameters are proper. UK2Node_BaseMCDelegate* MCDelegateNode = Cast(FromPin->GetOwningNode()); UEdGraphPin* InputDelegatePin = MCDelegateNode ? MCDelegateNode->GetDelegatePin() : NULL; UFunction* OriginalFunction = MCDelegateNode ? MCDelegateNode->GetDelegateSignature() : NULL; if (OriginalFunction && (OriginalFunction != SignatureFunction) && (FromPin == InputDelegatePin) && SignatureFunction->IsSignatureCompatibleWith(OriginalFunction)) { SignatureFunction = OriginalFunction; } } const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_AddNode", "Add Node") ); ParentGraph->Modify(); if (FromPin) { FromPin->Modify(); } EventNode = UK2Node_CustomEvent::CreateFromFunction(Location, ParentGraph, SignatureFunction->GetName() + TEXT("_Event"), SignatureFunction, bSelectNewNode); EventNode->AutowireNewNode(FromPin); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); } return EventNode; } UEdGraphNode* FEdGraphSchemaAction_EventFromFunction::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2D Location, bool bSelectNewNode/* = true*/) { UEdGraphNode* ResultNode = NULL; if (FromPins.Num() > 0) { ResultNode = PerformAction(ParentGraph, FromPins[0], Location, bSelectNewNode); // Try autowiring the rest of the pins for (int32 Index = 1; Index < FromPins.Num(); ++Index) { ResultNode->AutowireNewNode(FromPins[Index]); } } else { ResultNode = PerformAction(ParentGraph, NULL, Location, bSelectNewNode); } return ResultNode; } void FEdGraphSchemaAction_EventFromFunction::AddReferencedObjects(FReferenceCollector& Collector) { FEdGraphSchemaAction::AddReferencedObjects(Collector); // These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around Collector.AddReferencedObject(SignatureFunction); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddComponent UEdGraphNode* FEdGraphSchemaAction_K2AddComponent::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { if (ComponentClass == NULL) { return NULL; } UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); UEdGraphNode* NewNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, Location, bSelectNewNode); if ((NewNode != NULL) && (Blueprint != NULL)) { UK2Node_AddComponent* AddCompNode = CastChecked(NewNode); ensure(NULL != Cast(Blueprint->GeneratedClass)); // Then create a new template object, and add to array in UActorComponent* NewTemplate = ConstructObject(ComponentClass, Blueprint->GeneratedClass); NewTemplate->SetFlags(RF_ArchetypeObject); Blueprint->ComponentTemplates.Add(NewTemplate); // Set the name of the template as the default for the TemplateName param UEdGraphPin* TemplateNamePin = AddCompNode->GetTemplateNamePinChecked(); if (TemplateNamePin) { TemplateNamePin->DefaultValue = NewTemplate->GetName(); } // Set the return type to be the type of the template UEdGraphPin* ReturnPin = AddCompNode->GetReturnValuePin(); if (ReturnPin) { ReturnPin->PinType.PinSubCategoryObject = *ComponentClass; } // Set the asset if(ComponentAsset != NULL) { FComponentAssetBrokerage::AssignAssetToComponent(NewTemplate, ComponentAsset); } AddCompNode->ReconstructNode(); } FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); return NewNode; } void FEdGraphSchemaAction_K2AddComponent::AddReferencedObjects( FReferenceCollector& Collector ) { FEdGraphSchemaAction_K2NewNode::AddReferencedObjects( Collector ); // These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around Collector.AddReferencedObject( ComponentAsset ); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddTimeline UEdGraphNode* FEdGraphSchemaAction_K2AddTimeline::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_AddTimeline", "Add Timeline") ); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); UEdGraphNode* NewNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, Location, bSelectNewNode); // Set the name to be a unique timeline name, so the generated node will have a default name that is already validated UK2Node_Timeline* TimelineNode = CastChecked(NewNode); TimelineNode->TimelineName = FBlueprintEditorUtils::FindUniqueTimelineName(Blueprint); if(Blueprint != NULL) { if (FBlueprintEditorUtils::AddNewTimeline(Blueprint, TimelineNode->TimelineName) != NULL) { // Clear off any existing error message now the timeline has been added TimelineNode->ErrorMsg.Empty(); TimelineNode->bHasCompilerMessage = false; } } return NewNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddEvent UEdGraphNode* FEdGraphSchemaAction_K2AddEvent::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { UEdGraphNode* NewNode = NULL; const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_Event", "Add Event") ); UBlueprint const* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); UK2Node_Event const* ExistingEvent = NULL; if (EventHasAlreadyBeenPlaced(Blueprint, &ExistingEvent)) { check(ExistingEvent != NULL); FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ExistingEvent); } else { NewNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, Location, bSelectNewNode); } return NewNode; } bool FEdGraphSchemaAction_K2AddEvent::EventHasAlreadyBeenPlaced(UBlueprint const* Blueprint, UK2Node_Event const** FoundEventOut /*= NULL*/) const { UK2Node_Event* ExistingEvent = NULL; if (Blueprint != NULL) { UK2Node_Event const* EventTemplate = Cast(NodeTemplate); ExistingEvent = FBlueprintEditorUtils::FindOverrideForFunction(Blueprint, EventTemplate->EventSignatureClass, EventTemplate->EventSignatureName); } if (FoundEventOut != NULL) { *FoundEventOut = ExistingEvent; } return (ExistingEvent != NULL); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddCustomEvent UEdGraphNode* FEdGraphSchemaAction_K2AddCustomEvent::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_CustomEvent", "Add Custom Event") ); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); UEdGraphNode* NewNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, Location, bSelectNewNode); // Set the name for the template to be a unique custom event name, so the generated node will have a default name that is already validated UK2Node_CustomEvent* CustomEventNode = CastChecked(NewNode); CustomEventNode->CustomFunctionName = FBlueprintEditorUtils::FindUniqueCustomEventName(Blueprint); return NewNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddCallOnActor UEdGraphNode* FEdGraphSchemaAction_K2AddCallOnActor::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { const UEdGraphSchema_K2* K2Schema = GetDefault(); // Snap the node placement location to the grid, ensures calculations later match up better FVector2D LocalLocation; LocalLocation.X = FMath::GridSnap( Location.X, SNAP_GRID ); LocalLocation.Y = FMath::GridSnap( Location.Y, SNAP_GRID ); // First use the base functionality to spawn the 'call function' node UEdGraphNode* CallNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, LocalLocation); const float FunctionNodeHeightUnsnapped = UEdGraphSchema_K2::EstimateNodeHeight( CallNode ); // this is the guesstimate of the function node's height, snapped to grid units const float FunctionNodeHeight = FMath::GridSnap( FunctionNodeHeightUnsnapped, SNAP_GRID ); // this is roughly the middle of the function node height const float FunctionNodeMidY = LocalLocation.Y + FunctionNodeHeight * 0.5f; // this is the offset up from the mid point at which we start placing nodes const float StartYOffset = (float((LevelActors.Num() > 0) ? LevelActors.Num()-1 : 0) * -NodeLiteralHeight) * 0.5f; // The Y location we start placing nodes from const float ReferencedNodesPlacementYLocation = FunctionNodeMidY + StartYOffset; // Now we need to create the actor literal to wire up for ( int32 ActorIndex = 0; ActorIndex < LevelActors.Num(); ActorIndex++ ) { AActor* LevelActor = LevelActors[ActorIndex]; if(LevelActor != NULL) { UK2Node_Literal* LiteralNode = NewObject(ParentGraph); ParentGraph->AddNode(LiteralNode, false, bSelectNewNode); LiteralNode->SetFlags(RF_Transactional); LiteralNode->SetObjectRef(LevelActor); LiteralNode->AllocateDefaultPins(); LiteralNode->NodePosX = LocalLocation.X - FunctionNodeLiteralReferencesXOffset; // this is the current offset down from the Y start location to place the next node at float CurrentNodeOffset = NodeLiteralHeight * float(ActorIndex); LiteralNode->NodePosY = ReferencedNodesPlacementYLocation + CurrentNodeOffset; LiteralNode->SnapToGrid(SNAP_GRID); // Connect the literal out to the self of the call UEdGraphPin* LiteralOutput = LiteralNode->GetValuePin(); UEdGraphPin* CallSelfInput = CallNode->FindPin(K2Schema->PN_Self); if(LiteralOutput != NULL && CallSelfInput != NULL) { LiteralOutput->MakeLinkTo(CallSelfInput); } } } return CallNode; } void FEdGraphSchemaAction_K2AddCallOnActor::AddReferencedObjects( FReferenceCollector& Collector ) { FEdGraphSchemaAction_K2NewNode::AddReferencedObjects( Collector ); for ( int32 ActorIndex = 0; ActorIndex < LevelActors.Num(); ActorIndex++ ) { if ( LevelActors[ActorIndex] != NULL ) { Collector.AddReferencedObject( LevelActors[ActorIndex] ); } } } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddCallOnVariable UEdGraphNode* FEdGraphSchemaAction_K2AddCallOnVariable::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { const UEdGraphSchema_K2* K2Schema = GetDefault(); // Snap the node placement location to the grid, ensures calculations later match up better FVector2D LocalLocation; LocalLocation.X = FMath::GridSnap( Location.X, SNAP_GRID ); LocalLocation.Y = FMath::GridSnap( Location.Y, SNAP_GRID ); // First use the base functionality to spawn the 'call function' node FVector2D TempLocation = LocalLocation; UEdGraphNode* CallNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, TempLocation, bSelectNewNode); // this is the guesstimate of the function node's height, snapped to grid units const float FunctionNodeHeight = FMath::GridSnap( TempLocation.Y - LocalLocation.Y, SNAP_GRID ); // this is roughly the middle of the function node height const float FunctionNodeMidY = LocalLocation.Y + FunctionNodeHeight * 0.5f; // this is the offset up from the mid point at which we start placing nodes const float StartYOffset = -NodeLiteralHeight * 0.5f; // The Y location we start placing nodes from const float ReferencedNodesPlacementYLocation = FunctionNodeMidY + StartYOffset; // Now we need to create the variable literal to wire up if(VariableName != NAME_None) { UK2Node_VariableGet* GetVarNode = NewObject(ParentGraph); ParentGraph->AddNode(GetVarNode, false, bSelectNewNode); GetVarNode->SetFlags(RF_Transactional); GetVarNode->VariableReference.SetSelfMember(VariableName); GetVarNode->AllocateDefaultPins(); GetVarNode->NodePosX = LocalLocation.X - FunctionNodeLiteralReferencesXOffset; GetVarNode->NodePosY = ReferencedNodesPlacementYLocation; GetVarNode->SnapToGrid(SNAP_GRID); // Connect the literal out to the self of the call UEdGraphPin* LiteralOutput = GetVarNode->GetValuePin(); UEdGraphPin* CallSelfInput = CallNode->FindPin(K2Schema->PN_Self); if(LiteralOutput != NULL && CallSelfInput != NULL) { LiteralOutput->MakeLinkTo(CallSelfInput); } } return CallNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddComment UEdGraphNode* FEdGraphSchemaAction_K2AddComment::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { // Add menu item for creating comment boxes UEdGraphNode_Comment* CommentTemplate = NewObject(); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(ParentGraph); FVector2D SpawnLocation = Location; FSlateRect Bounds; if ((Blueprint != NULL) && FKismetEditorUtilities::GetBoundsForSelectedNodes(Blueprint, Bounds, 50.0f)) { CommentTemplate->SetBounds(Bounds); SpawnLocation.X = CommentTemplate->NodePosX; SpawnLocation.Y = CommentTemplate->NodePosY; } UEdGraphNode* NewNode = FEdGraphSchemaAction_NewNode::SpawnNodeFromTemplate(ParentGraph, CommentTemplate, SpawnLocation, bSelectNewNode); // Update Analytics for these nodes FBlueprintEditorUtils::AnalyticsTrackNewNode( NewNode ); // Mark Blueprint as structurally modified since // UK2Node_Comment::NodeCausesStructuralBlueprintChange used to return true FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); return NewNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddDocumentation UEdGraphNode* FEdGraphSchemaAction_K2AddDocumentation::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/) { // Add menu item for creating document nodes UEdGraphNode_Documentation* DocumentTemplate = NewObject(); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(ParentGraph); FVector2D SpawnLocation = Location; FSlateRect Bounds; if ((Blueprint != NULL) && FKismetEditorUtilities::GetBoundsForSelectedNodes(Blueprint, Bounds, 50.0f)) { DocumentTemplate->SetBounds(Bounds); SpawnLocation.X = DocumentTemplate->NodePosX; SpawnLocation.Y = DocumentTemplate->NodePosY; } UEdGraphNode* NewNode = FEdGraphSchemaAction_NewNode::SpawnNodeFromTemplate(ParentGraph, DocumentTemplate, SpawnLocation, bSelectNewNode); // Mark Blueprint as structurally modified since // UK2Node_Documentation::NodeCausesStructuralBlueprintChange used to return true FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); return NewNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2TargetNode UEdGraphNode* FEdGraphSchemaAction_K2TargetNode::PerformAction( class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/ ) { FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(NodeTemplate); return NULL; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2PasteHere UEdGraphNode* FEdGraphSchemaAction_K2PasteHere::PerformAction( class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode/* = true*/ ) { FKismetEditorUtilities::PasteNodesHere(ParentGraph, Location); return NULL; }