Пример #1
0
        public void CanRefactorCustomNodeName()
        {
            var nodeName = "TheNoodle";
            var catName = "TheCat";
            var descr = "TheCat";
            var path = @"C:\turtle\graphics.dyn";
            var guid1 = Guid.NewGuid();
            var dummyInfo1 = new CustomNodeInfo(guid1, nodeName, catName, descr, path);

            _search.Add(dummyInfo1);

            Assert.AreEqual(1, _search.SearchDictionary.NumElements);

            var newNodeName = "TheTurtle";
            var newInfo = new CustomNodeInfo(guid1, newNodeName, catName, descr, path);
            _search.Refactor(newInfo);

            Assert.AreEqual(1, _search.SearchDictionary.NumElements);

            // search for new name
            _search.SearchAndUpdateResultsSync(newNodeName);

            // results are correct
            Assert.AreEqual(1, _search.SearchResults.Count);
            var res1 = _search.SearchResults[0];
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res1);
            var node1 = res1 as CustomNodeSearchElement;
            Assert.AreEqual(node1.Guid, guid1);

            // search for old name
            _search.SearchAndUpdateResultsSync(nodeName);

            // results are correct
            Assert.AreEqual(0, _search.SearchResults.Count);
        }
Пример #2
0
        public void CanRefactorCustomNodeDescription()
        {
            var nodeName = "TheNoodle";
            var catName = "TheCat";
            var descr = "Cool description, man";
            var path = @"C:\turtle\graphics.dyn";
            var guid1 = Guid.NewGuid();
            var dummyInfo1 = new CustomNodeInfo(guid1, nodeName, catName, descr, path);

            _search.Add(dummyInfo1);

            Assert.AreEqual(1, _search.SearchDictionary.NumElements);

            // search for name
            _search.SearchAndUpdateResultsSync(nodeName);

            // results are correct
            Assert.AreEqual(1, _search.SearchResults.Count);
            var res1 = _search.SearchResults[0];
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res1);
            var node1 = res1 as CustomNodeSearchElement;
            Assert.AreEqual(node1.Guid, guid1);
            Assert.AreEqual(node1.Description, descr);

            // refactor description
            const string newDescription = "Tickle me elmo";
            var newInfo = new CustomNodeInfo(guid1, nodeName, catName, newDescription, path);
            _search.Refactor(newInfo);

            // num elements is unchanged
            Assert.AreEqual(1, _search.SearchDictionary.NumElements);

            // search for name
            _search.SearchAndUpdateResultsSync(nodeName);

            // description is updated
            Assert.AreEqual(1, _search.SearchResults.Count);
            var res2 = _search.SearchResults[0];
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res2);
            var node2 = res2 as CustomNodeSearchElement;
            Assert.AreEqual( guid1, node2.Guid );
            Assert.AreEqual( newDescription, node2.Description);

        }
Пример #3
0
        /// <summary>
        /// Stores the path and function definition without initializing a node.  Overwrites
        /// the existing NodeInfo if necessary
        /// </summary>
        private void SetNodeInfo(CustomNodeInfo newInfo)
        {
            var guids = NodeInfos.Where(x =>
                        {
                            return !string.IsNullOrEmpty(x.Value.Path) &&
                                string.Compare(x.Value.Path, newInfo.Path, StringComparison.OrdinalIgnoreCase) == 0;
                        }).Select(x => x.Key).ToList();

            foreach (var guid in guids)
            {
                NodeInfos.Remove(guid);
            }

            NodeInfos[newInfo.FunctionId] = newInfo;
            OnInfoUpdated(newInfo);
        }
Пример #4
0
        /// <summary>
        ///     Creates a new Custom Node Instance.
        /// </summary>
        /// <param name="id">Identifier referring to a custom node definition.</param>
        /// <param name="nickname">
        ///     Nickname for the custom node to be instantiated, used for error recovery if
        ///     the given id could not be found.
        /// </param>
        /// <param name="isTestMode">
        ///     Flag specifying whether or not this should operate in "test mode".
        /// </param>
        public Function CreateCustomNodeInstance(
            Guid id, string nickname = null, bool isTestMode = false)
        {
            CustomNodeWorkspaceModel workspace;
            CustomNodeDefinition def;
            CustomNodeInfo info;
            // Try to get the definition, initializing the custom node if necessary
            if (TryGetFunctionDefinition(id, isTestMode, out def))
            {
                // Got the definition, proceed as planned.
                info = NodeInfos[id];
            }
            else
            {
                // Couldn't get the workspace with the given ID, try a nickname lookup instead.
                if (nickname != null && TryGetNodeInfo(nickname, out info))
                    return CreateCustomNodeInstance(info.FunctionId, nickname, isTestMode);
                
                // Couldn't find the workspace at all, prepare for a late initialization.
                Log(
                    Properties.Resources.UnableToCreateCustomNodeID + id + "\"",
                    WarningLevel.Moderate);
                info = new CustomNodeInfo(id, nickname ?? "", "", "", "");
            }

            if (def == null)
            {
                def = CustomNodeDefinition.MakeProxy(id, info.Name);
            }

            var node = new Function(def, info.Name, info.Description, info.Category);
            if (loadedWorkspaceModels.TryGetValue(id, out workspace))
                RegisterCustomNodeInstanceForUpdates(node, workspace);
            else
                RegisterCustomNodeInstanceForLateInitialization(node, id, nickname, isTestMode);

            return node;
        }
Пример #5
0
 /// <summary>
 /// Stores the path and function definition without initializing a node.  Overwrites
 /// the existing NodeInfo if necessary
 /// </summary>
 /// <param name="guid">The unique id for the node.</param>
 /// <param name="path">The path for the node.</param>
 public void SetNodeInfo(CustomNodeInfo newInfo)
 {
     var nodeInfo = GetNodeInfo(newInfo.Guid);
     if (nodeInfo == null)
     {
         NodeInfos.Add(newInfo.Guid, newInfo);
     }
     else
     {
         NodeInfos[newInfo.Guid] = newInfo;
     }
 }
Пример #6
0
        public void CustomNodeSaveAsAddsNewCustomNodeToSearchAndItCanBeRefactoredWhilePreservingOriginalFromExistingDyf2()
        {
            // open custom node
            // SaveAs
            // two nodes are returned in search on custom node name, difer 

            var model = Controller.DynamoModel;
            var examplePath = Path.Combine(GetTestDirectory(), @"core\custom_node_saving", "Constant2.dyf");
            Controller.DynamoViewModel.OpenCommand.Execute(examplePath);

            var nodeWorkspace =
                model.Workspaces.FirstOrDefault(x => x is CustomNodeWorkspaceModel) as CustomNodeWorkspaceModel;

            Assert.IsNotNull(nodeWorkspace);
            var oldId = nodeWorkspace.CustomNodeDefinition.FunctionId;

            var newPath = this.GetNewFileNameOnTempPath("dyf");
            var originalNumElements = Controller.SearchViewModel.SearchDictionary.NumElements;

            // save as
            nodeWorkspace.SaveAs(newPath); // introduces new function id

            var newId = nodeWorkspace.CustomNodeDefinition.FunctionId;

            // refactor oldId with new name

            var nodeName = "Constant2 Alt";
            var catName = "TheCat";
            var descr = "TheCat";
            var dummyInfo1 = new CustomNodeInfo(newId, nodeName, catName, descr, "");
            Controller.CustomNodeManager.Refactor(dummyInfo1);

            // num elements is unchanged by refactor
            Assert.AreEqual(originalNumElements + 1, Controller.SearchViewModel.SearchDictionary.NumElements);

            // search common base name
            Controller.SearchViewModel.SearchAndUpdateResultsSync("Constant2");

            // results are correct
            Assert.AreEqual(2, Controller.SearchViewModel.SearchResults.Count);

            var res1 = Controller.SearchViewModel.SearchResults[0];
            var res2 = Controller.SearchViewModel.SearchResults[1];

            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res1);
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res2);

            var node1 = res1 as CustomNodeSearchElement;
            var node2 = res2 as CustomNodeSearchElement;

            Assert.IsTrue((node1.Guid == oldId && node2.Guid == newId) ||
                          (node1.Guid == newId && node2.Guid == oldId));
        }
Пример #7
0
 public Package GetOwnerPackage(CustomNodeInfo def)
 {
     return GetOwnerPackage(def.Path);
 }
Пример #8
0
 internal void Refactor(CustomNodeInfo nodeInfo)
 {
     this.Remove(nodeInfo.Name);
     this.Add(nodeInfo);
 }
Пример #9
0
 /// <summary>
 /// Stores the path and function definition without initializing a node.  Overwrites
 /// the existing NodeInfo if necessary
 /// </summary>
 private void SetNodeInfo(CustomNodeInfo newInfo)
 {
     NodeInfos[newInfo.FunctionId] = newInfo;
     OnInfoUpdated(newInfo);
 }
Пример #10
0
        public void CanRefactorCustomNodeWhilePreservingDuplicates()
        {
            var nodeName = "TheNoodle";
            var catName = "TheCat";
            var descr = "TheCat";
            var path = @"C:\turtle\graphics.dyn";
            var guid1 = Guid.NewGuid();
            var dummyInfo1 = new CustomNodeInfo(guid1, nodeName, catName, descr, path);
            var guid2 = Guid.NewGuid();
            var dummyInfo2 = new CustomNodeInfo(guid2, nodeName, catName, descr, path);

            _search.Add(dummyInfo1);
            _search.Add(dummyInfo2);

            Assert.AreEqual(2, _search.SearchDictionary.NumElements);

            // refactor one of the nodes with newNodeName
            var newNodeName = "TheTurtle";
            var newInfo = new CustomNodeInfo(guid1, newNodeName, catName, descr, path);
            _search.Refactor(newInfo);

            // num elements is unchanged
            Assert.AreEqual(2, _search.SearchDictionary.NumElements);

            // search for new name
            _search.SearchAndUpdateResultsSync(newNodeName);

            // results are correct - only one result
            Assert.AreEqual(1, _search.SearchResults.Count);
            var res1 = _search.SearchResults[0];
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res1);
            var node1 = res1 as CustomNodeSearchElement;
            Assert.AreEqual(node1.Guid, guid1);

            // search for old name
            _search.SearchAndUpdateResultsSync(nodeName);

            // results are correct - the first nodes are returned
            Assert.AreEqual(1, _search.SearchResults.Count);
            var res2 = _search.SearchResults[0];
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res2);
            var node2 = res2 as CustomNodeSearchElement;
            Assert.AreEqual(node2.Guid, guid2);
        }
Пример #11
0
        /// <summary>
        ///     Collapse a set of nodes in a given workspace.  Has the side effects of prompting the user
        ///     first in order to obtain the name and category for the new node,
        ///     writes the function to a dyf file, adds it to the FunctionDict, adds it to search, and compiles and
        ///     places the newly created symbol (defining a lambda) in the Controller's FScheme Environment.
        /// </summary>
        /// <param name="selectedNodes"> The function definition for the user-defined node </param>
        /// <param name="currentWorkspace"> The workspace where</param>
        internal static void Collapse(IEnumerable <dynNodeModel> selectedNodes, dynWorkspaceModel currentWorkspace)
        {
            var selectedNodeSet = new HashSet <dynNodeModel>(selectedNodes);

            //First, prompt the user to enter a name
            string newNodeName = "", newNodeCategory = "", newNodeDescription = "A collapsed node";

            if (!dynSettings.Controller.DynamoViewModel.ShowNewFunctionDialog(ref newNodeName, ref newNodeCategory, ref newNodeDescription))
            {
                return;
            }

            var newNodeWorkspace = new FuncWorkspace(newNodeName, newNodeCategory, newNodeDescription, 0, 0)
            {
                WatchChanges = false
            };

            var newNodeDefinition = new FunctionDefinition(Guid.NewGuid())
            {
                Workspace = newNodeWorkspace
            };

            currentWorkspace.DisableReporting();

            #region Determine Inputs and Outputs

            //Step 1: determine which nodes will be inputs to the new node
            var inputs = new HashSet <Tuple <dynNodeModel, int, Tuple <int, dynNodeModel> > >(
                selectedNodeSet.SelectMany(
                    node => Enumerable.Range(0, node.InPortData.Count).Where(node.HasInput)
                    .Select(data => Tuple.Create(node, data, node.Inputs[data]))
                    .Where(input => !selectedNodeSet.Contains(input.Item3.Item2))));

            var outputs = new HashSet <Tuple <dynNodeModel, int, Tuple <int, dynNodeModel> > >(
                selectedNodeSet.SelectMany(
                    node => Enumerable.Range(0, node.OutPortData.Count).Where(node.HasOutput).SelectMany(
                        data => node.Outputs[data]
                        .Where(output => !selectedNodeSet.Contains(output.Item2))
                        .Select(output => Tuple.Create(node, data, output)))));

            #endregion

            #region Detect 1-node holes (higher-order function extraction)

            var curriedNodeArgs =
                new HashSet <dynNodeModel>(
                    inputs
                    .Select(x => x.Item3.Item2)
                    .Intersect(outputs.Select(x => x.Item3.Item2)))
                .Select(
                    outerNode =>
            {
                var node = new dynApply1();

                //MVVM : Don't make direct reference to view here
                //MVVM: no reference to view here
                //dynNodeView nodeUI = node.NodeUI;

                var elNameAttrib =
                    node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as
                    NodeNameAttribute;
                if (elNameAttrib != null)
                {
                    node.NickName = elNameAttrib.Name;
                }

                node.GUID = Guid.NewGuid();

                //store the element in the elements list
                newNodeWorkspace.Nodes.Add(node);
                node.WorkSpace = newNodeWorkspace;

                node.DisableReporting();

                //MVVM : Can't set view location here

                //dynSettings.Bench.WorkBench.Children.Add(nodeUI);

                //Place it in an appropriate spot
                //Canvas.SetLeft(nodeUI, Canvas.GetLeft(outerNode.NodeUI));
                //Canvas.SetTop(nodeUI, Canvas.GetTop(outerNode.NodeUI));
                node.X = outerNode.X;
                node.Y = outerNode.Y;

                //Fetch all input ports
                // in order
                // that have inputs
                // and whose input comes from an inner node
                List <int> inPortsConnected = Enumerable.Range(0, outerNode.InPortData.Count)
                                              .Where(
                    x =>
                    outerNode.HasInput(x) &&
                    selectedNodeSet.Contains(
                        outerNode.Inputs[x].Item2))
                                              .ToList();

                var nodeInputs = outputs
                                 .Where(output => output.Item3.Item2 == outerNode)
                                 .Select(
                    output =>
                    new
                {
                    InnerNodeInputSender = output.Item1,
                    OuterNodeInPortData  = output.Item3.Item1
                }).ToList();

                nodeInputs.ForEach(_ => node.AddInput());

                node.RegisterAllPorts();

                //MVVM: don't call update layout here
                //dynSettings.Bench.WorkBench.UpdateLayout();

                return(new
                {
                    OuterNode = outerNode,
                    InnerNode = node,
                    Outputs = inputs.Where(input => input.Item3.Item2 == outerNode)
                              .Select(input => input.Item3.Item1),
                    Inputs = nodeInputs,
                    OuterNodePortDataList = inPortsConnected
                });
            }).ToList();

            #endregion

            #region UI Positioning Calculations

            double avgX = selectedNodeSet.Average(node => node.X);
            double avgY = selectedNodeSet.Average(node => node.Y);

            double leftMost  = selectedNodeSet.Min(node => node.X);
            double topMost   = selectedNodeSet.Min(node => node.Y);
            double rightMost = selectedNodeSet.Max(node => node.X + node.Width);

            #endregion

            #region Move selection to new workspace

            var connectors = new HashSet <dynConnectorModel>(currentWorkspace.Connectors.Where(
                                                                 conn => selectedNodeSet.Contains(conn.Start.Owner) &&
                                                                 selectedNodeSet.Contains(conn.End.Owner)));

            //Step 2: move all nodes to new workspace
            //  remove from old
            foreach (var ele in selectedNodeSet)
            {
                currentWorkspace.Nodes.Remove(ele);
            }
            foreach (var ele in connectors)
            {
                currentWorkspace.Connectors.Remove(ele);
            }

            //  add to new
            newNodeWorkspace.Nodes.AddRange(selectedNodeSet);
            newNodeWorkspace.Connectors.AddRange(connectors);

            double leftShift = leftMost - 250;
            foreach (dynNodeModel node in newNodeWorkspace.Nodes)
            {
                node.X = node.X - leftShift;
                node.Y = node.Y - topMost;
            }

            #endregion

            #region Insert new node into the current workspace

            //Step 5: insert new node into original workspace
            var collapsedNode = dynSettings.Controller.DynamoViewModel.CreateFunction(
                inputs.Select(x => x.Item1.InPortData[x.Item2].NickName),
                outputs
                .Where(x => !curriedNodeArgs.Any(y => y.OuterNode == x.Item3.Item2))
                .Select(x => x.Item1.OutPortData[x.Item2].NickName),
                newNodeDefinition);

            collapsedNode.GUID = Guid.NewGuid();

            currentWorkspace.Nodes.Add(collapsedNode);
            collapsedNode.WorkSpace = currentWorkspace;

            collapsedNode.X = avgX;
            collapsedNode.Y = avgY;

            #endregion

            #region Destroy all hanging connectors

            //Step 6: connect inputs and outputs

            var removeConnectors = currentWorkspace.Connectors.Where(c =>
                                                                     selectedNodeSet.Contains(c.Start.Owner) ||
                                                                     selectedNodeSet.Contains(c.End.Owner))
                                   .ToList();
            foreach (dynConnectorModel connector in removeConnectors)
            {
                connector.NotifyConnectedPortsOfDeletion();
                currentWorkspace.Connectors.Remove(connector);
            }

            #endregion

            newNodeWorkspace.Nodes.ToList().ForEach(x => x.DisableReporting());

            var inConnectors = new List <Tuple <dynNodeModel, int, int> >();

            #region Process inputs

            var uniqueInputSenders = new Dictionary <Tuple <dynNodeModel, int>, dynSymbol>();

            //Step 3: insert variables (reference step 1)
            foreach (var input in Enumerable.Range(0, inputs.Count).Zip(inputs, Tuple.Create))
            {
                int inputIndex = input.Item1;

                dynNodeModel inputReceiverNode = input.Item2.Item1;
                int          inputReceiverData = input.Item2.Item2;

                dynNodeModel inputNode = input.Item2.Item3.Item2;
                int          inputData = input.Item2.Item3.Item1;

                dynSymbol node;

                var key = Tuple.Create(inputNode, inputData);
                if (uniqueInputSenders.ContainsKey(key))
                {
                    node = uniqueInputSenders[key];
                }
                else
                {
                    //MVVM : replace NodeUI reference with node
                    inConnectors.Add(Tuple.Create(inputNode, inputData, inputIndex));

                    //Create Symbol Node
                    node = new dynSymbol
                    {
                        Symbol = inputReceiverNode.InPortData[inputReceiverData].NickName
                    };

                    //MVVM : Don't make direct reference to view here
                    //dynNodeView nodeUI = node.NodeUI;

                    var elNameAttrib =
                        node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as NodeNameAttribute;
                    if (elNameAttrib != null)
                    {
                        node.NickName = elNameAttrib.Name;
                    }

                    node.GUID = Guid.NewGuid();

                    //store the element in the elements list
                    newNodeWorkspace.Nodes.Add(node);
                    node.WorkSpace = newNodeWorkspace;

                    node.DisableReporting();

                    //MVVM : Do not add view directly to canvas

                    /*dynSettings.Bench.WorkBench.Children.Add(nodeUI);
                     *
                     * //Place it in an appropriate spot
                     * Canvas.SetLeft(nodeUI, 0);
                     * Canvas.SetTop(nodeUI, inputIndex * (50 + node.NodeUI.Height));
                     *
                     * dynSettings.Bench.WorkBench.UpdateLayout();*/
                    node.X = 0;
                    node.Y = inputIndex * (50 + node.Height);

                    uniqueInputSenders[key] = node;
                }

                var curriedNode = curriedNodeArgs.FirstOrDefault(x => x.OuterNode == inputNode);

                if (curriedNode == null)
                {
                    var conn1 = dynConnectorModel.Make(node,
                                                       inputReceiverNode,
                                                       0,
                                                       inputReceiverData,
                                                       0);

                    if (conn1 != null)
                    {
                        newNodeWorkspace.Connectors.Add(conn1);
                    }
                }
                else
                {
                    //Connect it to the applier
                    var conn = dynConnectorModel.Make(node,
                                                      curriedNode.InnerNode,
                                                      0,
                                                      0,
                                                      0);
                    if (conn != null)
                    {
                        newNodeWorkspace.Connectors.Add(conn);
                    }

                    //Connect applier to the inner input receive
                    var conn2 = dynConnectorModel.Make(
                        curriedNode.InnerNode,
                        inputReceiverNode,
                        0,
                        inputReceiverData,
                        0);

                    if (conn2 != null)
                    {
                        newNodeWorkspace.Connectors.Add(conn2);
                    }
                }
            }

            #endregion

            #region Process outputs

            //List of all inner nodes to connect an output. Unique.
            var outportList = new List <Tuple <dynNodeModel, int> >();

            var outConnectors = new List <Tuple <dynNodeModel, int, int> >();

            int i = 0;
            foreach (var output in outputs)
            {
                if (outportList.All(x => !(x.Item1 == output.Item1 && x.Item2 == output.Item2)))
                {
                    dynNodeModel outputSenderNode   = output.Item1;
                    int          outputSenderData   = output.Item2;
                    dynNodeModel outputReceiverNode = output.Item3.Item2;

                    if (curriedNodeArgs.Any(x => x.OuterNode == outputReceiverNode))
                    {
                        continue;
                    }

                    outportList.Add(Tuple.Create(outputSenderNode, outputSenderData));

                    //Create Symbol Node
                    var node = new dynOutput
                    {
                        Symbol = outputSenderNode.OutPortData[outputSenderData].NickName
                    };

                    //dynNodeView nodeUI = node.NodeUI;

                    var elNameAttrib =
                        node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), false)[0] as NodeNameAttribute;
                    if (elNameAttrib != null)
                    {
                        node.NickName = elNameAttrib.Name;
                    }

                    node.GUID = Guid.NewGuid();

                    //store the element in the elements list
                    newNodeWorkspace.Nodes.Add(node);
                    node.WorkSpace = newNodeWorkspace;

                    node.DisableReporting();

                    //MVVM : Do not add view directly to canvas

                    /*dynSettings.Bench.WorkBench.Children.Add(nodeUI);
                     *
                     * //Place it in an appropriate spot
                     * Canvas.SetLeft(nodeUI, rightMost + 75 - leftShift);
                     * Canvas.SetTop(nodeUI, i * (50 + node.NodeUI.Height));
                     *
                     * dynSettings.Bench.WorkBench.UpdateLayout();*/

                    node.X = rightMost + 75 - leftShift;
                    node.Y = i * (50 + node.Height);

                    var conn = dynConnectorModel.Make(
                        outputSenderNode,
                        node,
                        outputSenderData,
                        0,
                        0);

                    if (conn != null)
                    {
                        newNodeWorkspace.Connectors.Add(conn);
                    }

                    i++;
                }
            }

            //Connect outputs to new node
            foreach (var output in outputs)
            {
                //Node to be connected to in CurrentSpace
                dynNodeModel outputSenderNode = output.Item1;

                //Port to be connected to on outPutNode_outer
                int outputSenderData = output.Item2;

                int          outputReceiverData = output.Item3.Item1;
                dynNodeModel outputReceiverNode = output.Item3.Item2;

                var curriedNode = curriedNodeArgs.FirstOrDefault(
                    x => x.OuterNode == outputReceiverNode);

                if (curriedNode == null)
                {
                    // we create the connectors in the current space later
//MVVM : replaced multiple dynNodeView refrences with dynNode
                    outConnectors.Add(
                        Tuple.Create(
                            outputReceiverNode,
                            outportList.FindIndex(
                                x => x.Item1 == outputSenderNode && x.Item2 == outputSenderData),
                            outputReceiverData));
                }
                else
                {
                    int targetPort = curriedNode.Inputs
                                     .First(
                        x => x.InnerNodeInputSender == outputSenderNode)
                                     .OuterNodeInPortData;

                    int targetPortIndex = curriedNode.OuterNodePortDataList.IndexOf(targetPort);

                    //Connect it (new dynConnector)

                    var conn = dynConnectorModel.Make(
                        outputSenderNode,
                        curriedNode.InnerNode,
                        outputSenderData,
                        targetPortIndex + 1,
                        0);

                    if (conn != null)
                    {
                        newNodeWorkspace.Connectors.Add(conn);
                    }
                }
            }

            #endregion

            //set the name on the node
            collapsedNode.NickName = newNodeName;
            currentWorkspace.Nodes.Remove(collapsedNode);

            // save and load the definition from file
            var customNodeInfo = new CustomNodeInfo(newNodeDefinition.FunctionId, newNodeName, newNodeCategory, "", "");
            dynSettings.Controller.CustomNodeManager.SetNodeInfo(customNodeInfo);
            var path = dynSettings.Controller.DynamoViewModel.SaveFunctionOnly(newNodeDefinition);
            dynSettings.Controller.CustomNodeManager.SetNodePath(newNodeDefinition.FunctionId, path);
            dynSettings.Controller.SearchViewModel.Add(newNodeName, newNodeCategory, "No description provided", newNodeDefinition.FunctionId);

            dynSettings.Controller.DynamoViewModel.CreateNodeCommand.Execute(new Dictionary <string, object>()
            {
                { "name", collapsedNode.Definition.FunctionId.ToString() },
                { "x", avgX },
                { "y", avgY }
            });

            var newlyPlacedCollapsedNode = currentWorkspace.Nodes
                                           .Where(node => node is dynFunction)
                                           .First(node => ((dynFunction)node).Definition.FunctionId == newNodeDefinition.FunctionId);

            // place the node as intended, not centered
            newlyPlacedCollapsedNode.X = avgX;
            newlyPlacedCollapsedNode.Y = avgY;

            newlyPlacedCollapsedNode.DisableReporting();

            foreach (var nodeTuple in inConnectors)
            {
                var conn = dynConnectorModel.Make(
                    nodeTuple.Item1,
                    newlyPlacedCollapsedNode,
                    nodeTuple.Item2,
                    nodeTuple.Item3,
                    0);

                if (conn != null)
                {
                    currentWorkspace.Connectors.Add(conn);
                }
            }

            foreach (var nodeTuple in outConnectors)
            {
                var conn = dynConnectorModel.Make(
                    newlyPlacedCollapsedNode,
                    nodeTuple.Item1,
                    nodeTuple.Item2,
                    nodeTuple.Item3,
                    0);

                if (conn != null)
                {
                    currentWorkspace.Connectors.Add(conn);
                }
            }

            newlyPlacedCollapsedNode.EnableReporting();
            currentWorkspace.EnableReporting();

            newNodeWorkspace.WatchChanges = true;
        }
Пример #12
0
 public void Refactor(CustomNodeInfo nodeInfo)
 {
     this.Refactor(nodeInfo.Guid, nodeInfo.Name, nodeInfo.Category, nodeInfo.Description);
 }
Пример #13
0
 /// <summary>
 ///     Attempts to retrieve information for the given custom node name. If there are multiple
 ///     custom nodes matching the given name, this method will return any one of them.
 /// </summary>
 /// <param name="name">Name of a custom node.</param>
 /// <param name="info"></param>
 /// <returns></returns>
 public bool TryGetNodeInfo(string name, out CustomNodeInfo info)
 {
     info = NodeInfos.Values.FirstOrDefault(x => x.Name == name);
     return(info != null);
 }
Пример #14
0
 /// <summary>
 ///     Attempts to retrieve information for the given custom node identifier.
 /// </summary>
 /// <param name="id">Custom node identifier.</param>
 /// <param name="info"></param>
 /// <returns>Success or failure.</returns>
 public bool TryGetNodeInfo(Guid id, out CustomNodeInfo info)
 {
     return(NodeInfos.TryGetValue(id, out info));
 }
Пример #15
0
        private void RegisterCustomNodeWorkspace(
            CustomNodeWorkspaceModel newWorkspace, CustomNodeInfo info, CustomNodeDefinition definition)
        {
            loadedWorkspaceModels[newWorkspace.CustomNodeId] = newWorkspace;

            SetFunctionDefinition(definition);
            OnDefinitionUpdated(definition);
            newWorkspace.DefinitionUpdated += () =>
            {
                var newDef = newWorkspace.CustomNodeDefinition;
                SetFunctionDefinition(newDef);
                OnDefinitionUpdated(newDef);
            };

            SetNodeInfo(info);
            newWorkspace.InfoChanged += () =>
            {
                var newInfo = newWorkspace.CustomNodeInfo;
                SetNodeInfo(newInfo);
                OnInfoUpdated(newInfo);
            };

            newWorkspace.FunctionIdChanged += oldGuid =>
            {
                loadedWorkspaceModels.Remove(oldGuid);
                loadedCustomNodes.Remove(oldGuid);
                loadOrder.Remove(oldGuid);
                loadedWorkspaceModels[newWorkspace.CustomNodeId] = newWorkspace;
            };
        }
Пример #16
0
 /// <summary>
 ///     Attempts to retrieve information for the given custom node name. If there are multiple
 ///     custom nodes matching the given name, this method will return any one of them.
 /// </summary>
 /// <param name="name">Name of a custom node.</param>
 /// <param name="info"></param>
 /// <returns></returns>
 public bool TryGetNodeInfo(string name, out CustomNodeInfo info)
 {
     info = NodeInfos.Values.FirstOrDefault(x => x.Name == name);
     return info != null;
 }
Пример #17
0
        public void CanRemoveNodeAndCategoryByFunctionId()
        {
            var nodeName = "TheNoodle";
            var catName = "TheCat";
            var descr = "TheCat";
            var path = @"C:\turtle\graphics.dyn";
            var guid1 = Guid.NewGuid();
            var dummyInfo1 = new CustomNodeInfo(guid1, nodeName, catName, descr, path);

            // add custom node
            _search.Add(dummyInfo1);

            // confirm it's in the dictionary
            Assert.AreEqual(1, _search.SearchDictionary.NumElements);

            // remove custom node
            _search.RemoveNodeAndEmptyParentCategory(guid1);

            // it's gone
            Assert.AreEqual(0, _search.SearchDictionary.NumElements);
            _search.SearchAndUpdateResultsSync(nodeName);
            Assert.AreEqual(0, _search.SearchResults.Count);

        }
Пример #18
0
 public void Add(CustomNodeInfo nodeInfo)
 {
     this.Add(nodeInfo.Name, nodeInfo.Category, nodeInfo.Description, nodeInfo.Guid);
 }
Пример #19
0
        public void CanAddDuplicateCustomNodeWithDifferentGuidsAndGetBothInResults()
        {
            var nodeName = "TheNoodle";
            var catName = "TheCat";
            var descr = "TheCat";
            var path = @"C:\turtle\graphics.dyn";
            var guid1 = Guid.NewGuid();
            var guid2 = Guid.NewGuid();
            var dummyInfo1 = new CustomNodeInfo(guid1, nodeName, catName, descr, path);
            var dummyInfo2 = new CustomNodeInfo(guid2, nodeName, catName, descr, path);

            _search.Add(dummyInfo1);
            _search.Add(dummyInfo2);

            Assert.AreEqual(2, _search.SearchDictionary.NumElements);

            _search.SearchAndUpdateResultsSync(nodeName);

            Assert.AreEqual(2, _search.SearchResults.Count);

            var res1 = _search.SearchResults[0];
            var res2 = _search.SearchResults[1];

            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res1);
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res2);

            var node1 = res1 as CustomNodeSearchElement;
            var node2 = res2 as CustomNodeSearchElement;

            Assert.AreEqual(node1.Guid, guid1);
            Assert.AreEqual(node2.Guid, guid2);

        }
Пример #20
0
 public bool IsUnderPackageControl(CustomNodeInfo def)
 {
     return IsUnderPackageControl(def.Path);
 }
Пример #21
0
        public void CanRemoveSingleCustomNodeByIdWhereThereAreDuplicatesWithDifferentIds()
        {
            var nodeName = "TheNoodle";
            var catName = "TheCat";
            var descr = "TheCat";
            var path = @"C:\turtle\graphics.dyn";
            var guid1 = Guid.NewGuid();
            var guid2 = Guid.NewGuid();
            var dummyInfo1 = new CustomNodeInfo(guid1, nodeName, catName, descr, path);
            var dummyInfo2 = new CustomNodeInfo(guid2, nodeName, catName, descr, path);

            _search.Add(dummyInfo1);
            _search.Add(dummyInfo2);

            Assert.AreEqual(2, _search.SearchDictionary.NumElements);

            _search.RemoveNodeAndEmptyParentCategory(guid2);

            Assert.AreEqual(1, _search.SearchDictionary.NumElements);

            _search.SearchAndUpdateResultsSync(nodeName);

            Assert.AreEqual(1, _search.SearchResults.Count);

            var res1 = _search.SearchResults[0];
            Assert.IsAssignableFrom(typeof(CustomNodeSearchElement), res1);
            var node1 = res1 as CustomNodeSearchElement;
            Assert.AreEqual(node1.Guid, guid1);
        }
Пример #22
0
        /// <summary>
        /// Helper method for custom node adding and removing
        /// </summary>
        public void AssertAddAndRemoveCustomNode(SearchViewModel search, string nodeName, string catName, string descr = "Bla",
                                                 string path = "Bla")
        {
            var dummyInfo = new CustomNodeInfo(Guid.NewGuid(), nodeName, catName, descr, path);

            search.Add(dummyInfo);

            search.SearchAndUpdateResultsSync(nodeName);
            Assert.AreNotEqual(0, search.SearchResults.Count);
            Assert.AreEqual(search.SearchResults[0].Name, nodeName);
            Assert.IsTrue(search.ContainsCategory(catName));

            search.RemoveNodeAndEmptyParentCategory(nodeName);
            search.SearchAndUpdateResultsSync(nodeName);

            Assert.AreEqual(0, search.SearchResults.Count);
            Assert.IsFalse(search.ContainsCategory(catName));

        }
Пример #23
0
        /// <summary>
        ///     Save a function.  This includes writing to a file and compiling the 
        ///     function and saving it to the FSchemeEnvironment
        /// </summary>
        /// <param name="definition">The definition to save</param>
        /// <param name="bool">Whether to write the function to file.</param>
        /// <returns>Whether the operation was successful</returns>
        public void SaveFunction(FunctionDefinition definition, bool writeDefinition = true, bool addToSearch = false, bool compileFunction = true)
        {
            if (definition == null)
                return;

            // Get the internal nodes for the function
            var functionWorkspace = definition.Workspace as FuncWorkspace;

            // If asked to, write the definition to file
            if (writeDefinition)
            {
                string path = "";
                if (String.IsNullOrEmpty(definition.Workspace.FilePath))
                {
                    var pluginsPath = this.Controller.CustomNodeManager.GetDefaultSearchPath();

                    if (!Directory.Exists(pluginsPath))
                        Directory.CreateDirectory(pluginsPath);

                    path = Path.Combine(pluginsPath, dynSettings.FormatFileName(functionWorkspace.Name) + ".dyf");
                }
                else
                {
                    path = definition.Workspace.FilePath;
                }

                try
                {

                    dynWorkspaceModel.SaveWorkspace(path, functionWorkspace);

                    if (addToSearch)
                    {
                        Controller.SearchViewModel.Add(functionWorkspace.Name, functionWorkspace.Category, functionWorkspace.Description, definition.FunctionId);
                    }

                    var customNodeInfo = new CustomNodeInfo(definition.FunctionId, functionWorkspace.Name, functionWorkspace.Category,
                                                            functionWorkspace.Description, path);
                    Controller.CustomNodeManager.SetNodeInfo(customNodeInfo);

                    #region Compile Function and update all nodes

                    IEnumerable<string> inputNames = new List<string>();
                    IEnumerable<string> outputNames = new List<string>();
                    dynSettings.Controller.FSchemeEnvironment.DefineSymbol(definition.FunctionId.ToString(), CustomNodeManager.CompileFunction(definition, ref inputNames, ref outputNames));

                    //Update existing function nodes which point to this function to match its changes
                    foreach (dynNodeModel el in AllNodes)
                    {
                        if (el is dynFunction)
                        {
                            var node = (dynFunction)el;

                            if (node.Definition != definition)
                                continue;

                            node.SetInputs(inputNames);
                            node.SetOutputs(outputNames);
                            el.RegisterAllPorts();
                        }
                    }

                    //Call OnSave for all saved elements
                    foreach (dynNodeModel el in functionWorkspace.Nodes)
                        el.onSave();

                    #endregion

                }
                catch (Exception e)
                {
                    Log("Error saving:" + e.GetType());
                    Log(e);
                }
            }
        }
Пример #24
0
        /// <summary>
        ///     Import a dyf file for eventual initialization.  
        /// </summary>
        /// <returns>null if we failed to get data from the path, otherwise the CustomNodeInfo object for the </returns>
        public CustomNodeInfo AddFileToPath(string file)
        {
            Guid guid;
            string name;
            string category;
            string description;
            if (!GetHeaderFromPath(file, out guid, out name, out category, out description))
            {
                return null;
            }

            // the node has already been loaded
            // from somewhere else
            if (Contains(guid))
            {
                var nodeInfo = GetNodeInfo(guid);
                if (nodeInfo != null)
                {
                    return nodeInfo;
                }
            }

            var info = new CustomNodeInfo(guid, name, category, description, file);
            SetNodeInfo(info);
            return info;
        }
Пример #25
0
 /// <summary>
 ///     Stores the path and function definition without initializing a node
 /// </summary>
 /// <param name="guid">The unique id for the node.</param>
 /// <param name="path">The path for the node.</param>
 public void SetNodeInfo(CustomNodeInfo info)
 {
     this.SetNodeName(info.Guid, info.Name);
     this.SetNodeCategory(info.Guid, info.Category);
     this.SetNodeDescription(info.Guid, info.Description);
     this.SetNodePath(info.Guid, info.Path);
 }
Пример #26
0
 public void Refactor(CustomNodeInfo nodeInfo)
 {
     Refactor(nodeInfo.Guid, nodeInfo.Name, nodeInfo.Category, nodeInfo.Description);
 }
Пример #27
0
        public bool Add(CustomNodeInfo nodeInfo)
        {
            var nodeEle = new CustomNodeSearchElement(nodeInfo);

            if (SearchDictionary.Contains(nodeEle))
            {
                return this.Refactor(nodeInfo);
            }

            SearchDictionary.Add(nodeEle, nodeEle.Name);
            SearchDictionary.Add(nodeEle, nodeInfo.Category + "." + nodeEle.Name);

            TryAddCategoryAndItem(nodeInfo.Category, nodeEle);

            return true;
        }
Пример #28
0
 /// <summary>
 ///     Import a dyf file for eventual initialization.  
 /// </summary>
 /// <param name="file">Path to a custom node file on disk.</param>
 /// <param name="isTestMode">
 ///     Flag specifying whether or not this should operate in "test mode".
 /// </param>
 /// <param name="info">
 ///     If the info was successfully processed, this parameter will be set to
 ///     it. Otherwise, it will be set to null.
 /// </param>
 /// <returns>True on success, false if the file could not be read properly.</returns>
 public bool AddUninitializedCustomNode(string file, bool isTestMode, out CustomNodeInfo info)
 {
     if (TryGetInfoFromPath(file, isTestMode, out info))
     {
         SetNodeInfo(info);
         return true;
     }
     return false;
 }
Пример #29
0
 public bool Refactor(CustomNodeInfo nodeInfo)
 {
     this.RemoveNodeAndEmptyParentCategory(nodeInfo.Guid);
     return this.Add(nodeInfo);
 }
Пример #30
0
        /// <summary>
        ///     Get a guid from a specific path, internally this first calls GetDefinitionFromPath
        /// </summary>
        /// <param name="path">The path from which to get the guid</param>
        /// <param name="isTestMode">
        ///     Flag specifying whether or not this should operate in "test mode".
        /// </param>
        /// <param name="info"></param>
        /// <returns>The custom node info object - null if we failed</returns>
        public bool TryGetInfoFromPath(string path, bool isTestMode, out CustomNodeInfo info)
        {
            try
            {
                var xmlDoc = new XmlDocument();
                xmlDoc.Load(path);

                WorkspaceInfo header;
                if (!WorkspaceInfo.FromXmlDocument(xmlDoc, path, isTestMode, false, AsLogger(), out header))
                {
                    Log(String.Format(Properties.Resources.FailedToLoadHeader, path));
                    info = null;
                    return false;
                }
                info = new CustomNodeInfo(
                    Guid.Parse(header.ID),
                    header.Name,
                    header.Category,
                    header.Description, 
                    path);
                return true;
            }
            catch (Exception e)
            {
                Log(String.Format(Properties.Resources.FailedToLoadHeader, path));
                Log(e.ToString());
                info = null;
                return false;
            }
        }
Пример #31
0
 /// <summary>
 /// Stores the path and function definition without initializing a node.  Overwrites
 /// the existing NodeInfo if necessary
 /// </summary>
 private void SetNodeInfo(CustomNodeInfo newInfo)
 {
     NodeInfos[newInfo.FunctionId] = newInfo;
     OnInfoUpdated(newInfo);
 }
Пример #32
0
 /// <summary>
 ///     Attempts to retrieve information for the given custom node identifier.
 /// </summary>
 /// <param name="id">Custom node identifier.</param>
 /// <param name="info"></param>
 /// <returns>Success or failure.</returns>
 public bool TryGetNodeInfo(Guid id, out CustomNodeInfo info)
 {
     return NodeInfos.TryGetValue(id, out info);
 }
Пример #33
0
        public void CustomNodeSaveAsAddsNewCustomNodeToSearchAndItCanBeRefactoredWhilePreservingOriginalFromExistingDyf()
        {
            // open custom node
            // SaveAs
            // two nodes are returned in search on custom node name, difer 

            var model = Controller.DynamoModel;
            var examplePath = Path.Combine(GetTestDirectory(), @"core\custom_node_saving", "Constant2.dyf");
            model.Open(examplePath);

            var nodeWorkspace =
                model.Workspaces.FirstOrDefault(x => x is CustomNodeWorkspaceModel) as CustomNodeWorkspaceModel;

            Assert.IsNotNull(nodeWorkspace);
            var oldId = nodeWorkspace.CustomNodeDefinition.FunctionId;

            var newPath = this.GetNewFileNameOnTempPath("dyf");
            var originalNumElements = Controller.SearchViewModel.SearchDictionary.NumElements;

            // save as
            nodeWorkspace.SaveAs(newPath); // introduces new function id

            var newId = nodeWorkspace.CustomNodeDefinition.FunctionId;

            // refactor oldId with new name

            var nodeName = "TheNoodle";
            var catName = "TheCat";
            var descr = "TheCat";
            var dummyInfo1 = new CustomNodeInfo(newId, nodeName, catName, descr, "");
            Controller.CustomNodeManager.Refactor(dummyInfo1);

            // num elements is unchanged by refactor
            Assert.AreEqual(originalNumElements + 1, Controller.SearchViewModel.SearchDictionary.NumElements);

            // search for refactored node
            Controller.SearchViewModel.SearchAndUpdateResultsSync("TheNoodle");

            // results are correct
            Assert.AreEqual(1, Controller.SearchViewModel.SearchResults.Count);
            var node3 = Controller.SearchViewModel.SearchResults[0] as CustomNodeSearchElement;
            Assert.AreEqual(newId, node3.Guid);

            // search for un-refactored node
            Controller.SearchViewModel.SearchAndUpdateResultsSync("Constant2");

            // results are correct
            Assert.AreEqual(1, Controller.SearchViewModel.SearchResults.Count);
            var node4 = Controller.SearchViewModel.SearchResults[0] as CustomNodeSearchElement;
            Assert.AreEqual(oldId, node4.Guid);

        }
Пример #34
0
 protected virtual void OnInfoUpdated(CustomNodeInfo obj)
 {
     var handler = InfoUpdated;
     if (handler != null) handler(obj);
 }
Пример #35
0
        /// <summary>
        ///     Collapse a set of nodes in a given workspace.  Has the side effects of prompting the user
        ///     first in order to obtain the name and category for the new node, 
        ///     writes the function to a dyf file, adds it to the FunctionDict, adds it to search, and compiles and 
        ///     places the newly created symbol (defining a lambda) in the Controller's FScheme Environment.  
        /// </summary>
        /// <param name="selectedNodes"> The function definition for the user-defined node </param>
        /// <param name="currentWorkspace"> The workspace where</param>
        internal static void Collapse(IEnumerable<dynNodeModel> selectedNodes, dynWorkspaceModel currentWorkspace)
        {
            var selectedNodeSet = new HashSet<dynNodeModel>(selectedNodes);

            //First, prompt the user to enter a name
            string newNodeName = "", newNodeCategory = "", newNodeDescription = "A collapsed node";
            if (!dynSettings.Controller.DynamoViewModel.ShowNewFunctionDialog(ref newNodeName, ref newNodeCategory, ref newNodeDescription))
            {
                return;
            }

            var newNodeWorkspace = new FuncWorkspace(newNodeName, newNodeCategory, newNodeDescription, 0, 0)
            {
                WatchChanges = false
            };

            var newNodeDefinition = new FunctionDefinition(Guid.NewGuid())
            {
                Workspace = newNodeWorkspace
            };

            currentWorkspace.DisableReporting();

            #region Determine Inputs and Outputs

            //Step 1: determine which nodes will be inputs to the new node
            var inputs = new HashSet<Tuple<dynNodeModel, int, Tuple<int, dynNodeModel>>>(
                selectedNodeSet.SelectMany(
                    node => Enumerable.Range(0, node.InPortData.Count).Where(node.HasInput)
                        .Select(data => Tuple.Create(node, data, node.Inputs[data]))
                        .Where(input => !selectedNodeSet.Contains(input.Item3.Item2))));

            var outputs = new HashSet<Tuple<dynNodeModel, int, Tuple<int, dynNodeModel>>>(
                selectedNodeSet.SelectMany(
                    node => Enumerable.Range(0, node.OutPortData.Count).Where(node.HasOutput).SelectMany(
                        data => node.Outputs[data]
                                    .Where(output => !selectedNodeSet.Contains(output.Item2))
                                    .Select(output => Tuple.Create(node, data, output)))));

            #endregion

            #region Detect 1-node holes (higher-order function extraction)

            var curriedNodeArgs =
                new HashSet<dynNodeModel>(
                    inputs
                        .Select(x => x.Item3.Item2)
                        .Intersect(outputs.Select(x => x.Item3.Item2)))
                    .Select(
                        outerNode =>
                        {
                            var node = new dynApply1();

                            //MVVM : Don't make direct reference to view here
                            //MVVM: no reference to view here
                            //dynNodeView nodeUI = node.NodeUI;

                            var elNameAttrib =
                                node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as
                                NodeNameAttribute;
                            if (elNameAttrib != null)
                            {
                                node.NickName = elNameAttrib.Name;
                            }

                            node.GUID = Guid.NewGuid();

                            //store the element in the elements list
                            newNodeWorkspace.Nodes.Add(node);
                            node.WorkSpace = newNodeWorkspace;

                            node.DisableReporting();

                            //MVVM : Can't set view location here

                            //dynSettings.Bench.WorkBench.Children.Add(nodeUI);

                            //Place it in an appropriate spot
                            //Canvas.SetLeft(nodeUI, Canvas.GetLeft(outerNode.NodeUI));
                            //Canvas.SetTop(nodeUI, Canvas.GetTop(outerNode.NodeUI));
                            node.X = outerNode.X;
                            node.Y = outerNode.Y;

                            //Fetch all input ports
                            // in order
                            // that have inputs
                            // and whose input comes from an inner node
                            List<int> inPortsConnected = Enumerable.Range(0, outerNode.InPortData.Count)
                                                                   .Where(
                                                                       x =>
                                                                       outerNode.HasInput(x) &&
                                                                       selectedNodeSet.Contains(
                                                                           outerNode.Inputs[x].Item2))
                                                                   .ToList();

                            var nodeInputs = outputs
                                .Where(output => output.Item3.Item2 == outerNode)
                                .Select(
                                    output =>
                                    new
                                    {
                                        InnerNodeInputSender = output.Item1,
                                        OuterNodeInPortData = output.Item3.Item1
                                    }).ToList();

                            nodeInputs.ForEach(_ => node.AddInput());

                            node.RegisterAllPorts();

                            //MVVM: don't call update layout here
                            //dynSettings.Bench.WorkBench.UpdateLayout();

                            return new
                            {
                                OuterNode = outerNode,
                                InnerNode = node,
                                Outputs = inputs.Where(input => input.Item3.Item2 == outerNode)
                                                .Select(input => input.Item3.Item1),
                                Inputs = nodeInputs,
                                OuterNodePortDataList = inPortsConnected
                            };
                        }).ToList();

            #endregion

            #region UI Positioning Calculations

            double avgX = selectedNodeSet.Average(node => node.X);
            double avgY = selectedNodeSet.Average(node => node.Y);

            double leftMost = selectedNodeSet.Min(node => node.X);
            double topMost = selectedNodeSet.Min(node => node.Y);
            double rightMost = selectedNodeSet.Max(node => node.X + node.Width);

            #endregion

            #region Move selection to new workspace

            var connectors = new HashSet<dynConnectorModel>(currentWorkspace.Connectors.Where(
                                                                conn => selectedNodeSet.Contains(conn.Start.Owner)
                                                                    && selectedNodeSet.Contains(conn.End.Owner)));

            //Step 2: move all nodes to new workspace
            //  remove from old
            foreach (var ele in selectedNodeSet)
            {
                currentWorkspace.Nodes.Remove(ele);
            }
            foreach (var ele in connectors)
            {
                currentWorkspace.Connectors.Remove(ele);
            }

            //  add to new
            newNodeWorkspace.Nodes.AddRange(selectedNodeSet);
            newNodeWorkspace.Connectors.AddRange(connectors);

            double leftShift = leftMost - 250;
            foreach (dynNodeModel node in newNodeWorkspace.Nodes)
            {
                node.X = node.X - leftShift;
                node.Y = node.Y - topMost;
            }

            #endregion

            #region Insert new node into the current workspace

            //Step 5: insert new node into original workspace
            var collapsedNode = dynSettings.Controller.DynamoViewModel.CreateFunction(
                inputs.Select(x => x.Item1.InPortData[x.Item2].NickName),
                outputs
                    .Where(x => !curriedNodeArgs.Any(y => y.OuterNode == x.Item3.Item2))
                    .Select(x => x.Item1.OutPortData[x.Item2].NickName),
                newNodeDefinition);

            collapsedNode.GUID = Guid.NewGuid();

            currentWorkspace.Nodes.Add(collapsedNode);
            collapsedNode.WorkSpace = currentWorkspace;

            collapsedNode.X = avgX;
            collapsedNode.Y = avgY;

            #endregion

            #region Destroy all hanging connectors

            //Step 6: connect inputs and outputs

            var removeConnectors = currentWorkspace.Connectors.Where(c =>
                                                                     selectedNodeSet.Contains(c.Start.Owner) ||
                                                                     selectedNodeSet.Contains(c.End.Owner))
                                                   .ToList();
            foreach (dynConnectorModel connector in removeConnectors)
            {
                connector.NotifyConnectedPortsOfDeletion();
                currentWorkspace.Connectors.Remove(connector);
            }

            #endregion

            newNodeWorkspace.Nodes.ToList().ForEach(x => x.DisableReporting());

            var inConnectors = new List<Tuple<dynNodeModel, int, int>>();

            #region Process inputs

            var uniqueInputSenders = new Dictionary<Tuple<dynNodeModel, int>, dynSymbol>();

            //Step 3: insert variables (reference step 1)
            foreach (var input in Enumerable.Range(0, inputs.Count).Zip(inputs, Tuple.Create))
            {
                int inputIndex = input.Item1;

                dynNodeModel inputReceiverNode = input.Item2.Item1;
                int inputReceiverData = input.Item2.Item2;

                dynNodeModel inputNode = input.Item2.Item3.Item2;
                int inputData = input.Item2.Item3.Item1;

                dynSymbol node;

                var key = Tuple.Create(inputNode, inputData);
                if (uniqueInputSenders.ContainsKey(key))
                {
                    node = uniqueInputSenders[key];
                }
                else
                {
                    //MVVM : replace NodeUI reference with node
                    inConnectors.Add(Tuple.Create(inputNode, inputData, inputIndex));

                    //Create Symbol Node
                    node = new dynSymbol
                    {
                        Symbol = inputReceiverNode.InPortData[inputReceiverData].NickName
                    };

                    //MVVM : Don't make direct reference to view here
                    //dynNodeView nodeUI = node.NodeUI;

                    var elNameAttrib =
                        node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as NodeNameAttribute;
                    if (elNameAttrib != null)
                    {
                        node.NickName = elNameAttrib.Name;
                    }

                    node.GUID = Guid.NewGuid();

                    //store the element in the elements list
                    newNodeWorkspace.Nodes.Add(node);
                    node.WorkSpace = newNodeWorkspace;

                    node.DisableReporting();

                    //MVVM : Do not add view directly to canvas
                    /*dynSettings.Bench.WorkBench.Children.Add(nodeUI);

                    //Place it in an appropriate spot
                    Canvas.SetLeft(nodeUI, 0);
                    Canvas.SetTop(nodeUI, inputIndex * (50 + node.NodeUI.Height));

                    dynSettings.Bench.WorkBench.UpdateLayout();*/
                    node.X = 0;
                    node.Y = inputIndex * (50 + node.Height);

                    uniqueInputSenders[key] = node;
                }

                var curriedNode = curriedNodeArgs.FirstOrDefault(x => x.OuterNode == inputNode);

                if (curriedNode == null)
                {
                    var conn1 = dynConnectorModel.Make(node,
                                                  inputReceiverNode,
                                                  0,
                                                  inputReceiverData,
                                                  0 );

                    if (conn1 != null)
                        newNodeWorkspace.Connectors.Add(conn1);
                }
                else
                {
                    //Connect it to the applier
                    var conn = dynConnectorModel.Make(node,
                                                     curriedNode.InnerNode,
                                                     0,
                                                     0,
                                                     0);
                    if (conn != null)
                        newNodeWorkspace.Connectors.Add(conn);

                    //Connect applier to the inner input receive
                    var conn2 = dynConnectorModel.Make(
                        curriedNode.InnerNode,
                        inputReceiverNode,
                        0,
                        inputReceiverData,
                        0);

                    if (conn2 != null)
                        newNodeWorkspace.Connectors.Add(conn2);
                }
            }

            #endregion

            #region Process outputs

            //List of all inner nodes to connect an output. Unique.
            var outportList = new List<Tuple<dynNodeModel, int>>();

            var outConnectors = new List<Tuple<dynNodeModel, int, int>>();

            int i = 0;
            foreach (var output in outputs)
            {
                if (outportList.All(x => !(x.Item1 == output.Item1 && x.Item2 == output.Item2)))
                {
                    dynNodeModel outputSenderNode = output.Item1;
                    int outputSenderData = output.Item2;
                    dynNodeModel outputReceiverNode = output.Item3.Item2;

                    if (curriedNodeArgs.Any(x => x.OuterNode == outputReceiverNode))
                        continue;

                    outportList.Add(Tuple.Create(outputSenderNode, outputSenderData));

                    //Create Symbol Node
                    var node = new dynOutput
                    {
                        Symbol = outputSenderNode.OutPortData[outputSenderData].NickName
                    };

                    //dynNodeView nodeUI = node.NodeUI;

                    var elNameAttrib =
                        node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), false)[0] as NodeNameAttribute;
                    if (elNameAttrib != null)
                    {
                        node.NickName = elNameAttrib.Name;
                    }

                    node.GUID = Guid.NewGuid();

                    //store the element in the elements list
                    newNodeWorkspace.Nodes.Add(node);
                    node.WorkSpace = newNodeWorkspace;

                    node.DisableReporting();

                    //MVVM : Do not add view directly to canvas
                    /*dynSettings.Bench.WorkBench.Children.Add(nodeUI);

                    //Place it in an appropriate spot
                    Canvas.SetLeft(nodeUI, rightMost + 75 - leftShift);
                    Canvas.SetTop(nodeUI, i * (50 + node.NodeUI.Height));

                    dynSettings.Bench.WorkBench.UpdateLayout();*/

                    node.X = rightMost + 75 - leftShift;
                    node.Y = i*(50 + node.Height);

                    var conn = dynConnectorModel.Make(
                                outputSenderNode,
                                node,
                                outputSenderData,
                                0,
                                0 );

                    if (conn != null)
                        newNodeWorkspace.Connectors.Add(conn);

                    i++;
                }
            }

            //Connect outputs to new node
            foreach (var output in outputs)
            {
                //Node to be connected to in CurrentSpace
                dynNodeModel outputSenderNode = output.Item1;

                //Port to be connected to on outPutNode_outer
                int outputSenderData = output.Item2;

                int outputReceiverData = output.Item3.Item1;
                dynNodeModel outputReceiverNode = output.Item3.Item2;

                var curriedNode = curriedNodeArgs.FirstOrDefault(
                    x => x.OuterNode == outputReceiverNode);

                if (curriedNode == null)
                {
                    // we create the connectors in the current space later
            //MVVM : replaced multiple dynNodeView refrences with dynNode
                    outConnectors.Add(
                        Tuple.Create(
                            outputReceiverNode,
                            outportList.FindIndex(
                                x => x.Item1 == outputSenderNode && x.Item2 == outputSenderData),
                            outputReceiverData));
                }
                else
                {
                    int targetPort = curriedNode.Inputs
                                                .First(
                                                    x => x.InnerNodeInputSender == outputSenderNode)
                                                .OuterNodeInPortData;

                    int targetPortIndex = curriedNode.OuterNodePortDataList.IndexOf(targetPort);

                    //Connect it (new dynConnector)

                    var conn = dynConnectorModel.Make(
                        outputSenderNode,
                        curriedNode.InnerNode,
                        outputSenderData,
                        targetPortIndex + 1,
                        0);

                    if (conn != null)
                        newNodeWorkspace.Connectors.Add(conn);
                }
            }

            #endregion

            //set the name on the node
            collapsedNode.NickName = newNodeName;
            currentWorkspace.Nodes.Remove(collapsedNode);

            // save and load the definition from file
            var customNodeInfo = new CustomNodeInfo(newNodeDefinition.FunctionId, newNodeName, newNodeCategory, "", "");
            dynSettings.Controller.CustomNodeManager.SetNodeInfo(customNodeInfo);
            var path = dynSettings.Controller.DynamoViewModel.SaveFunctionOnly(newNodeDefinition);
            dynSettings.Controller.CustomNodeManager.SetNodePath(newNodeDefinition.FunctionId, path);
            dynSettings.Controller.SearchViewModel.Add(newNodeName, newNodeCategory, "No description provided", newNodeDefinition.FunctionId);

            dynSettings.Controller.DynamoViewModel.CreateNodeCommand.Execute(new Dictionary<string, object>()
                {
                    {"name", collapsedNode.Definition.FunctionId.ToString() },
                    {"x", avgX },
                    {"y", avgY }
                });

            var newlyPlacedCollapsedNode = currentWorkspace.Nodes
                                            .Where(node => node is dynFunction)
                                            .First(node => ((dynFunction)node).Definition.FunctionId == newNodeDefinition.FunctionId);

            // place the node as intended, not centered
            newlyPlacedCollapsedNode.X = avgX;
            newlyPlacedCollapsedNode.Y = avgY;

            newlyPlacedCollapsedNode.DisableReporting();

            foreach (var nodeTuple in inConnectors)
            {
                var conn = dynConnectorModel.Make(
                                    nodeTuple.Item1,
                                    newlyPlacedCollapsedNode,
                                    nodeTuple.Item2,
                                    nodeTuple.Item3,
                                    0 );

                if (conn != null)
                    currentWorkspace.Connectors.Add(conn);
            }

            foreach (var nodeTuple in outConnectors)
            {
                var conn = dynConnectorModel.Make(
                                    newlyPlacedCollapsedNode,
                                    nodeTuple.Item1,
                                    nodeTuple.Item2,
                                    nodeTuple.Item3,
                                    0 );

                if (conn != null)
                    currentWorkspace.Connectors.Add(conn);
            }

            newlyPlacedCollapsedNode.EnableReporting();
            currentWorkspace.EnableReporting();

            newNodeWorkspace.WatchChanges = true;
        }