| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766 | 
							- using System;
 
- using System.Collections.Generic;
 
- using System.Linq;
 
- using UnityEditor;
 
- using UnityEngine;
 
- using XNode;
 
- using XNodeEditor.Internal;
 
- namespace XNodeEditor
 
- {
 
-     public partial class NodeEditorWindow
 
-     {
 
-         public enum NodeActivity
 
-         {
 
-             Idle,
 
-             HoldNode,
 
-             DragNode,
 
-             HoldGrid,
 
-             DragGrid
 
-         }
 
-         public static NodeActivity currentActivity = NodeActivity.Idle;
 
-         public static bool isPanning { get; private set; }
 
-         public static Vector2[] dragOffset;
 
-         public static XNode.Node[] copyBuffer = null;
 
-         private bool IsDraggingPort
 
-         {
 
-             get { return draggedOutput != null; }
 
-         }
 
-         private bool IsHoveringPort
 
-         {
 
-             get { return hoveredPort != null; }
 
-         }
 
-         private bool IsHoveringNode
 
-         {
 
-             get { return hoveredNode != null; }
 
-         }
 
-         private bool IsHoveringReroute
 
-         {
 
-             get { return hoveredReroute.port != null; }
 
-         }
 
-         private XNode.Node hoveredNode = null;
 
-         [NonSerialized] public XNode.NodePort hoveredPort = null;
 
-         [NonSerialized] private XNode.NodePort draggedOutput = null;
 
-         [NonSerialized] private XNode.NodePort draggedOutputTarget = null;
 
-         [NonSerialized] private XNode.NodePort autoConnectOutput = null;
 
-         [NonSerialized] private List<Vector2> draggedOutputReroutes = new List<Vector2>();
 
-         private RerouteReference hoveredReroute = new RerouteReference();
 
-         public List<RerouteReference> selectedReroutes = new List<RerouteReference>();
 
-         private Vector2 dragBoxStart;
 
-         private List<Node> preBoxSelection;
 
-         private RerouteReference[] preBoxSelectionReroute;
 
-         private Rect selectionBox;
 
-         private bool isDoubleClick = false;
 
-         private Vector2 lastMousePosition;
 
-         private float dragThreshold = 1f;
 
-         public void Controls()
 
-         {
 
-             wantsMouseMove = true;
 
-             Event e = Event.current;
 
-             switch (e.type)
 
-             {
 
-                 case EventType.DragUpdated:
 
-                 case EventType.DragPerform:
 
-                     DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
 
-                     if (e.type == EventType.DragPerform)
 
-                     {
 
-                         DragAndDrop.AcceptDrag();
 
-                         graphEditor.OnDropObjects(DragAndDrop.objectReferences);
 
-                     }
 
-                     break;
 
-                 case EventType.MouseMove:
 
-                     //Keyboard commands will not get correct mouse position from Event
 
-                     lastMousePosition = e.mousePosition;
 
-                     break;
 
-                 case EventType.ScrollWheel:
 
-                     float oldZoom = zoom;
 
-                     if (e.delta.y > 0) zoom += 0.1f * zoom;
 
-                     else zoom -= 0.1f * zoom;
 
-                     if (NodeEditorPreferences.GetSettings().zoomToMouse)
 
-                         panOffset += (1 - oldZoom / zoom) * (WindowToGridPosition(e.mousePosition) + panOffset);
 
-                     break;
 
-                 case EventType.MouseDrag:
 
-                     if (e.button == 0)
 
-                     {
 
-                         if (IsDraggingPort)
 
-                         {
 
-                             // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously
 
-                             if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort))
 
-                             {
 
-                                 draggedOutputTarget = hoveredPort;
 
-                             }
 
-                             else
 
-                             {
 
-                                 draggedOutputTarget = null;
 
-                             }
 
-                             Repaint();
 
-                         }
 
-                         else if (currentActivity == NodeActivity.HoldNode)
 
-                         {
 
-                             RecalculateDragOffsets(e);
 
-                             currentActivity = NodeActivity.DragNode;
 
-                             Repaint();
 
-                         }
 
-                         if (currentActivity == NodeActivity.DragNode)
 
-                         {
 
-                             // Holding ctrl inverts grid snap
 
-                             bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap;
 
-                             if (e.control) gridSnap = !gridSnap;
 
-                             Vector2 mousePos = WindowToGridPosition(e.mousePosition);
 
-                             // Move selected nodes with offset
 
-                             for (int i = 0; i < _selectedNodes.Count; i++)
 
-                             {
 
-                                 XNode.Node node = _selectedNodes[i];
 
-                                 if (node == null)
 
-                                     continue;
 
-                                 Undo.RecordObject(node, "Moved Node");
 
-                                 Vector2 initial = node.position;
 
-                                 try
 
-                                 {
 
-                                     node.position = mousePos + dragOffset[i];
 
-                                 }
 
-                                 catch (Exception exception)
 
-                                 {
 
-                                     Console.WriteLine(exception);
 
-                                     throw;
 
-                                 }
 
-                                 if (gridSnap)
 
-                                 {
 
-                                     node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8;
 
-                                     node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8;
 
-                                 }
 
-                                 // Offset portConnectionPoints instantly if a node is dragged so they aren't delayed by a frame.
 
-                                 Vector2 offset = node.position - initial;
 
-                                 if (offset.sqrMagnitude > 0)
 
-                                 {
 
-                                     foreach (XNode.NodePort output in node.Outputs)
 
-                                     {
 
-                                         Rect rect;
 
-                                         if (portConnectionPoints.TryGetValue(output, out rect))
 
-                                         {
 
-                                             rect.position += offset;
 
-                                             portConnectionPoints[output] = rect;
 
-                                         }
 
-                                     }
 
-                                     foreach (XNode.NodePort input in node.Inputs)
 
-                                     {
 
-                                         Rect rect;
 
-                                         if (portConnectionPoints.TryGetValue(input, out rect))
 
-                                         {
 
-                                             rect.position += offset;
 
-                                             portConnectionPoints[input] = rect;
 
-                                         }
 
-                                     }
 
-                                 }
 
-                             }
 
-                             // Move selected reroutes with offset
 
-                             for (int i = 0; i < selectedReroutes.Count; i++)
 
-                             {
 
-                                 Vector2 pos = mousePos + dragOffset[_selectedNodes.Count + i];
 
-                                 if (gridSnap)
 
-                                 {
 
-                                     pos.x = (Mathf.Round(pos.x / 16) * 16);
 
-                                     pos.y = (Mathf.Round(pos.y / 16) * 16);
 
-                                 }
 
-                                 selectedReroutes[i].SetPoint(pos);
 
-                             }
 
-                             Repaint();
 
-                         }
 
-                         else if (currentActivity == NodeActivity.HoldGrid)
 
-                         {
 
-                             currentActivity = NodeActivity.DragGrid;
 
-                             preBoxSelection = _selectedNodes;
 
-                             preBoxSelectionReroute = selectedReroutes.ToArray();
 
-                             dragBoxStart = WindowToGridPosition(e.mousePosition);
 
-                             Repaint();
 
-                         }
 
-                         else if (currentActivity == NodeActivity.DragGrid)
 
-                         {
 
-                             Vector2 boxStartPos = GridToWindowPosition(dragBoxStart);
 
-                             Vector2 boxSize = e.mousePosition - boxStartPos;
 
-                             if (boxSize.x < 0)
 
-                             {
 
-                                 boxStartPos.x += boxSize.x;
 
-                                 boxSize.x = Mathf.Abs(boxSize.x);
 
-                             }
 
-                             if (boxSize.y < 0)
 
-                             {
 
-                                 boxStartPos.y += boxSize.y;
 
-                                 boxSize.y = Mathf.Abs(boxSize.y);
 
-                             }
 
-                             selectionBox = new Rect(boxStartPos, boxSize);
 
-                             Repaint();
 
-                         }
 
-                     }
 
-                     else if (e.button == 1 || e.button == 2)
 
-                     {
 
-                         //check drag threshold for larger screens
 
-                         if (e.delta.magnitude > dragThreshold)
 
-                         {
 
-                             panOffset += e.delta * zoom;
 
-                             isPanning = true;
 
-                         }
 
-                     }
 
-                     break;
 
-                 case EventType.MouseDown:
 
-                     Repaint();
 
-                     if (e.button == 0)
 
-                     {
 
-                         draggedOutputReroutes.Clear();
 
-                         if (IsHoveringPort)
 
-                         {
 
-                             if (hoveredPort.IsOutput)
 
-                             {
 
-                                 draggedOutput = hoveredPort;
 
-                                 autoConnectOutput = hoveredPort;
 
-                             }
 
-                             else
 
-                             {
 
-                                 hoveredPort.VerifyConnections();
 
-                                 autoConnectOutput = null;
 
-                                 if (hoveredPort.IsConnected)
 
-                                 {
 
-                                     XNode.Node node = hoveredPort.node;
 
-                                     XNode.NodePort output = hoveredPort.Connection;
 
-                                     int outputConnectionIndex = output.GetConnectionIndex(hoveredPort);
 
-                                     draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex);
 
-                                     hoveredPort.Disconnect(output);
 
-                                     draggedOutput = output;
 
-                                     draggedOutputTarget = hoveredPort;
 
-                                     if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
 
-                                 }
 
-                             }
 
-                         }
 
-                         else if (IsHoveringNode && IsHoveringTitle(hoveredNode))
 
-                         {
 
-                             // If mousedown on node header, select or deselect
 
-                             if (!_selectedNodes.Contains(hoveredNode))
 
-                             {
 
-                                 if (e.control || e.shift)
 
-                                 {
 
-                                     SelectNode(hoveredNode, true);
 
-                                 }
 
-                                 else
 
-                                 {
 
-                                     SelectNode(hoveredNode, false);
 
-                                 }
 
-                                 if (!e.control && !e.shift) selectedReroutes.Clear();
 
-                             }
 
-                             // Cache double click state, but only act on it in MouseUp - Except ClickCount only works in mouseDown.
 
-                             isDoubleClick = (e.clickCount == 2);
 
-                             e.Use();
 
-                             currentActivity = NodeActivity.HoldNode;
 
-                         }
 
-                         else if (IsHoveringReroute)
 
-                         {
 
-                             // If reroute isn't selected
 
-                             if (!selectedReroutes.Contains(hoveredReroute))
 
-                             {
 
-                                 // Add it
 
-                                 if (e.control || e.shift) selectedReroutes.Add(hoveredReroute);
 
-                                 // Select it
 
-                                 else
 
-                                 {
 
-                                     selectedReroutes = new List<RerouteReference>() { hoveredReroute };
 
-                                     Selection.activeObject = null;
 
-                                 }
 
-                             }
 
-                             // Deselect
 
-                             else if (e.control || e.shift) selectedReroutes.Remove(hoveredReroute);
 
-                             e.Use();
 
-                             currentActivity = NodeActivity.HoldNode;
 
-                         }
 
-                         // If mousedown on grid background, deselect all
 
-                         else if (!IsHoveringNode)
 
-                         {
 
-                             // 暂时屏蔽以前的逻辑了
 
-                             currentActivity = NodeActivity.HoldGrid;
 
-                             if (!e.control && !e.shift)
 
-                             {
 
-                                 selectedReroutes.Clear();
 
-                                 _selectedNodes.Clear();
 
-                             }
 
-                         }
 
-                     }
 
-                     break;
 
-                 case EventType.MouseUp:
 
-                     if (e.button == 0)
 
-                     {
 
-                         //Port drag release
 
-                         if (IsDraggingPort)
 
-                         {
 
-                             // If connection is valid, save it
 
-                             if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget))
 
-                             {
 
-                                 XNode.Node node = draggedOutputTarget.node;
 
-                                 if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
 
-                                 // ConnectionIndex can be -1 if the connection is removed instantly after creation
 
-                                 int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget);
 
-                                 if (connectionIndex != -1)
 
-                                 {
 
-                                     draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes);
 
-                                     if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
 
-                                     EditorUtility.SetDirty(graph);
 
-                                 }
 
-                             }
 
-                             // Open context menu for auto-connection if there is no target node
 
-                             else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate &&
 
-                                      autoConnectOutput != null)
 
-                             {
 
-                                 GenericMenu menu = new GenericMenu();
 
-                                 graphEditor.AddContextMenuItems(menu);
 
-                                 menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
 
-                             }
 
-                             //Release dragged connection
 
-                             draggedOutput = null;
 
-                             draggedOutputTarget = null;
 
-                             EditorUtility.SetDirty(graph);
 
-                             if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
 
-                         }
 
-                         else if (currentActivity == NodeActivity.DragNode)
 
-                         {
 
-                             IEnumerable<XNode.Node> nodes = _selectedNodes;
 
-                             foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node);
 
-                             if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
 
-                         }
 
-                         else if (!IsHoveringNode)
 
-                         {
 
-                             // If click outside node, release field focus
 
-                             if (!isPanning)
 
-                             {
 
-                                 EditorGUI.FocusTextInControl(null);
 
-                                 EditorGUIUtility.editingTextField = false;
 
-                             }
 
-                             if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
 
-                         }
 
-                         // If click node header, select it.
 
-                         if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift))
 
-                         {
 
-                             selectedReroutes.Clear();
 
-                             SelectNode(hoveredNode, false);
 
-                             // Double click to center node
 
-                             if (isDoubleClick)
 
-                             {
 
-                                 Vector2 nodeDimension = nodeSizes.ContainsKey(hoveredNode)
 
-                                     ? nodeSizes[hoveredNode] / 2
 
-                                     : Vector2.zero;
 
-                                 panOffset = -hoveredNode.position - nodeDimension;
 
-                             }
 
-                         }
 
-                         // If click reroute, select it.
 
-                         if (IsHoveringReroute && !(e.control || e.shift))
 
-                         {
 
-                             selectedReroutes = new List<RerouteReference>() { hoveredReroute };
 
-                             _selectedNodes.Clear();
 
-                         }
 
-                         Repaint();
 
-                         currentActivity = NodeActivity.Idle;
 
-                     }
 
-                     else if (e.button == 1 || e.button == 2)
 
-                     {
 
-                         if (!isPanning)
 
-                         {
 
-                             if (IsDraggingPort)
 
-                             {
 
-                                 draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition));
 
-                             }
 
-                             else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null &&
 
-                                      selectedReroutes.Count == 1)
 
-                             {
 
-                                 selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint());
 
-                                 selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port,
 
-                                     selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1);
 
-                             }
 
-                             else if (IsHoveringReroute)
 
-                             {
 
-                                 ShowRerouteContextMenu(hoveredReroute);
 
-                             }
 
-                             else if (IsHoveringPort)
 
-                             {
 
-                                 ShowPortContextMenu(hoveredPort);
 
-                             }
 
-                             else if (IsHoveringNode && IsHoveringTitle(hoveredNode))
 
-                             {
 
-                                 if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, false);
 
-                                 autoConnectOutput = null;
 
-                                 GenericMenu menu = new GenericMenu();
 
-                                 NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu);
 
-                                 menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
 
-                                 e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places.
 
-                             }
 
-                             else if (!IsHoveringNode)
 
-                             {
 
-                                 autoConnectOutput = null;
 
-                                 GenericMenu menu = new GenericMenu();
 
-                                 graphEditor.AddContextMenuItems(menu);
 
-                                 menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
 
-                             }
 
-                         }
 
-                         isPanning = false;
 
-                     }
 
-                     // Reset DoubleClick
 
-                     isDoubleClick = false;
 
-                     break;
 
-                 case EventType.KeyDown:
 
-                 // if (EditorGUIUtility.editingTextField) break;
 
-                 // else if (e.keyCode == KeyCode.F) Home();
 
-                 // if (NodeEditorUtilities.IsMac())
 
-                 // {
 
-                 //     if (e.keyCode == KeyCode.Return) RenameSelectedNode();
 
-                 // }
 
-                 // else
 
-                 // {
 
-                 //     if (e.keyCode == KeyCode.F2) RenameSelectedNode();
 
-                 // }
 
-                 //
 
-                 // if (e.keyCode == KeyCode.A)
 
-                 // {
 
-                 //     if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node)))
 
-                 //     {
 
-                 //         foreach (XNode.Node node in graph.nodes)
 
-                 //         {
 
-                 //             DeselectNode(node);
 
-                 //         }
 
-                 //     }
 
-                 //     else
 
-                 //     {
 
-                 //         foreach (XNode.Node node in graph.nodes)
 
-                 //         {
 
-                 //             SelectNode(node, true);
 
-                 //         }
 
-                 //     }
 
-                 //
 
-                 //     Repaint();
 
-                 // }
 
-                 //
 
-                 // break;
 
-                 case EventType.ValidateCommand:
 
-                 case EventType.ExecuteCommand:
 
-                     if (e.commandName == "SoftDelete")
 
-                     {
 
-                         if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes();
 
-                         e.Use();
 
-                     }
 
-                     else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete")
 
-                     {
 
-                         if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes();
 
-                         e.Use();
 
-                     }
 
-                     else if (e.commandName == "Duplicate")
 
-                     {
 
-                         if (e.type == EventType.ExecuteCommand) DuplicateSelectedNodes();
 
-                         e.Use();
 
-                     }
 
-                     else if (e.commandName == "Copy")
 
-                     {
 
-                         if (e.type == EventType.ExecuteCommand) CopySelectedNodes();
 
-                         e.Use();
 
-                     }
 
-                     else if (e.commandName == "Paste")
 
-                     {
 
-                         if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition));
 
-                         e.Use();
 
-                     }
 
-                     Repaint();
 
-                     break;
 
-                 case EventType.Ignore:
 
-                     // If release mouse outside window
 
-                     if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid)
 
-                     {
 
-                         Repaint();
 
-                         currentActivity = NodeActivity.Idle;
 
-                     }
 
-                     break;
 
-             }
 
-         }
 
-         private void RecalculateDragOffsets(Event current)
 
-         {
 
-             dragOffset = new Vector2[_selectedNodes.Count + selectedReroutes.Count];
 
-             // Selected nodes
 
-             for (int i = 0; i < _selectedNodes.Count; i++)
 
-             {
 
-                 Node node = _selectedNodes[i];
 
-                 dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition);
 
-             }
 
-             // Selected reroutes
 
-             for (int i = 0; i < selectedReroutes.Count; i++)
 
-             {
 
-                 dragOffset[_selectedNodes.Count + i] =
 
-                     selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition);
 
-             }
 
-         }
 
-         /// <summary> Puts all selected nodes in focus. If no nodes are present, resets view and zoom to to origin </summary>
 
-         public void Home()
 
-         {
 
-             if (_selectedNodes.Count > 0)
 
-             {
 
-                 Vector2 minPos = _selectedNodes.Select(x => x.position)
 
-                     .Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
 
-                 Vector2 maxPos = _selectedNodes
 
-                     .Select(x => x.position + (nodeSizes.ContainsKey(x) ? nodeSizes[x] : Vector2.zero))
 
-                     .Aggregate((x, y) => new Vector2(Mathf.Max(x.x, y.x), Mathf.Max(x.y, y.y)));
 
-                 panOffset = -(minPos + (maxPos - minPos) / 2f);
 
-             }
 
-             else
 
-             {
 
-                 zoom = 2;
 
-                 panOffset = Vector2.zero;
 
-             }
 
-         }
 
-         /// <summary> Remove nodes in the graph in _selectedNodes</summary>
 
-         public void RemoveSelectedNodes()
 
-         {
 
-             // We need to delete reroutes starting at the highest point index to avoid shifting indices
 
-             selectedReroutes = selectedReroutes.OrderByDescending(x => x.pointIndex).ToList();
 
-             for (int i = 0; i < selectedReroutes.Count; i++)
 
-             {
 
-                 selectedReroutes[i].RemovePoint();
 
-             }
 
-             selectedReroutes.Clear();
 
-             foreach (var node in _selectedNodes)
 
-             {
 
-                 if (!node.IsRemove())
 
-                 {
 
-                     continue;
 
-                 }
 
-                 graphEditor.RemoveNode(node);
 
-             }
 
-         }
 
-         /// <summary> Initiate a rename on the currently selected node </summary>
 
-         public void RenameSelectedNode()
 
-         {
 
-             if (_selectedNodes.Count == 1 && Selection.activeObject is XNode.Node)
 
-             {
 
-                 XNode.Node node = Selection.activeObject as XNode.Node;
 
-                 Vector2 size;
 
-                 if (nodeSizes.TryGetValue(node, out size))
 
-                 {
 
-                     RenamePopup.Show(Selection.activeObject, size.x);
 
-                 }
 
-                 else
 
-                 {
 
-                     RenamePopup.Show(Selection.activeObject);
 
-                 }
 
-             }
 
-         }
 
-         /// <summary> Draw this node on top of other nodes by placing it last in the graph.nodes list </summary>
 
-         public void MoveNodeToTop(XNode.Node node)
 
-         {
 
-             int index;
 
-             while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1)
 
-             {
 
-                 graph.nodes[index] = graph.nodes[index + 1];
 
-                 graph.nodes[index + 1] = node;
 
-             }
 
-         }
 
-         /// <summary> Duplicate selected nodes and select the duplicates </summary>
 
-         public void DuplicateSelectedNodes()
 
-         {
 
-             // Get selected nodes which are part of this graph
 
-             XNode.Node[] selectedNodes = _selectedNodes.Select(x => x as XNode.Node)
 
-                 .Where(x => x != null && x.graph == graph).ToArray();
 
-             if (selectedNodes == null || selectedNodes.Length == 0) return;
 
-             // Get top left node position
 
-             Vector2 topLeftNode = selectedNodes.Select(x => x.position)
 
-                 .Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
 
-             InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30));
 
-         }
 
-         public void CopySelectedNodes()
 
-         {
 
-             copyBuffer = _selectedNodes.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph)
 
-                 .ToArray();
 
-         }
 
-         public void PasteNodes(Vector2 pos)
 
-         {
 
-             InsertDuplicateNodes(copyBuffer, pos);
 
-         }
 
-         private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft)
 
-         {
 
-             if (nodes == null || nodes.Length == 0) return;
 
-             // Get top-left node
 
-             Vector2 topLeftNode = nodes.Select(x => x.position)
 
-                 .Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
 
-             Vector2 offset = topLeft - topLeftNode;
 
-             List<Node> newNodes = new List<Node>();
 
-             Dictionary<XNode.Node, XNode.Node> substitutes = new Dictionary<XNode.Node, XNode.Node>();
 
-             for (int i = 0; i < nodes.Length; i++)
 
-             {
 
-                 XNode.Node srcNode = nodes[i];
 
-                 if (srcNode == null) continue;
 
-                 // Check if user is allowed to add more of given node type
 
-                 XNode.Node.DisallowMultipleNodesAttribute disallowAttrib;
 
-                 Type nodeType = srcNode.GetType();
 
-                 if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib))
 
-                 {
 
-                     int typeCount = graph.nodes.Count(x => x.GetType() == nodeType);
 
-                     if (typeCount >= disallowAttrib.max) continue;
 
-                 }
 
-                 XNode.Node newNode = graphEditor.CopyNode(srcNode);
 
-                 substitutes.Add(srcNode, newNode);
 
-                 newNode.position = srcNode.position + offset;
 
-                 newNode.ResetUniqueID();
 
-                 newNodes.Add(newNode);
 
-             }
 
-             // Walk through the selected nodes again, recreate connections, using the new nodes
 
-             for (int i = 0; i < nodes.Length; i++)
 
-             {
 
-                 XNode.Node srcNode = nodes[i];
 
-                 if (srcNode == null) continue;
 
-                 foreach (XNode.NodePort port in srcNode.Ports)
 
-                 {
 
-                     for (int c = 0; c < port.ConnectionCount; c++)
 
-                     {
 
-                         XNode.NodePort inputPort =
 
-                             port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c);
 
-                         XNode.NodePort outputPort =
 
-                             port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c);
 
-                         XNode.Node newNodeIn, newNodeOut;
 
-                         if (substitutes.TryGetValue(inputPort.node, out newNodeIn) &&
 
-                             substitutes.TryGetValue(outputPort.node, out newNodeOut))
 
-                         {
 
-                             newNodeIn.UpdatePorts();
 
-                             newNodeOut.UpdatePorts();
 
-                             inputPort = newNodeIn.GetInputPort(inputPort.fieldName);
 
-                             outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
 
-                         }
 
-                         if (!inputPort.IsConnectedTo(outputPort)) inputPort.Connect(outputPort);
 
-                     }
 
-                 }
 
-             }
 
-             // Select the new nodes
 
-             _selectedNodes = newNodes;
 
-         }
 
-         /// <summary> Draw a connection as we are dragging it </summary>
 
-         public void DrawDraggedConnection()
 
-         {
 
-             if (IsDraggingPort)
 
-             {
 
-                 Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null);
 
-                 float thickness = graphEditor.GetNoodleThickness(draggedOutput, null);
 
-                 NoodlePath path = graphEditor.GetNoodlePath(draggedOutput, null);
 
-                 NoodleStroke stroke = graphEditor.GetNoodleStroke(draggedOutput, null);
 
-                 Rect fromRect;
 
-                 if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return;
 
-                 List<Vector2> gridPoints = new List<Vector2>();
 
-                 gridPoints.Add(fromRect.center);
 
-                 for (int i = 0; i < draggedOutputReroutes.Count; i++)
 
-                 {
 
-                     gridPoints.Add(draggedOutputReroutes[i]);
 
-                 }
 
-                 if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center);
 
-                 else gridPoints.Add(WindowToGridPosition(Event.current.mousePosition));
 
-                 DrawNoodle(gradient, path, stroke, thickness, gridPoints);
 
-                 Color bgcol = Color.black;
 
-                 Color frcol = gradient.colorKeys[0].color;
 
-                 bgcol.a = 0.6f;
 
-                 frcol.a = 0.6f;
 
-                 // Loop through reroute points again and draw the points
 
-                 for (int i = 0; i < draggedOutputReroutes.Count; i++)
 
-                 {
 
-                     // Draw reroute point at position
 
-                     Rect rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16));
 
-                     rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8);
 
-                     rect = GridToWindowRect(rect);
 
-                     NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol);
 
-                 }
 
-             }
 
-         }
 
-         bool IsHoveringTitle(XNode.Node node)
 
-         {
 
-             Vector2 mousePos = Event.current.mousePosition;
 
-             //Get node position
 
-             Vector2 nodePos = GridToWindowPosition(node.position);
 
-             float width;
 
-             Vector2 size;
 
-             if (nodeSizes.TryGetValue(node, out size)) width = size.x;
 
-             else width = 200;
 
-             Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom));
 
-             return windowRect.Contains(mousePos);
 
-         }
 
-         /// <summary> Attempt to connect dragged output to target node </summary>
 
-         public void AutoConnect(XNode.Node node)
 
-         {
 
-             if (autoConnectOutput == null) return;
 
-             // Find input port of same type
 
-             XNode.NodePort inputPort =
 
-                 node.Ports.FirstOrDefault(x => x.IsInput && x.ValueType == autoConnectOutput.ValueType);
 
-             // Fallback to input port
 
-             if (inputPort == null) inputPort = node.Ports.FirstOrDefault(x => x.IsInput);
 
-             // Autoconnect if connection is compatible
 
-             if (inputPort != null && inputPort.CanConnectTo(autoConnectOutput)) autoConnectOutput.Connect(inputPort);
 
-             // Save changes
 
-             EditorUtility.SetDirty(graph);
 
-             if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
 
-             autoConnectOutput = null;
 
-         }
 
-     }
 
- }
 
 
  |