Example #1
0
        private void VmOnWorkspacePropertyEditRequested(WorkspaceModel workspace)
        {
            // copy these strings
            var newName        = workspace.Name.Substring(0);
            var newCategory    = workspace.Category.Substring(0);
            var newDescription = workspace.Description.Substring(0);

            var args = new FunctionNamePromptEventArgs
            {
                Name        = newName,
                Description = newDescription,
                Category    = newCategory,
                CanEditName = false
            };

            this.ViewModel.DynamoViewModel.Model.OnRequestsFunctionNamePrompt(this, args);

            if (args.Success)
            {
                if (workspace is CustomNodeWorkspaceModel)
                {
                    var def = (workspace as CustomNodeWorkspaceModel).CustomNodeDefinition;
                    this.ViewModel.DynamoViewModel.Model.CustomNodeManager.Refactor(def.FunctionId, args.CanEditName ? args.Name : workspace.Name, args.Category, args.Description);
                }

                if (args.CanEditName)
                {
                    workspace.Name = args.Name;
                }
                workspace.Description = args.Description;
                workspace.Category    = args.Category;
            }
        }
        private void VmOnWorkspacePropertyEditRequested(dynWorkspaceModel workspace)
        {
            // copy these strings
            var newName        = workspace.Name.Substring(0);
            var newCategory    = workspace.Category.Substring(0);
            var newDescription = workspace.Description.Substring(0);

            var args = new FunctionNamePromptEventArgs
            {
                Name        = newName,
                Description = newDescription,
                Category    = newCategory
            };

            dynSettings.Controller.DynamoModel.OnRequestsFunctionNamePrompt(this, args);

            if (args.Success)
            {
                if (workspace is FuncWorkspace)
                {
                    var def = dynSettings.CustomNodeManager.GetDefinitionFromWorkspace(workspace);
                    dynSettings.CustomNodeManager.Refactor(def.FunctionId, args.Name, args.Category, args.Description);
                }

                workspace.Name        = args.Name;
                workspace.Description = args.Description;
                workspace.Category    = args.Category;
                // workspace.Author = "";
            }
        }
Example #3
0
        private void EditCustomNodeProperties()
        {
            CustomNodeInfo info;
            var            model = dynamoViewModel.Model;

            if (!model.CustomNodeManager.TryGetNodeInfo(functionNodeModel.Definition.FunctionId,
                                                        out info))
            {
                return;
            }

            // copy these strings
            var newName        = info.Name.Substring(0);
            var newCategory    = info.Category.Substring(0);
            var newDescription = info.Description.Substring(0);

            var args = new FunctionNamePromptEventArgs
            {
                Name        = newName,
                Description = newDescription,
                Category    = newCategory,
                CanEditName = false
            };

            model.OnRequestsFunctionNamePrompt(functionNodeModel, args);

            if (args.Success)
            {
                SerializeCustomNodeWorkspaceWithNewInfo(args, dynamoViewModel, functionNodeModel);
            }
        }
Example #4
0
        private void VmOnWorkspacePropertyEditRequested(WorkspaceModel workspace)
        {
            var customNodeWs = workspace as CustomNodeWorkspaceModel;

            if (customNodeWs != null)
            {
                // copy these strings
                var newName        = customNodeWs.Name.Substring(0);
                var newCategory    = customNodeWs.Category.Substring(0);
                var newDescription = customNodeWs.Description.Substring(0);

                var args = new FunctionNamePromptEventArgs
                {
                    Name        = newName,
                    Description = newDescription,
                    Category    = newCategory,
                    CanEditName = false
                };

                this.ViewModel.DynamoViewModel.Model.OnRequestsFunctionNamePrompt(this, args);

                if (args.Success)
                {
                    customNodeWs.SetInfo(
                        args.CanEditName ? args.Name : workspace.Name,
                        args.Category,
                        args.Description);
                }
            }
        }
Example #5
0
        /// <summary>
        /// Presents the function name dialogue. Returns true if the user enters
        /// a function name and category.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="category"></param>
        /// <returns></returns>
        public void ShowNewFunctionDialog(FunctionNamePromptEventArgs e)
        {
            string error = "";

            do
            {
                //var dialog = new FunctionNamePrompt(dynSettings.Controller.SearchViewModel.Categories, error);
                var dialog = new FunctionNamePrompt(dynSettings.Controller.SearchViewModel.Categories)
                {
                    nameBox     = { Text = e.Name },
                    categoryBox = { Text = e.Category }
                };

                if (dialog.ShowDialog() != true)
                {
                    e.Success = false;
                    return;
                }

                if (String.IsNullOrEmpty(dialog.Text))
                {
                    error = "You must supply a name.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else if (e.Name != dialog.Text && dynSettings.Controller.CustomNodeManager.Contains(dialog.Text))
                {
                    error = "A custom node with the given name already exists.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else if (e.Name != dialog.Text && dynSettings.Controller.BuiltInTypesByNickname.ContainsKey(dialog.Text))
                {
                    error = "A built-in node with the given name already exists.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else if (e.Category.Equals(""))
                {
                    error = "You must enter a new category or choose one from the existing categories.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else
                {
                    error = "";
                }

                e.Name        = dialog.Text;
                e.Category    = dialog.Category;
                e.Description = dialog.Description;
            } while (!error.Equals(""));

            e.Success = true;
        }
Example #6
0
        /// <summary>
        ///     Collapse a set of nodes in this workspace
        /// </summary>
        /// <param name="selectedNodes"> The function definition for the user-defined node </param>
        internal void CollapseNodes(IEnumerable <NodeModel> selectedNodes)
        {
            var args = new FunctionNamePromptEventArgs();

            DynamoViewModel.Model.OnRequestsFunctionNamePrompt(null, args);

            if (!args.Success)
            {
                return;
            }

            DynamoViewModel.Model.CustomNodeManager.Collapse(
                selectedNodes, Model, DynamoModel.IsTestMode, args);
        }
Example #7
0
        public void CustomNodeEditNodeDescriptionKeepingViewBlockInDyf()
        {
            // new custom node
            var dynamoModel = ViewModel.Model;
            var nodeName    = "Cool node";
            var catName     = "Custom Nodes";
            var initialId   = Guid.NewGuid();

            var def       = dynamoModel.CustomNodeManager.CreateCustomNode(nodeName, catName, "", initialId, true);
            var workspace = (CustomNodeWorkspaceModel)def;

            // Set file path
            workspace.FileName = GetNewFileNameOnTempPath("dyf");
            // search common base name
            ViewModel.SearchViewModel.Visible = true;
            ViewModel.SearchViewModel.SearchAndUpdateResults("Cool");
            // results are correct
            Assert.AreEqual(1, ViewModel.SearchViewModel.FilteredResults.Count());

            var newCustNodeInstance = dynamoModel.CustomNodeManager.CreateCustomNodeInstance(initialId);

            dynamoModel.CurrentWorkspace.AddAndRegisterNode(newCustNodeInstance, false);

            // run expression
            Assert.AreEqual(1, dynamoModel.CurrentWorkspace.Nodes.Count());

            // Mimic UI behavior so that custom node updates node name
            FunctionNodeViewCustomization temp = new FunctionNodeViewCustomization();
            FunctionNamePromptEventArgs   args = new FunctionNamePromptEventArgs
            {
                Success     = true,
                Name        = "Cool node_v2",
                Category    = "Custom Nodes",
                CanEditName = false
            };

            temp.SerializeCustomNodeWorkspaceWithNewInfo(args, ViewModel, newCustNodeInstance);

            // Check if serialized workspace still have view block
            string fileContents = File.ReadAllText(workspace.FileName);
            var    Jobject      = Newtonsoft.Json.Linq.JObject.Parse(fileContents);

            Assert.IsTrue(Jobject["View"] != null);
            Assert.IsTrue(Jobject["Name"].ToString() == "Cool node_v2");
        }
        /// <summary>
        /// Collapse a set of nodes and notes currently selected in workspace
        /// </summary>
        internal void CollapseSelectedNodes()
        {
            var args = new FunctionNamePromptEventArgs();

            DynamoViewModel.Model.OnRequestsFunctionNamePrompt(null, args);

            if (!args.Success)
            {
                return;
            }

            var selectedNodes = DynamoSelection.Instance.Selection.OfType <NodeModel>();
            var selectedNotes = DynamoSelection.Instance.Selection.OfType <NoteModel>();

            DynamoViewModel.Model.AddCustomNodeWorkspace(
                DynamoViewModel.Model.CustomNodeManager.Collapse(selectedNodes,
                                                                 selectedNotes, Model, args));
        }
Example #9
0
        /// <summary>
        /// Collapse a set of nodes and notes currently selected in workspace
        /// </summary>
        internal void CollapseSelectedNodes()
        {
            var args = new FunctionNamePromptEventArgs();
            DynamoViewModel.Model.OnRequestsFunctionNamePrompt(null, args);

            if (!args.Success)
                return;

            var selectedNodes = DynamoSelection.Instance.Selection.OfType<NodeModel>();
            var selectedNotes = DynamoSelection.Instance.Selection.OfType<NoteModel>();

            DynamoViewModel.Model.AddCustomNodeWorkspace(
                DynamoViewModel.Model.CustomNodeManager.Collapse(selectedNodes,
                selectedNotes, Model, DynamoModel.IsTestMode, args));

            Dynamo.Logging.Analytics.TrackCommandEvent("NewCustomNode",
                "NodeCount", selectedNodes.Count());
        }
Example #10
0
        /// <summary>
        /// Serialize and update dyf based on FunctionNamePromptEventArgs
        /// </summary>
        /// <param name="args">FunctionNamePromptEventArgs which contains updated dyf info</param>
        /// <param name="dynamoViewModel">Dynamo View Model</param>
        /// <param name="functionNodeModel">Custom Node</param>
        internal void SerializeCustomNodeWorkspaceWithNewInfo(FunctionNamePromptEventArgs args, DynamoViewModel dynamoViewModel, Function functionNodeModel)
        {
            CustomNodeWorkspaceModel ws;

            dynamoViewModel.Model.CustomNodeManager.TryGetFunctionWorkspace(
                functionNodeModel.Definition.FunctionId,
                DynamoModel.IsTestMode,
                out ws);
            ws.SetInfo(args.Name, args.Category, args.Description);

            if (!string.IsNullOrEmpty(ws.FileName))
            {
                // Construct a temp WorkspaceViewModel based on the CustomNodeWorkspaceModel
                // for serialization. We need to do so because only CustomNodeWorkspaceModel
                // is accessible at this point, the dyf is not guaranteed to be opened
                WorkspaceViewModel temp = new WorkspaceViewModel(ws, dynamoViewModel);
                temp.Save(ws.FileName);
                temp.Dispose();
            }
        }
Example #11
0
        public void CollapsedNodeWOrkspaceIsAddedToDynamoWithUnsavedChanges()
        {
            NodeModel node;

            if (!CurrentDynamoModel.NodeFactory.CreateNodeFromTypeName("CoreNodeModels.Input.DoubleInput", out node))
            {
                throw new Exception("Failed to create node!");
            }

            var selectionSet = new[] { node };

            DynamoModel.FunctionNamePromptRequestHandler del = (sender, args) =>
            {
                args.Category    = "Testing";
                args.Description = "";
                args.Name        = "__CollapseTest__";
                args.Success     = true;
            };

            CurrentDynamoModel.RequestsFunctionNamePrompt += del;

            var arg = new FunctionNamePromptEventArgs();

            CurrentDynamoModel.OnRequestsFunctionNamePrompt(null, arg);
            Assert.IsTrue(arg.Success);

            CurrentDynamoModel.AddCustomNodeWorkspace(
                CurrentDynamoModel.CustomNodeManager.Collapse(
                    selectionSet, Enumerable.Empty <NoteModel>(),
                    CurrentDynamoModel.CurrentWorkspace, DynamoModel.IsTestMode, arg));

            Assert.IsNotNull(CurrentDynamoModel.CurrentWorkspace.FirstNodeFromWorkspace <Function>());

            Assert.AreEqual(1, CurrentDynamoModel.CurrentWorkspace.Nodes.Count());
            Assert.AreEqual(2, CurrentDynamoModel.Workspaces.Count());

            var customWorkspace = CurrentDynamoModel.Workspaces.ElementAt(1);

            Assert.AreEqual("__CollapseTest__", customWorkspace.Name);
            Assert.IsTrue(customWorkspace.HasUnsavedChanges);
        }
Example #12
0
        private void EditCustomNodeProperties()
        {
            CustomNodeInfo info;
            var            model = dynamoViewModel.Model;

            if (!model.CustomNodeManager.TryGetNodeInfo(functionNodeModel.Definition.FunctionId,
                                                        out info))
            {
                return;
            }

            // copy these strings
            var newName        = info.Name.Substring(0);
            var newCategory    = info.Category.Substring(0);
            var newDescription = info.Description.Substring(0);

            var args = new FunctionNamePromptEventArgs
            {
                Name        = newName,
                Description = newDescription,
                Category    = newCategory,
                CanEditName = false
            };

            model.OnRequestsFunctionNamePrompt(functionNodeModel, args);

            if (args.Success)
            {
                CustomNodeWorkspaceModel ws;
                model.CustomNodeManager.TryGetFunctionWorkspace(
                    functionNodeModel.Definition.FunctionId,
                    DynamoModel.IsTestMode,
                    out ws);
                ws.SetInfo(args.Name, args.Category, args.Description);

                if (!string.IsNullOrEmpty(ws.FileName))
                {
                    ws.Save(model.EngineController.LiveRunnerRuntimeCore);
                }
            }
        }
Example #13
0
        public void CustomNodeEditNodeDescriptionKeepingIsVisibleInDynamoLibraryInDyf()
        {
            // Open an existing dyf
            var dynamoModel = ViewModel.Model;
            var examplePath = Path.Combine(TestDirectory, @"core\combine", "Sequence_Json.dyf");

            ViewModel.OpenCommand.Execute(examplePath);
            var customNodeWorkspace = dynamoModel.CurrentWorkspace;

            var initialId           = new Guid("6aecda57-7679-4afb-aa02-05a75cc3433e");
            var newCustNodeInstance = dynamoModel.CustomNodeManager.CreateCustomNodeInstance(initialId, null, true);

            // Switch HomeWorkspace and place custom node on it
            dynamoModel.CurrentWorkspace = dynamoModel.Workspaces.First();
            dynamoModel.CurrentWorkspace.AddAndRegisterNode(newCustNodeInstance, false);

            // run expression
            Assert.AreEqual(1, dynamoModel.CurrentWorkspace.Nodes.Count());

            // Mimic UI behavior so that custom node updates node name
            FunctionNodeViewCustomization temp = new FunctionNodeViewCustomization();
            FunctionNamePromptEventArgs   args = new FunctionNamePromptEventArgs
            {
                Success     = true,
                Name        = "Sequence2",
                Category    = "Misc",
                CanEditName = false,
                Description = "test node"
            };

            temp.SerializeCustomNodeWorkspaceWithNewInfo(args, ViewModel, newCustNodeInstance);

            // Check if serialized workspace still have view block
            string fileContents = File.ReadAllText(customNodeWorkspace.FileName);
            var    Jobject      = Newtonsoft.Json.Linq.JObject.Parse(fileContents);

            Assert.IsTrue(Jobject["View"] != null);
            // Expect matching value
            Assert.IsTrue(Jobject["View"]["Dynamo"]["IsVisibleInDynamoLibrary"].ToString() == "True");
        }
Example #14
0
        private void EditCustomNodeProperties()
        {
            var workspace = Definition.WorkspaceModel;

            // copy these strings
            var newName        = workspace.Name.Substring(0);
            var newCategory    = workspace.Category.Substring(0);
            var newDescription = workspace.Description.Substring(0);

            var args = new FunctionNamePromptEventArgs
            {
                Name        = newName,
                Description = newDescription,
                Category    = newCategory,
                CanEditName = false
            };

            Workspace.DynamoModel.OnRequestsFunctionNamePrompt(this, args);

            if (args.Success)
            {
                if (workspace is CustomNodeWorkspaceModel)
                {
                    var def = (workspace as CustomNodeWorkspaceModel).CustomNodeDefinition;
                    this.Workspace.DynamoModel.CustomNodeManager.Refactor(def.FunctionId, args.CanEditName ? args.Name : workspace.Name, args.Category, args.Description);
                }

                if (args.CanEditName)
                {
                    workspace.Name = args.Name;
                }
                workspace.Description = args.Description;
                workspace.Category    = args.Category;

                if (workspace.FileName != null)
                {
                    workspace.Save();
                }
            }
        }
Example #15
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>
        public static void Collapse(IEnumerable <NodeModel> selectedNodes, WorkspaceModel currentWorkspace, FunctionNamePromptEventArgs args = null)
        {
            var selectedNodeSet = new HashSet <NodeModel>(selectedNodes);

            if (args == null || !args.Success)
            {
                args = new FunctionNamePromptEventArgs();
                dynSettings.Controller.DynamoModel.OnRequestsFunctionNamePrompt(null, args);

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

            var newNodeWorkspace = new CustomNodeWorkspaceModel(args.Name, args.Category, args.Description, 0, 0)
            {
                WatchChanges      = false,
                HasUnsavedChanges = true
            };

            var newNodeDefinition = new FunctionDefinition(Guid.NewGuid())
            {
                WorkspaceModel = 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 <NodeModel, int, Tuple <int, NodeModel> > >(
                selectedNodeSet.SelectMany(
                    node => Enumerable.Range(0, node.InPortData.Count).Where(node.HasConnectedInput)
                    .Select(data => Tuple.Create(node, data, node.Inputs[data]))
                    .Where(input => !selectedNodeSet.Contains(input.Item3.Item2))));

            var outputs = new HashSet <Tuple <NodeModel, int, Tuple <int, NodeModel> > >(
                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 <NodeModel>(
                    inputs
                    .Select(x => x.Item3.Item2)
                    .Intersect(outputs.Select(x => x.Item3.Item2)))
                .Select(
                    outerNode =>
            {
                var node = new Apply1();

                //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();

                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 <ConnectorModel>(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)
            {
                ele.SaveResult = false;
                currentWorkspace.Nodes.Remove(ele);
                ele.WorkSpace = newNodeWorkspace;
            }
            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 (NodeModel 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 (ConnectorModel connector in removeConnectors)
            {
                connector.NotifyConnectedPortsOfDeletion();
                currentWorkspace.Connectors.Remove(connector);
            }

            #endregion

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

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

            #region Process inputs

            var uniqueInputSenders = new Dictionary <Tuple <NodeModel, int>, Symbol>();

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

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

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

                Symbol 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 Symbol
                    {
                        InputSymbol = 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();

                    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 = ConnectorModel.Make(node,
                                                    inputReceiverNode,
                                                    0,
                                                    inputReceiverData,
                                                    PortType.INPUT);

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

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

                    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 <NodeModel, int> >();

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

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

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

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

                    //Create Symbol Node
                    var node = new Output
                    {
                        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();

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

                    var conn = ConnectorModel.Make(
                        outputSenderNode,
                        node,
                        outputSenderData,
                        0,
                        PortType.INPUT);

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

                    i++;
                }
            }

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

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

                int       outputReceiverData = output.Item3.Item1;
                NodeModel 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 = ConnectorModel.Make(
                        outputSenderNode,
                        curriedNode.InnerNode,
                        outputSenderData,
                        targetPortIndex + 1,
                        PortType.INPUT);

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

            #endregion

            // save and load the definition from file
            newNodeDefinition.SyncWithWorkspace(true, true);
            dynSettings.Controller.DynamoModel.Workspaces.Add(newNodeWorkspace);

            string name          = newNodeDefinition.FunctionId.ToString();
            var    collapsedNode = dynSettings.Controller.DynamoModel.CreateNode(avgX, avgY, name);

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

            collapsedNode.DisableReporting();

            foreach (var nodeTuple in inConnectors)
            {
                var conn = ConnectorModel.Make(
                    nodeTuple.Item1,
                    collapsedNode,
                    nodeTuple.Item2,
                    nodeTuple.Item3,
                    PortType.INPUT);

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

            foreach (var nodeTuple in outConnectors)
            {
                var conn = ConnectorModel.Make(
                    collapsedNode,
                    nodeTuple.Item1,
                    nodeTuple.Item2,
                    nodeTuple.Item3,
                    PortType.INPUT);

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

            collapsedNode.EnableReporting();
            currentWorkspace.EnableReporting();

            newNodeWorkspace.WatchChanges = true;
        }
Example #16
0
        /// <summary>
        ///     Collapse a set of nodes in a given workspace.
        /// </summary>
        /// <param name="selectedNodes"> The function definition for the user-defined node </param>
        /// <param name="selectedNotes"> The note models in current selection </param>
        /// <param name="currentWorkspace"> The workspace where</param>
        /// <param name="args"></param>
        internal CustomNodeWorkspaceModel Collapse(
            IEnumerable <NodeModel> selectedNodes,
            IEnumerable <NoteModel> selectedNotes,
            WorkspaceModel currentWorkspace,
            FunctionNamePromptEventArgs args)
        {
            var selectedNodeSet = new HashSet <NodeModel>(selectedNodes);
            // Note that undoable actions are only recorded for the "currentWorkspace",
            // the nodes which get moved into "newNodeWorkspace" are not recorded for undo,
            // even in the new workspace. Their creations will simply be treated as part of
            // the opening of that new workspace (i.e. when a user opens a file, she will
            // not expect the nodes that show up to be undoable).
            //
            // After local nodes are moved into "newNodeWorkspace" as the result of
            // conversion, if user performs an undo, new set of nodes will be created in
            // "currentWorkspace" (not moving those nodes in the "newNodeWorkspace" back
            // into "currentWorkspace"). In another word, undo recording is on a per-
            // workspace basis, it does not work across different workspaces.
            //
            UndoRedoRecorder undoRecorder = currentWorkspace.UndoRecorder;

            CustomNodeWorkspaceModel newWorkspace;

            Debug.WriteLine("Current workspace has {0} nodes and {1} connectors",
                            currentWorkspace.Nodes.Count(), currentWorkspace.Connectors.Count());

            using (undoRecorder.BeginActionGroup())
            {
                #region Determine Inputs and Outputs

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

                var outputs =
                    new HashSet <Tuple <NodeModel, int, Tuple <int, NodeModel> > >(
                        selectedNodeSet.SelectMany(
                            node =>
                            Enumerable.Range(0, node.OutPorts.Count)
                            .Where(index => node.OutPorts[index].Connectors.Any())
                            .SelectMany(
                                data =>
                                node.OutputNodes[data].Where(
                                    output => !selectedNodeSet.Contains(output.Item2))
                                .Select(output => Tuple.Create(node, data, output)))));

                #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);

                double leftShift = leftMost - 250;

                #endregion

                #region Handle full selected connectors

                // Step 2: Determine all the connectors whose start/end owners are
                // both in the selection set, and then move them from the current
                // workspace into the new workspace.

                var fullySelectedConns = new HashSet <ConnectorModel>(
                    currentWorkspace.Connectors.Where(
                        conn =>
                {
                    bool startSelected = selectedNodeSet.Contains(conn.Start.Owner);
                    bool endSelected   = selectedNodeSet.Contains(conn.End.Owner);
                    return(startSelected && endSelected);
                }));

                foreach (var connector in fullySelectedConns)
                {
                    undoRecorder.RecordDeletionForUndo(connector);
                    connector.Delete();
                }

                #endregion

                #region Handle partially selected connectors

                // Step 3: Partially selected connectors (either one of its start
                // and end owners is in the selection) are to be destroyed.

                var partiallySelectedConns =
                    currentWorkspace.Connectors.Where(
                        conn =>
                        selectedNodeSet.Contains(conn.Start.Owner) ||
                        selectedNodeSet.Contains(conn.End.Owner)).ToList();

                foreach (var connector in partiallySelectedConns)
                {
                    undoRecorder.RecordDeletionForUndo(connector);
                    connector.Delete();
                }

                #endregion

                #region Transfer nodes and connectors to new workspace

                var newNodes       = new List <NodeModel>();
                var newNotes       = new List <NoteModel>();
                var newAnnotations = new List <AnnotationModel>();

                // Step 4: move all nodes and notes to new workspace remove from old
                // PB: This could be more efficiently handled by a copy paste, but we
                // are preservering the node
                foreach (var node in selectedNodeSet)
                {
                    undoRecorder.RecordDeletionForUndo(node);
                    currentWorkspace.RemoveAndDisposeNode(node);

                    // Assign a new guid to this node, otherwise when node is
                    // compiled to AST, literally it is still in global scope
                    // instead of in function scope.
                    node.GUID = Guid.NewGuid();

                    // shift nodes
                    node.X = node.X - leftShift;
                    node.Y = node.Y - topMost;

                    newNodes.Add(node);
                }

                foreach (var note in selectedNotes)
                {
                    undoRecorder.RecordDeletionForUndo(note);
                    currentWorkspace.RemoveNote(note);

                    note.GUID = Guid.NewGuid();
                    note.X    = note.X - leftShift;
                    note.Y    = note.Y - topMost;
                    newNotes.Add(note);
                }

                //Copy the group from newNodes
                foreach (var group in DynamoSelection.Instance.Selection.OfType <AnnotationModel>())
                {
                    undoRecorder.RecordDeletionForUndo(group);
                    currentWorkspace.RemoveGroup(group);

                    group.GUID  = Guid.NewGuid();
                    group.Nodes = group.DeletedModelBases;
                    newAnnotations.Add(group);
                }

                // Now all selected nodes already moved to custom workspace,
                // clear the selection.
                DynamoSelection.Instance.ClearSelection();

                foreach (var conn in fullySelectedConns)
                {
                    ConnectorModel.Make(conn.Start.Owner, conn.End.Owner, conn.Start.Index, conn.End.Index);
                }

                #endregion

                #region Process inputs

                var inConnectors       = new List <Tuple <NodeModel, int> >();
                var uniqueInputSenders = new Dictionary <Tuple <NodeModel, int>, Symbol>();

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

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

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

                    Symbol node;

                    var key = Tuple.Create(inputNode, inputData);
                    if (uniqueInputSenders.ContainsKey(key))
                    {
                        node = uniqueInputSenders[key];
                    }
                    else
                    {
                        inConnectors.Add(Tuple.Create(inputNode, inputData));

                        node = new Symbol
                        {
                            InputSymbol = inputReceiverNode.InPorts[inputReceiverData].Name,
                            X           = 0
                        };

                        // Try to figure out the type of input of custom node
                        // from the type of input of selected node. There are
                        // two kinds of nodes whose input type are available:
                        // function node and custom node.
                        List <Library.TypedParameter> parameters = null;

                        if (inputReceiverNode is EFunction)
                        {
                            var func = inputReceiverNode as EFunction;
                            parameters = func.Controller.Definition.Parameters.ToList();
                        }
                        else if (inputReceiverNode is DSFunctionBase)
                        {
                            var dsFunc   = inputReceiverNode as DSFunctionBase;
                            var funcDesc = dsFunc.Controller.Definition;
                            parameters = funcDesc.Parameters.ToList();

                            if (funcDesc.Type == Engine.FunctionType.InstanceMethod ||
                                funcDesc.Type == Engine.FunctionType.InstanceProperty)
                            {
                                var dummyType = new ProtoCore.Type()
                                {
                                    Name = funcDesc.ClassName
                                };
                                var instanceParam = new TypedParameter(funcDesc.ClassName, dummyType);
                                parameters.Insert(0, instanceParam);
                            }
                        }

                        // so the input of custom node has format
                        //    input_var_name : type
                        if (parameters != null && parameters.Count() > inputReceiverData)
                        {
                            var typeName = parameters[inputReceiverData].DisplayTypeName;
                            if (!string.IsNullOrEmpty(typeName))
                            {
                                node.InputSymbol += " : " + typeName;
                            }
                        }

                        node.SetNameFromNodeNameAttribute();
                        node.Y = inputIndex * (50 + node.Height);

                        uniqueInputSenders[key] = node;

                        newNodes.Add(node);
                    }

                    ConnectorModel.Make(node, inputReceiverNode, 0, inputReceiverData);
                }

                #endregion

                #region Process outputs

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

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

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

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

                            //Create Symbol Node
                            var node = new Output
                            {
                                Symbol = outputSenderNode.OutPorts[outputSenderData].Name,
                                X      = rightMost + 75 - leftShift
                            };

                            node.Y = i * (50 + node.Height);

                            node.SetNameFromNodeNameAttribute();

                            newNodes.Add(node);
                            ConnectorModel.Make(outputSenderNode, node, outputSenderData, 0);

                            i++;
                        }
                    }

                    //Connect outputs to new node
                    outConnectors.AddRange(
                        from output in outputs
                        let outputSenderNode = output.Item1
                                               let outputSenderData = output.Item2
                                                                      let outputReceiverData = output.Item3.Item1
                                                                                               let outputReceiverNode = output.Item3.Item2
                                                                                                                        select
                                                                                                                        Tuple.Create(
                            outputReceiverNode,
                            outportList.FindIndex(
                                x => x.Item1 == outputSenderNode && x.Item2 == outputSenderData),
                            outputReceiverData));
                }
                else
                {
                    foreach (var hanging in
                             selectedNodeSet.SelectMany(
                                 node =>
                                 Enumerable.Range(0, node.OutPorts.Count)
                                 .Where(index => !node.OutPorts[index].IsConnected)
                                 .Select(port => new { node, port })).Distinct())
                    {
                        //Create Symbol Node
                        var node = new Output
                        {
                            Symbol = hanging.node.OutPorts[hanging.port].Name,
                            X      = rightMost + 75 - leftShift
                        };
                        node.Y = i * (50 + node.Height);
                        node.SetNameFromNodeNameAttribute();

                        newNodes.Add(node);
                        ConnectorModel.Make(hanging.node, node, hanging.port, 0);

                        i++;
                    }
                }

                #endregion

                var newId = Guid.NewGuid();
                newWorkspace = new CustomNodeWorkspaceModel(
                    nodeFactory,
                    newNodes,
                    newNotes,
                    newAnnotations,
                    Enumerable.Empty <PresetModel>(),
                    currentWorkspace.ElementResolver,
                    new WorkspaceInfo()
                {
                    X           = 0,
                    Y           = 0,
                    Name        = args.Name,
                    Category    = args.Category,
                    Description = args.Description,
                    ID          = newId.ToString(),
                    FileName    = string.Empty,
                    IsVisibleInDynamoLibrary = true
                });

                newWorkspace.HasUnsavedChanges = true;

                RegisterCustomNodeWorkspace(newWorkspace);

                Debug.WriteLine("Collapsed workspace has {0} nodes and {1} connectors",
                                newWorkspace.Nodes.Count(), newWorkspace.Connectors.Count());

                var collapsedNode = CreateCustomNodeInstance(newId);
                collapsedNode.X = avgX;
                collapsedNode.Y = avgY;
                currentWorkspace.AddAndRegisterNode(collapsedNode, centered: false);
                undoRecorder.RecordCreationForUndo(collapsedNode);

                foreach (var connector in
                         inConnectors.Select((x, idx) => new { node = x.Item1, from = x.Item2, to = idx })
                         .Select(
                             nodeTuple =>
                             ConnectorModel.Make(
                                 nodeTuple.node,
                                 collapsedNode,
                                 nodeTuple.@from,
                                 nodeTuple.to))
                         .Where(connector => connector != null))
                {
                    undoRecorder.RecordCreationForUndo(connector);
                }

                foreach (var connector in
                         outConnectors.Select(
                             nodeTuple =>
                             ConnectorModel.Make(
                                 collapsedNode,
                                 nodeTuple.Item1,
                                 nodeTuple.Item2,
                                 nodeTuple.Item3)).Where(connector => connector != null))
                {
                    undoRecorder.RecordCreationForUndo(connector);
                }
            }
            return(newWorkspace);
        }
Example #17
0
 /// <summary>
 /// Handles the request for the presentation of the function name prompt
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 void _vm_RequestsFunctionNamePrompt(object sender, FunctionNamePromptEventArgs e)
 {
     ShowNewFunctionDialog(e);
 }
Example #18
0
        /// <summary>
        /// Presents the function name dialogue. Returns true if the user enters
        /// a function name and category.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="category"></param>
        /// <returns></returns>
        public void ShowNewFunctionDialog(FunctionNamePromptEventArgs e)
        {
            string error = "";

            do
            {
                var categorized =
                    SearchCategory.CategorizeSearchEntries(
                        dynamoViewModel.Model.SearchModel.SearchEntries,
                        entry => entry.Categories);

                var allCategories =
                    categorized.SubCategories.SelectMany(sub => sub.GetAllCategoryNames());

                var dialog = new FunctionNamePrompt(allCategories)
                {
                    categoryBox      = { Text = e.Category },
                    DescriptionInput = { Text = e.Description },
                    nameView         = { Text = e.Name },
                    nameBox          = { Text = e.Name },
                    // center the prompt
                    Owner = this,
                    WindowStartupLocation = WindowStartupLocation.CenterOwner
                };

                if (e.CanEditName)
                {
                    dialog.nameBox.Visibility  = Visibility.Visible;
                    dialog.nameView.Visibility = Visibility.Collapsed;
                }
                else
                {
                    dialog.nameView.Visibility = Visibility.Visible;
                    dialog.nameBox.Visibility  = Visibility.Collapsed;
                }

                if (dialog.ShowDialog() != true)
                {
                    e.Success = false;
                    return;
                }

                if (String.IsNullOrEmpty(dialog.Text))
                {
                    error = "You must supply a name.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                //else if (e.Name != dialog.Text && dynamoViewModel.Model.BuiltInTypesByNickname.ContainsKey(dialog.Text))
                //{
                //    error = "A built-in node with the given name already exists.";
                //    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                //                                   MessageBoxImage.Error);
                //}
                else if (dialog.Category.Equals(""))
                {
                    error = "You must enter a new category or choose one from the existing categories.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else
                {
                    error = "";
                }

                e.Name        = dialog.Text;
                e.Category    = dialog.Category;
                e.Description = dialog.Description;
            } while (!error.Equals(""));

            e.Success = true;
        }
Example #19
0
 /// <summary>
 /// Handles the request for the presentation of the function name prompt
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 void DynamoViewModelRequestsFunctionNamePrompt(object sender, FunctionNamePromptEventArgs e)
 {
     ShowNewFunctionDialog(e);
 }
Example #20
0
        /// <summary>
        /// Presents the function name dialogue. Returns true if the user enters
        /// a function name and category.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="category"></param>
        /// <returns></returns>
        public void ShowNewFunctionDialog(FunctionNamePromptEventArgs e)
        {
            string error = "";

            do
            {
                var dialog = new FunctionNamePrompt(dynamoViewModel.Model.SearchModel.Categories)
                {
                    categoryBox      = { Text = e.Category },
                    DescriptionInput = { Text = e.Description },
                    nameView         = { Text = e.Name },
                    nameBox          = { Text = e.Name }
                };

                if (e.CanEditName)
                {
                    dialog.nameBox.Visibility  = Visibility.Visible;
                    dialog.nameView.Visibility = Visibility.Collapsed;
                }
                else
                {
                    dialog.nameView.Visibility = Visibility.Visible;
                    dialog.nameBox.Visibility  = Visibility.Collapsed;
                }

                if (dialog.ShowDialog() != true)
                {
                    e.Success = false;
                    return;
                }

                if (String.IsNullOrEmpty(dialog.Text))
                {
                    error = "You must supply a name.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else if (e.Name != dialog.Text && dynamoViewModel.Model.BuiltInTypesByNickname.ContainsKey(dialog.Text))
                {
                    error = "A built-in node with the given name already exists.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else if (dialog.Category.Equals(""))
                {
                    error = "You must enter a new category or choose one from the existing categories.";
                    MessageBox.Show(error, "Custom Node Property Error", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
                else
                {
                    error = "";
                }

                e.Name        = dialog.Text;
                e.Category    = dialog.Category;
                e.Description = dialog.Description;
            } while (!error.Equals(""));

            e.Success = true;
        }
Example #21
0
        /// <summary>
        ///     Collapse a set of nodes in a given workspace.
        /// </summary>
        /// <param name="selectedNodes"> The function definition for the user-defined node </param>
        /// <param name="currentWorkspace"> The workspace where</param>
        /// <param name="isTestMode"></param>
        /// <param name="args"></param>
        public CustomNodeWorkspaceModel Collapse(
            IEnumerable <NodeModel> selectedNodes, WorkspaceModel currentWorkspace,
            bool isTestMode, FunctionNamePromptEventArgs args)
        {
            var selectedNodeSet = new HashSet <NodeModel>(selectedNodes);
            // Note that undoable actions are only recorded for the "currentWorkspace",
            // the nodes which get moved into "newNodeWorkspace" are not recorded for undo,
            // even in the new workspace. Their creations will simply be treated as part of
            // the opening of that new workspace (i.e. when a user opens a file, she will
            // not expect the nodes that show up to be undoable).
            //
            // After local nodes are moved into "newNodeWorkspace" as the result of
            // conversion, if user performs an undo, new set of nodes will be created in
            // "currentWorkspace" (not moving those nodes in the "newNodeWorkspace" back
            // into "currentWorkspace"). In another word, undo recording is on a per-
            // workspace basis, it does not work across different workspaces.
            //
            UndoRedoRecorder undoRecorder = currentWorkspace.UndoRecorder;

            CustomNodeWorkspaceModel newWorkspace;

            using (undoRecorder.BeginActionGroup())
            {
                #region Determine Inputs and Outputs

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

                var outputs =
                    new HashSet <Tuple <NodeModel, int, Tuple <int, NodeModel> > >(
                        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)

                Log(Properties.Resources.CouldNotRepairOneNodeHoles, WarningLevel.Mild);
                // http://adsk-oss.myjetbrains.com/youtrack/issue/MAGN-5603

                //var curriedNodeArgs =
                //    new HashSet<NodeModel>(
                //        inputs.Select(x => x.Item3.Item2)
                //            .Intersect(outputs.Select(x => x.Item3.Item2))).Select(
                //                outerNode =>
                //                {
                //                    //var node = new Apply1();
                //                    var node = newNodeWorkspace.AddNode<Apply1>();
                //                    node.SetNickNameFromAttribute();

                //                    node.DisableReporting();

                //                    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();

                //                    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);

                double leftShift = leftMost - 250;

                #endregion

                #region Handle full selected connectors

                // Step 2: Determine all the connectors whose start/end owners are
                // both in the selection set, and then move them from the current
                // workspace into the new workspace.

                var fullySelectedConns = new HashSet <ConnectorModel>(
                    currentWorkspace.Connectors.Where(
                        conn =>
                {
                    bool startSelected = selectedNodeSet.Contains(conn.Start.Owner);
                    bool endSelected   = selectedNodeSet.Contains(conn.End.Owner);
                    return(startSelected && endSelected);
                }));

                foreach (var connector in fullySelectedConns)
                {
                    undoRecorder.RecordDeletionForUndo(connector);
                    connector.Delete();
                }

                #endregion

                #region Handle partially selected connectors

                // Step 3: Partially selected connectors (either one of its start
                // and end owners is in the selection) are to be destroyed.

                var partiallySelectedConns =
                    currentWorkspace.Connectors.Where(
                        conn =>
                        selectedNodeSet.Contains(conn.Start.Owner) ||
                        selectedNodeSet.Contains(conn.End.Owner)).ToList();

                foreach (var connector in partiallySelectedConns)
                {
                    undoRecorder.RecordDeletionForUndo(connector);
                    connector.Delete();
                }

                #endregion

                #region Transfer nodes and connectors to new workspace

                var newNodes = new List <NodeModel>();

                // Step 4: move all nodes to new workspace remove from old
                // PB: This could be more efficiently handled by a copy paste, but we
                // are preservering the node
                foreach (var node in selectedNodeSet)
                {
                    undoRecorder.RecordDeletionForUndo(node);
                    currentWorkspace.RemoveNode(node);

                    // Assign a new guid to this node, otherwise when node is
                    // compiled to AST, literally it is still in global scope
                    // instead of in function scope.
                    node.GUID = Guid.NewGuid();
                    node.RenderPackages.Clear();

                    // shit nodes
                    node.X = node.X - leftShift;
                    node.Y = node.Y - topMost;

                    newNodes.Add(node);
                }

                foreach (var conn in fullySelectedConns)
                {
                    ConnectorModel.Make(conn.Start.Owner, conn.End.Owner, conn.Start.Index, conn.End.Index);
                }

                #endregion

                #region Process inputs

                var inConnectors       = new List <Tuple <NodeModel, int> >();
                var uniqueInputSenders = new Dictionary <Tuple <NodeModel, int>, Symbol>();

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

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

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

                    Symbol node;

                    var key = Tuple.Create(inputNode, inputData);
                    if (uniqueInputSenders.ContainsKey(key))
                    {
                        node = uniqueInputSenders[key];
                    }
                    else
                    {
                        inConnectors.Add(Tuple.Create(inputNode, inputData));

                        node = new Symbol
                        {
                            InputSymbol = inputReceiverNode.InPortData[inputReceiverData].NickName,
                            X           = 0
                        };

                        // Try to figure out the type of input of custom node
                        // from the type of input of selected node. There are
                        // two kinds of nodes whose input type are available:
                        // function node and custom node.
                        List <Library.TypedParameter> parameters = null;
                        if (inputReceiverNode is Function)
                        {
                            var func = inputReceiverNode as Function;
                            parameters = func.Controller.Definition.Parameters.ToList();
                        }
                        else if (inputReceiverNode is DSFunctionBase)
                        {
                            var dsFunc = inputReceiverNode as DSFunctionBase;
                            parameters = dsFunc.Controller.Definition.Parameters.ToList();
                        }

                        // so the input of custom node has format
                        //    input_var_name : type
                        if (parameters != null && parameters.Count() > inputReceiverData)
                        {
                            var typeName = parameters[inputReceiverData].DisplayTypeName;
                            if (!string.IsNullOrEmpty(typeName))
                            {
                                node.InputSymbol += " : " + typeName;
                            }
                        }

                        node.SetNickNameFromAttribute();
                        node.Y = inputIndex * (50 + node.Height);

                        uniqueInputSenders[key] = node;

                        newNodes.Add(node);
                    }

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

                    //if (curriedNode == null)
                    //{
                    ConnectorModel.Make(node, inputReceiverNode, 0, inputReceiverData);
                    //}
                    //else
                    //{
                    //    //Connect it to the applier
                    //    newNodeWorkspace.AddConnection(node, curriedNode.InnerNode, 0, 0);

                    //    //Connect applier to the inner input receive
                    //    newNodeWorkspace.AddConnection(
                    //        curriedNode.InnerNode,
                    //        inputReceiverNode,
                    //        0,
                    //        inputReceiverData);
                    //}
                }

                #endregion

                #region Process outputs

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

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

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

                            //NodeModel outputReceiverNode = output.Item3.Item2;

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

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

                            //Create Symbol Node
                            var node = new Output
                            {
                                Symbol = outputSenderNode.OutPortData[outputSenderData].NickName,
                                X      = rightMost + 75 - leftShift
                            };

                            node.Y = i * (50 + node.Height);

                            node.SetNickNameFromAttribute();

                            newNodes.Add(node);
                            ConnectorModel.Make(outputSenderNode, node, outputSenderData, 0);

                            i++;
                        }
                    }

                    //Connect outputs to new node
                    outConnectors.AddRange(
                        from output in outputs
                        let outputSenderNode = output.Item1
                                               let outputSenderData = output.Item2
                                                                      let outputReceiverData = output.Item3.Item1
                                                                                               let outputReceiverNode = output.Item3.Item2
                                                                                                                        select
                                                                                                                        Tuple.Create(
                            outputReceiverNode,
                            outportList.FindIndex(
                                x => x.Item1 == outputSenderNode && x.Item2 == outputSenderData),
                            outputReceiverData));
                }
                else
                {
                    foreach (var hanging in
                             selectedNodeSet.SelectMany(
                                 node =>
                                 Enumerable.Range(0, node.OutPortData.Count)
                                 .Where(port => !node.HasOutput(port))
                                 .Select(port => new { node, port })).Distinct())
                    {
                        //Create Symbol Node
                        var node = new Output
                        {
                            Symbol = hanging.node.OutPortData[hanging.port].NickName,
                            X      = rightMost + 75 - leftShift
                        };
                        node.Y = i * (50 + node.Height);
                        node.SetNickNameFromAttribute();

                        newNodes.Add(node);
                        ConnectorModel.Make(hanging.node, node, hanging.port, 0);

                        i++;
                    }
                }

                #endregion

                var newId = Guid.NewGuid();
                newWorkspace = new CustomNodeWorkspaceModel(
                    args.Name,
                    args.Category,
                    args.Description,
                    nodeFactory,
                    newNodes,
                    Enumerable.Empty <NoteModel>(),
                    0,
                    0,
                    newId, currentWorkspace.ElementResolver, string.Empty);

                newWorkspace.HasUnsavedChanges = true;

                RegisterCustomNodeWorkspace(newWorkspace);

                var collapsedNode = CreateCustomNodeInstance(newId, isTestMode: isTestMode);
                collapsedNode.X = avgX;
                collapsedNode.Y = avgY;
                currentWorkspace.AddNode(collapsedNode, centered: false);
                undoRecorder.RecordCreationForUndo(collapsedNode);

                foreach (var connector in
                         inConnectors.Select((x, idx) => new { node = x.Item1, from = x.Item2, to = idx })
                         .Select(
                             nodeTuple =>
                             ConnectorModel.Make(
                                 nodeTuple.node,
                                 collapsedNode,
                                 nodeTuple.@from,
                                 nodeTuple.to))
                         .Where(connector => connector != null))
                {
                    undoRecorder.RecordCreationForUndo(connector);
                }

                foreach (var connector in
                         outConnectors.Select(
                             nodeTuple =>
                             ConnectorModel.Make(
                                 collapsedNode,
                                 nodeTuple.Item1,
                                 nodeTuple.Item2,
                                 nodeTuple.Item3)).Where(connector => connector != null))
                {
                    undoRecorder.RecordCreationForUndo(connector);
                }
            }
            return(newWorkspace);
        }
Example #22
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>
        /// <param name="args"></param>
        public static void Collapse(IEnumerable <NodeModel> selectedNodes, WorkspaceModel currentWorkspace, FunctionNamePromptEventArgs args = null)
        {
            var selectedNodeSet = new HashSet <NodeModel>(selectedNodes);

            if (args == null || !args.Success)
            {
                args = new FunctionNamePromptEventArgs();
                dynSettings.Controller.DynamoViewModel.OnRequestsFunctionNamePrompt(null, args);

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

            // Note that undoable actions are only recorded for the "currentWorkspace",
            // the nodes which get moved into "newNodeWorkspace" are not recorded for undo,
            // even in the new workspace. Their creations will simply be treated as part of
            // the opening of that new workspace (i.e. when a user opens a file, she will
            // not expect the nodes that show up to be undoable).
            //
            // After local nodes are moved into "newNodeWorkspace" as the result of
            // conversion, if user performs an undo, new set of nodes will be created in
            // "currentWorkspace" (not moving those nodes in the "newNodeWorkspace" back
            // into "currentWorkspace"). In another word, undo recording is on a per-
            // workspace basis, it does not work across different workspaces.
            //
            UndoRedoRecorder undoRecorder = currentWorkspace.UndoRecorder;

            undoRecorder.BeginActionGroup();

            var newNodeWorkspace = new CustomNodeWorkspaceModel(args.Name, args.Category, args.Description, 0, 0)
            {
                WatchChanges      = false,
                HasUnsavedChanges = true
            };

            var newNodeDefinition = new CustomNodeDefinition(Guid.NewGuid())
            {
                WorkspaceModel = 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 <NodeModel, int, Tuple <int, NodeModel> > >(
                selectedNodeSet.SelectMany(
                    node => Enumerable.Range(0, node.InPortData.Count).Where(node.HasConnectedInput)
                    .Select(data => Tuple.Create(node, data, node.Inputs[data]))
                    .Where(input => !selectedNodeSet.Contains(input.Item3.Item2))));

            var outputs = new HashSet <Tuple <NodeModel, int, Tuple <int, NodeModel> > >(
                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 <NodeModel>(
                    inputs
                    .Select(x => x.Item3.Item2)
                    .Intersect(outputs.Select(x => x.Item3.Item2)))
                .Select(
                    outerNode =>
            {
                var node = new Apply1();

                //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();

                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 Handle full selected connectors

            // Step 2: Determine all the connectors whose start/end owners are
            // both in the selection set, and then move them from the current
            // workspace into the new workspace.

            var fullySelectedConns = new HashSet <ConnectorModel>(
                currentWorkspace.Connectors.Where(conn =>
            {
                bool startSelected = selectedNodeSet.Contains(conn.Start.Owner);
                bool endSelected   = selectedNodeSet.Contains(conn.End.Owner);
                return(startSelected && endSelected);
            }));

            foreach (var ele in fullySelectedConns)
            {
                undoRecorder.RecordDeletionForUndo(ele);
                currentWorkspace.Connectors.Remove(ele);
            }

            #endregion

            #region Handle partially selected connectors

            // Step 3: Partially selected connectors (either one of its start
            // and end owners is in the selection) are to be destroyed.

            var partiallySelectedConns =
                currentWorkspace.Connectors.Where(
                    conn =>
                    selectedNodeSet.Contains(conn.Start.Owner) ||
                    selectedNodeSet.Contains(conn.End.Owner)).ToList();

            foreach (ConnectorModel connector in partiallySelectedConns)
            {
                undoRecorder.RecordDeletionForUndo(connector);
                connector.NotifyConnectedPortsOfDeletion();
                currentWorkspace.Connectors.Remove(connector);
            }

            #endregion

            #region Transfer nodes and connectors to new workspace

            // Step 4: move all nodes to new workspace remove from old

            foreach (var ele in selectedNodeSet)
            {
                undoRecorder.RecordDeletionForUndo(ele);
                ele.SaveResult = false;
                currentWorkspace.Nodes.Remove(ele);
                ele.WorkSpace = newNodeWorkspace;
            }

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

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

            #endregion

            foreach (var node in newNodeWorkspace.Nodes)
            {
                node.DisableReporting();
            }

            #region Process inputs

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

            var uniqueInputSenders = new Dictionary <Tuple <NodeModel, int>, Symbol>();

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

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

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

                Symbol 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));

                    //Create Symbol Node
                    node = new Symbol
                    {
                        InputSymbol = 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();

                    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 = ConnectorModel.Make(node,
                                                    inputReceiverNode,
                                                    0,
                                                    inputReceiverData,
                                                    PortType.INPUT);

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

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

                    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 <NodeModel, int> >();

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

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

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

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

                        //Create Symbol Node
                        var node = new Output
                        {
                            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();

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

                        var conn = ConnectorModel.Make(
                            outputSenderNode,
                            node,
                            outputSenderData,
                            0,
                            PortType.INPUT);

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

                        i++;
                    }
                }

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

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

                    int       outputReceiverData = output.Item3.Item1;
                    NodeModel 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 = ConnectorModel.Make(
                            outputSenderNode,
                            curriedNode.InnerNode,
                            outputSenderData,
                            targetPortIndex + 1,
                            PortType.INPUT);

                        if (conn != null)
                        {
                            newNodeWorkspace.Connectors.Add(conn);
                        }
                    }
                }
            }
            else
            {
                foreach (var hanging in
                         selectedNodeSet.SelectMany(
                             node =>
                             Enumerable.Range(0, node.OutPortData.Count)
                             .Where(port => !node.HasOutput(port))
                             .Select(port => new { node, port })).Distinct())
                {
                    //Create Symbol Node
                    var node = new Output
                    {
                        Symbol = hanging.node.OutPortData[hanging.port].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();

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

                    var conn = ConnectorModel.Make(
                        hanging.node,
                        node,
                        hanging.port,
                        0,
                        PortType.INPUT);

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

                    i++;
                }
            }

            #endregion

            // save and load the definition from file
            newNodeDefinition.SyncWithWorkspace(true, true);
            dynSettings.Controller.DynamoModel.Workspaces.Add(newNodeWorkspace);

            string name          = newNodeDefinition.FunctionId.ToString();
            var    collapsedNode = dynSettings.Controller.DynamoModel.CreateNode(avgX, avgY, name);
            undoRecorder.RecordCreationForUndo(collapsedNode);

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

            collapsedNode.DisableReporting();

            foreach (var nodeTuple in inConnectors.Select((x, idx) => new { node = x.Item1, from = x.Item2, to = idx }))
            {
                var conn = ConnectorModel.Make(
                    nodeTuple.node,
                    collapsedNode,
                    nodeTuple.from,
                    nodeTuple.to,
                    PortType.INPUT);

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

            foreach (var nodeTuple in outConnectors)
            {
                var conn = ConnectorModel.Make(
                    collapsedNode,
                    nodeTuple.Item1,
                    nodeTuple.Item2,
                    nodeTuple.Item3,
                    PortType.INPUT);

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

            undoRecorder.EndActionGroup();
            collapsedNode.EnableReporting();
            currentWorkspace.EnableReporting();

            foreach (var node in newNodeWorkspace.Nodes)
            {
                node.EnableReporting();
            }

            newNodeWorkspace.WatchChanges = true;
        }
Example #23
0
        /// <summary>
        ///     Collapse a set of nodes in a given workspace.
        /// </summary>
        /// <param name="dynamoModel">The current DynamoModel</param>
        /// <param name="selectedNodes"> The function definition for the user-defined node </param>
        /// <param name="currentWorkspace"> The workspace where</param>
        /// <param name="args"></param>
        public static void Collapse(DynamoModel dynamoModel,
                                    IEnumerable <NodeModel> selectedNodes,
                                    WorkspaceModel currentWorkspace,
                                    FunctionNamePromptEventArgs args = null)
        {
            if (args == null || !args.Success)
            {
                args = new FunctionNamePromptEventArgs();
                dynamoModel.OnRequestsFunctionNamePrompt(null, args);

                if (!args.Success)
                {
                    return;
                }
            }

            var selectedNodeSet = new HashSet <NodeModel>(selectedNodes);
            // Note that undoable actions are only recorded for the "currentWorkspace",
            // the nodes which get moved into "newNodeWorkspace" are not recorded for undo,
            // even in the new workspace. Their creations will simply be treated as part of
            // the opening of that new workspace (i.e. when a user opens a file, she will
            // not expect the nodes that show up to be undoable).
            //
            // After local nodes are moved into "newNodeWorkspace" as the result of
            // conversion, if user performs an undo, new set of nodes will be created in
            // "currentWorkspace" (not moving those nodes in the "newNodeWorkspace" back
            // into "currentWorkspace"). In another word, undo recording is on a per-
            // workspace basis, it does not work across different workspaces.
            //
            UndoRedoRecorder undoRecorder = currentWorkspace.UndoRecorder;

            using (undoRecorder.BeginActionGroup())
            {
                var newNodeWorkspace = new CustomNodeWorkspaceModel(
                    dynamoModel,
                    args.Name,
                    args.Category,
                    args.Description,
                    0,
                    0)
                {
                    WatchChanges = false, HasUnsavedChanges = true
                };

                var newNodeDefinition = new CustomNodeDefinition(Guid.NewGuid())
                {
                    WorkspaceModel = 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 <NodeModel, int, Tuple <int, NodeModel> > >(
                        selectedNodeSet.SelectMany(
                            node =>
                            Enumerable.Range(0, node.InPortData.Count)
                            .Where(node.HasConnectedInput)
                            .Select(data => Tuple.Create(node, data, node.Inputs[data]))
                            .Where(input => !selectedNodeSet.Contains(input.Item3.Item2))));

                var outputs =
                    new HashSet <Tuple <NodeModel, int, Tuple <int, NodeModel> > >(
                        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)

                dynamoModel.Logger.LogWarning("Could not repair 1-node holes", WarningLevel.Mild);

                // http://adsk-oss.myjetbrains.com/youtrack/issue/MAGN-5603
                //var curriedNodeArgs =
                //    new HashSet<NodeModel>(
                //        inputs.Select(x => x.Item3.Item2)
                //            .Intersect(outputs.Select(x => x.Item3.Item2))).Select(
                //                outerNode =>
                //                {
                //                    //var node = new Apply1();
                //                    var node = newNodeWorkspace.AddNode<Apply1>();
                //                    node.SetNickNameFromAttribute();

                //                    node.DisableReporting();

                //                    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();

                //                    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 Handle full selected connectors

                // Step 2: Determine all the connectors whose start/end owners are
                // both in the selection set, and then move them from the current
                // workspace into the new workspace.

                var fullySelectedConns =
                    new HashSet <ConnectorModel>(
                        currentWorkspace.Connectors.Where(
                            conn =>
                {
                    bool startSelected = selectedNodeSet.Contains(conn.Start.Owner);
                    bool endSelected   = selectedNodeSet.Contains(conn.End.Owner);
                    return(startSelected && endSelected);
                }));

                foreach (var ele in fullySelectedConns)
                {
                    undoRecorder.RecordDeletionForUndo(ele);
                    currentWorkspace.Connectors.Remove(ele);
                }

                #endregion

                #region Handle partially selected connectors

                // Step 3: Partially selected connectors (either one of its start
                // and end owners is in the selection) are to be destroyed.

                var partiallySelectedConns =
                    currentWorkspace.Connectors.Where(
                        conn =>
                        selectedNodeSet.Contains(conn.Start.Owner) ||
                        selectedNodeSet.Contains(conn.End.Owner)).ToList();

                foreach (ConnectorModel connector in partiallySelectedConns)
                {
                    undoRecorder.RecordDeletionForUndo(connector);
                    connector.NotifyConnectedPortsOfDeletion();
                    currentWorkspace.Connectors.Remove(connector);
                }

                #endregion

                #region Transfer nodes and connectors to new workspace

                // Step 4: move all nodes to new workspace remove from old
                // PB: This could be more efficiently handled by a copy paste, but we
                // are preservering the node
                foreach (var ele in selectedNodeSet)
                {
                    undoRecorder.RecordDeletionForUndo(ele);
                    ele.SaveResult = false;
                    currentWorkspace.Nodes.Remove(ele);

                    // VisualizationManager and EngineController subscribe
                    // NodeDeleted event, so notify them that this node has
                    // been deleted.
                    dynamoModel.OnNodeDeleted(ele);

                    // Assign a new guid to this node, otherwise when node is
                    // compiled to AST, literally it is still in global scope
                    // instead of in function scope.
                    ele.GUID = Guid.NewGuid();
                    ele.RenderPackages.Clear();
                    ele.Workspace = newNodeWorkspace;
                }

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

                foreach (var node in newNodeWorkspace.Nodes)
                {
                    node.DisableReporting();
                }

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

                #endregion


                #region Process inputs

                var inConnectors       = new List <Tuple <NodeModel, int> >();
                var uniqueInputSenders = new Dictionary <Tuple <NodeModel, int>, Symbol>();

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

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

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

                    Symbol node;

                    var key = Tuple.Create(inputNode, inputData);
                    if (uniqueInputSenders.ContainsKey(key))
                    {
                        node = uniqueInputSenders[key];
                    }
                    else
                    {
                        inConnectors.Add(Tuple.Create(inputNode, inputData));

                        node             = newNodeWorkspace.AddNode <Symbol>();
                        node.InputSymbol = inputReceiverNode.InPortData[inputReceiverData].NickName;

                        node.SetNickNameFromAttribute();

                        node.DisableReporting();

                        node.X = 0;
                        node.Y = inputIndex * (50 + node.Height);

                        uniqueInputSenders[key] = node;
                    }

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

                    //if (curriedNode == null)
                    //{
                    newNodeWorkspace.AddConnection(
                        node,
                        inputReceiverNode,
                        0,
                        inputReceiverData);
                    //}
                    //else
                    //{
                    //    //Connect it to the applier
                    //    newNodeWorkspace.AddConnection(node, curriedNode.InnerNode, 0, 0);

                    //    //Connect applier to the inner input receive
                    //    newNodeWorkspace.AddConnection(
                    //        curriedNode.InnerNode,
                    //        inputReceiverNode,
                    //        0,
                    //        inputReceiverData);
                    //}
                }

                #endregion

                #region Process outputs

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

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

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

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

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

                            //Create Symbol Node
                            var node = newNodeWorkspace.AddNode <Output>();
                            node.Symbol = outputSenderNode.OutPortData[outputSenderData].NickName;

                            node.SetNickNameFromAttribute();
                            node.DisableReporting();

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

                            newNodeWorkspace.AddConnection(
                                outputSenderNode,
                                node,
                                outputSenderData,
                                0);

                            i++;
                        }
                    }

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

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

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

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

                        //if (curriedNode == null)
                        //{
                        // we create the connectors in the current space later
                        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)
                        //    newNodeWorkspace.AddConnection(
                        //        outputSenderNode,
                        //        curriedNode.InnerNode,
                        //        outputSenderData,
                        //        targetPortIndex + 1);

                        //}
                    }
                }
                else
                {
                    foreach (var hanging in
                             selectedNodeSet.SelectMany(
                                 node =>
                                 Enumerable.Range(0, node.OutPortData.Count)
                                 .Where(port => !node.HasOutput(port))
                                 .Select(port => new { node, port })).Distinct())
                    {
                        //Create Symbol Node
                        var node = newNodeWorkspace.AddNode <Output>();
                        node.Symbol = hanging.node.OutPortData[hanging.port].NickName;

                        node.SetNickNameFromAttribute();

                        //store the element in the elements list
                        node.DisableReporting();

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

                        newNodeWorkspace.AddConnection(hanging.node, node, hanging.port, 0);

                        i++;
                    }
                }

                #endregion

                // save and load the definition from file
                newNodeDefinition.SyncWithWorkspace(dynamoModel, true, true);
                dynamoModel.Workspaces.Add(newNodeWorkspace);

                string name          = newNodeDefinition.FunctionId.ToString();
                var    collapsedNode = currentWorkspace.AddNode(avgX, avgY, name);
                undoRecorder.RecordCreationForUndo(collapsedNode);

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

                collapsedNode.DisableReporting();

                foreach (
                    var nodeTuple in
                    inConnectors.Select(
                        (x, idx) => new { node = x.Item1, from = x.Item2, to = idx }))
                {
                    var conn = currentWorkspace.AddConnection(
                        nodeTuple.node,
                        collapsedNode,
                        nodeTuple.from,
                        nodeTuple.to);

                    if (conn != null)
                    {
                        undoRecorder.RecordCreationForUndo(conn);
                    }
                }

                foreach (var nodeTuple in outConnectors)
                {
                    var conn = currentWorkspace.AddConnection(
                        collapsedNode,
                        nodeTuple.Item1,
                        nodeTuple.Item2,
                        nodeTuple.Item3);

                    if (conn != null)
                    {
                        undoRecorder.RecordCreationForUndo(conn);
                    }
                }

                collapsedNode.EnableReporting();
                currentWorkspace.EnableReporting();

                foreach (var node in newNodeWorkspace.Nodes)
                {
                    node.EnableReporting();
                }

                newNodeWorkspace.WatchChanges = true;
            }
        }