public void CanSavePinState() { var model = CurrentDynamoModel; var cbn = new CodeBlockNodeModel(model.LibraryServices); var command = new DynamoModel.CreateNodeCommand(cbn, 0, 0, true, false); CurrentDynamoModel.ExecuteCommand(command); UpdateCodeBlockNodeContent(cbn, "42"); cbn.PreviewPinned = true; DynamoSelection.Instance.Selection.Add(cbn); var ids = DynamoSelection.Instance.Selection.OfType<NodeModel>().Select(x => x.GUID).ToList(); model.ExecuteCommand(new DynamoModel.AddPresetCommand("state1", "3", ids)); UpdateCodeBlockNodeContent(cbn, "146"); DynamoSelection.Instance.Selection.Remove(cbn); model.CurrentWorkspace.ApplyPreset(model.CurrentWorkspace.Presets.Where( x => x.Name == "state1").First()); RunCurrentModel(); Assert.IsTrue(cbn.PreviewPinned); }
/// <summary> /// Create code block editor by the view of code block node. /// </summary> /// <param name="nodeView"></param> public CodeBlockEditor(NodeView nodeView): base(nodeView) { this.codeBlockNode = nodeViewModel.NodeModel as CodeBlockNodeModel; if (codeBlockNode == null) { throw new InvalidOperationException( "Should not be used for nodes other than code block"); } // Determines if this editor is created for a new code block node. // In cases like an undo/redo operation, the editor is created for // an existing code block node. createdForNewCodeBlock = string.IsNullOrEmpty(codeBlockNode.Code); // the code block should not be in focus upon undo/redo actions on node if (codeBlockNode.ShouldFocus) { Loaded += (obj, args) => SetFocus(); } WatermarkLabel.Text = Properties.Resources.WatermarkLabelText; }
/// <summary> /// Paste ISelectable objects from the clipboard to the workspace at specified point. /// </summary> /// <param name="targetPoint">Location where data will be pasted</param> /// <param name="useOffset">Indicates whether we will use current workspace offset or paste nodes /// directly in this point. </param> public void Paste(Point2D targetPoint, bool useOffset = true) { if (useOffset) { // Provide a small offset when pasting so duplicate pastes aren't directly on top of each other CurrentWorkspace.IncrementPasteOffset(); } //clear the selection so we can put the //paste contents in DynamoSelection.Instance.ClearSelection(); //make a lookup table to store the guids of the //old models and the guids of their pasted versions var modelLookup = new Dictionary<Guid, ModelBase>(); //make a list of all newly created models so that their //creations can be recorded in the undo recorder. var createdModels = new List<ModelBase>(); var nodes = ClipBoard.OfType<NodeModel>(); var connectors = ClipBoard.OfType<ConnectorModel>(); var notes = ClipBoard.OfType<NoteModel>(); var annotations = ClipBoard.OfType<AnnotationModel>(); // Create the new NoteModel's var newNoteModels = new List<NoteModel>(); foreach (var note in notes) { var noteModel = new NoteModel(note.X, note.Y, note.Text, Guid.NewGuid()); //Store the old note as Key and newnote as value. modelLookup.Add(note.GUID,noteModel); newNoteModels.Add(noteModel); } var xmlDoc = new XmlDocument(); // Create the new NodeModel's var newNodeModels = new List<NodeModel>(); foreach (var node in nodes) { NodeModel newNode; if (CurrentWorkspace is HomeWorkspaceModel && (node is Symbol || node is Output)) { var symbol = (node is Symbol ? (node as Symbol).InputSymbol : (node as Output).Symbol); var code = (string.IsNullOrEmpty(symbol) ? "x" : symbol) + ";"; newNode = new CodeBlockNodeModel(code, node.X, node.Y, LibraryServices, CurrentWorkspace.ElementResolver); } else { var dynEl = node.Serialize(xmlDoc, SaveContext.Copy); newNode = NodeFactory.CreateNodeFromXml(dynEl, SaveContext.Copy, CurrentWorkspace.ElementResolver); } var lacing = node.ArgumentLacing.ToString(); newNode.UpdateValue(new UpdateValueParams("ArgumentLacing", lacing)); if (!string.IsNullOrEmpty(node.NickName) && !(node is Symbol) && !(node is Output)) newNode.NickName = node.NickName; newNode.Width = node.Width; newNode.Height = node.Height; modelLookup.Add(node.GUID, newNode); newNodeModels.Add(newNode); } var newItems = newNodeModels.Concat<ModelBase>(newNoteModels); var shiftX = targetPoint.X - newItems.Min(item => item.X); var shiftY = targetPoint.Y - newItems.Min(item => item.Y); var offset = useOffset ? CurrentWorkspace.CurrentPasteOffset : 0; foreach (var model in newItems) { model.X = model.X + shiftX + offset; model.Y = model.Y + shiftY + offset; } // Add the new NodeModel's to the Workspace foreach (var newNode in newNodeModels) { CurrentWorkspace.AddAndRegisterNode(newNode, false); createdModels.Add(newNode); } // TODO: is this required? OnRequestLayoutUpdate(this, EventArgs.Empty); // Add the new NoteModel's to the Workspace foreach (var newNote in newNoteModels) { CurrentWorkspace.AddNote(newNote, false); createdModels.Add(newNote); } ModelBase start; ModelBase end; var newConnectors = from c in connectors // If the guid is in nodeLookup, then we connect to the new pasted node. Otherwise we // re-connect to the original. let startNode = modelLookup.TryGetValue(c.Start.Owner.GUID, out start) ? start as NodeModel : CurrentWorkspace.Nodes.FirstOrDefault(x => x.GUID == c.Start.Owner.GUID) let endNode = modelLookup.TryGetValue(c.End.Owner.GUID, out end) ? end as NodeModel : CurrentWorkspace.Nodes.FirstOrDefault(x => x.GUID == c.End.Owner.GUID) // Don't make a connector if either end is null. where startNode != null && endNode != null select ConnectorModel.Make(startNode, endNode, c.Start.Index, c.End.Index); createdModels.AddRange(newConnectors); //Grouping depends on the selected node models. //so adding the group after nodes / notes are added to workspace. //select only those nodes that are part of a group. var newAnnotations = new List<AnnotationModel>(); foreach (var annotation in annotations) { var annotationNodeModel = new List<NodeModel>(); var annotationNoteModel = new List<NoteModel>(); // some models can be deleted after copying them, // so they need to be in pasted annotation as well var modelsToRestore = annotation.DeletedModelBases.Intersect(ClipBoard); var modelsToAdd = annotation.SelectedModels.Concat(modelsToRestore); // checked condition here that supports pasting of multiple groups foreach (var models in modelsToAdd) { ModelBase mbase; modelLookup.TryGetValue(models.GUID, out mbase); if (mbase is NodeModel) { annotationNodeModel.Add(mbase as NodeModel); } if (mbase is NoteModel) { annotationNoteModel.Add(mbase as NoteModel); } } var annotationModel = new AnnotationModel(annotationNodeModel, annotationNoteModel) { GUID = Guid.NewGuid(), AnnotationText = annotation.AnnotationText, Background = annotation.Background, FontSize = annotation.FontSize }; newAnnotations.Add(annotationModel); } // Add the new Annotation's to the Workspace foreach (var newAnnotation in newAnnotations) { CurrentWorkspace.AddAnnotation(newAnnotation); createdModels.Add(newAnnotation); AddToSelection(newAnnotation); } // adding an annotation overrides selection, so add nodes and notes after foreach (var item in newItems) { AddToSelection(item); } // Record models that are created as part of the command. CurrentWorkspace.RecordCreatedModels(createdModels); }
/// <summary> /// Forms new connections from the external nodes to the Node To Code Node, /// based on the connectors passed as inputs. /// </summary> /// <param name="externalInputConnections">List of connectors to remake, along with the port names of the new port</param> /// <param name="cbn">The new Node To Code created Code Block Node</param> private List<ConnectorModel> ReConnectInputConnections( Dictionary<ConnectorModel, string> externalInputConnections, CodeBlockNodeModel cbn) { List<ConnectorModel> newConnectors = new List<ConnectorModel>(); foreach (var kvp in externalInputConnections) { var connector = kvp.Key; var variableName = kvp.Value; var endPortIndex = CodeBlockNodeModel.GetInportIndex(cbn, variableName); if (endPortIndex < 0) continue; if (Connectors.Any(c => c.End == cbn.InPorts[endPortIndex])) continue; var newConnector = ConnectorModel.Make( connector.Start.Owner, cbn, connector.Start.Index, endPortIndex); newConnectors.Add(newConnector); } return newConnectors; }
/// <summary> /// Forms new connections from the external nodes to the Node To Code Node, /// based on the connectors passed as inputs. /// </summary> /// <param name="externalOutputConnections">List of connectors to remake, along with the port names of the new port</param> /// <param name="cbn">The new Node To Code created Code Block Node</param> private List<ConnectorModel> ReConnectOutputConnections(Dictionary<ConnectorModel, string> externalOutputConnections, CodeBlockNodeModel cbn) { List<ConnectorModel> newConnectors = new List<ConnectorModel>(); foreach (var kvp in externalOutputConnections) { var connector = kvp.Key; var variableName = kvp.Value; //Get the start and end idex for the ports for the connection var portModel = cbn.OutPorts.FirstOrDefault( port => cbn.GetRawAstIdentifierForOutputIndex(port.Index).Value.Equals(variableName)); if (portModel == null) continue; //Make the new connection and then record and add it var newConnector = ConnectorModel.Make( cbn, connector.End.Owner, portModel.Index, connector.End.Index); newConnectors.Add(newConnector); } return newConnectors; }
internal void ConvertNodesToCodeInternal(EngineController engineController, INamingProvider namingProvider) { var selectedNodes = DynamoSelection.Instance .Selection .OfType<NodeModel>() .Where(n => n.IsConvertible); if (!selectedNodes.Any()) return; var cliques = NodeToCodeCompiler.GetCliques(selectedNodes).Where(c => !(c.Count == 1 && c.First() is CodeBlockNodeModel)); var codeBlockNodes = new List<CodeBlockNodeModel>(); //UndoRedo Action Group---------------------------------------------- NodeToCodeUndoHelper undoHelper = new NodeToCodeUndoHelper(); // using (UndoRecorder.BeginActionGroup()) { foreach (var nodeList in cliques) { //Create two dictionarys to store the details of the external connections that have to //be recreated after the conversion var externalInputConnections = new Dictionary<ConnectorModel, string>(); var externalOutputConnections = new Dictionary<ConnectorModel, string>(); //Also collect the average X and Y co-ordinates of the different nodes int nodeCount = nodeList.Count; var nodeToCodeResult = engineController.ConvertNodesToCode(this.nodes, nodeList, namingProvider); #region Step I. Delete all nodes and their connections double totalX = 0, totalY = 0; foreach (var node in nodeList) { #region Step I.A. Delete the connections for the node foreach (var connector in node.AllConnectors.ToList()) { if (!IsInternalNodeToCodeConnection(nodeList, connector)) { //If the connector is an external connector, the save its details //for recreation later var startNode = connector.Start.Owner; int index = startNode.OutPorts.IndexOf(connector.Start); //We use the varibleName as the connection between the port of the old Node //to the port of the new node. var variableName = startNode.GetAstIdentifierForOutputIndex(index).Value; //Store the data in the corresponding dictionary if (startNode == node) { if (nodeToCodeResult.OutputMap.ContainsKey(variableName)) variableName = nodeToCodeResult.OutputMap[variableName]; externalOutputConnections.Add(connector, variableName); } else { if (nodeToCodeResult.InputMap.ContainsKey(variableName)) variableName = nodeToCodeResult.InputMap[variableName]; externalInputConnections.Add(connector, variableName); } } //Delete the connector undoHelper.RecordDeletion(connector); connector.Delete(); } #endregion #region Step I.B. Delete the node totalX += node.X; totalY += node.Y; undoHelper.RecordDeletion(node); RemoveNode(node); #endregion } #endregion #region Step II. Create the new code block node var outputVariables = externalOutputConnections.Values; var newResult = NodeToCodeCompiler.ConstantPropagationForTemp(nodeToCodeResult, outputVariables); // Rewrite the AST using the shortest unique name in case of namespace conflicts NodeToCodeCompiler.ReplaceWithShortestQualifiedName( engineController.LibraryServices.LibraryManagementCore.ClassTable, newResult.AstNodes, ElementResolver); var codegen = new ProtoCore.CodeGenDS(newResult.AstNodes); var code = codegen.GenerateCode(); var codeBlockNode = new CodeBlockNodeModel( code, System.Guid.NewGuid(), totalX / nodeCount, totalY / nodeCount, engineController.LibraryServices, ElementResolver); undoHelper.RecordCreation(codeBlockNode); AddAndRegisterNode(codeBlockNode, false); codeBlockNodes.Add(codeBlockNode); #endregion #region Step III. Recreate the necessary connections var newInputConnectors = ReConnectInputConnections(externalInputConnections, codeBlockNode); foreach (var connector in newInputConnectors) { undoHelper.RecordCreation(connector); } var newOutputConnectors = ReConnectOutputConnections(externalOutputConnections, codeBlockNode); foreach (var connector in newOutputConnectors) { undoHelper.RecordCreation(connector); } #endregion } } undoHelper.ApplyActions(UndoRecorder); DynamoSelection.Instance.ClearSelection(); DynamoSelection.Instance.Selection.AddRange(codeBlockNodes); RequestRun(); }
/// <summary> /// Returns the index of the port corresponding to the variable name given /// </summary> /// <param name="variableName"> Name of the variable corresponding to an input port </param> /// <returns> Index of the required port in the InPorts collection </returns> public static int GetInportIndex(CodeBlockNodeModel cbn, string variableName) { return cbn.inputIdentifiers.IndexOf(variableName); }
private CodeBlockNodeModel CreateCodeBlockNode() { var cbn = new CodeBlockNodeModel(CurrentDynamoModel.LibraryServices); var command = new DynCmd.CreateNodeCommand(cbn, 0, 0, true, false); CurrentDynamoModel.ExecuteCommand(command); Assert.IsNotNull(cbn); return cbn; }
private void UpdateCodeBlockNodeContent(CodeBlockNodeModel cbn, string value) { var command = new DynCmd.UpdateModelValueCommand(Guid.Empty, cbn.GUID, "Code", value); CurrentDynamoModel.ExecuteCommand(command); }
private void CreateCodeBlockNode(Point cursor) { // create node var node = new CodeBlockNodeModel(owningWorkspace.DynamoViewModel.Model.LibraryServices); owningWorkspace.DynamoViewModel.ExecuteCommand(new DynCmd.CreateNodeCommand(node, cursor.X, cursor.Y, false, true)); //correct node position node.X = (int)mouseDownPos.X - 92; node.Y = (int)mouseDownPos.Y - 31; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { NodeModel node = null; var obj = JObject.Load(reader); var type = Type.GetType(obj["$type"].Value<string>()); var guid = Guid.Parse(obj["Uuid"].Value<string>()); var displayName = obj["DisplayName"].Value<string>(); //var x = obj["X"].Value<double>(); //var y = obj["Y"].Value<double>(); var inPorts = obj["InputPorts"].ToArray().Select(t => t.ToObject<PortModel>()).ToArray(); var outPorts = obj["OutputPorts"].ToArray().Select(t => t.ToObject<PortModel>()).ToArray(); var resolver = (IdReferenceResolver)serializer.ReferenceResolver; if (type == typeof(Function)) { var functionId = Guid.Parse(obj["FunctionUuid"].Value<string>()); node = manager.CreateCustomNodeInstance(functionId); RemapPorts(node, inPorts, outPorts, resolver); } else if(type == typeof(CodeBlockNodeModel)) { var code = obj["Code"].Value<string>(); node = new CodeBlockNodeModel(code, guid, 0.0, 0.0, libraryServices, ElementResolver); RemapPorts(node, inPorts, outPorts, resolver); } else if(typeof(DSFunctionBase).IsAssignableFrom(type)) { var mangledName = obj["FunctionName"].Value<string>(); var description = libraryServices.GetFunctionDescriptor(mangledName); if(type == typeof(DSVarArgFunction)) { node = new DSVarArgFunction(description); // The node syncs with the function definition. // Then we need to make the inport count correct var varg = (DSVarArgFunction)node; varg.VarInputController.SetNumInputs(inPorts.Count()); } else if(type == typeof(DSFunction)) { node = new DSFunction(description); } RemapPorts(node, inPorts, outPorts, resolver); } else if (type == typeof(DSVarArgFunction)) { var functionId = Guid.Parse(obj["FunctionUuid"].Value<string>()); node = manager.CreateCustomNodeInstance(functionId); RemapPorts(node, inPorts, outPorts, resolver); } else { node = (NodeModel)obj.ToObject(type); } node.GUID = guid; node.NickName = displayName; //node.X = x; //node.Y = y; // Add references to the node and the ports to the reference resolver, // so that they are available for entities which are deserialized later. serializer.ReferenceResolver.AddReference(serializer.Context, node.GUID.ToString(), node); foreach(var p in node.InPorts) { serializer.ReferenceResolver.AddReference(serializer.Context, p.GUID.ToString(), p); } foreach (var p in node.OutPorts) { serializer.ReferenceResolver.AddReference(serializer.Context, p.GUID.ToString(), p); } return node; }
public void PreviewBubble_CodeBlockIsNotInFocus() { var code = new CodeBlockNodeModel(this.Model.LibraryServices); Model.AddNodeToCurrentWorkspace(code, true); Run(); // Click on DragCanvas. View.ChildOfType<DragCanvas>().RaiseEvent( new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = Mouse.MouseDownEvent }); var nodeView = NodeViewWithGuid(code.GUID.ToString()); Assert.IsNotNull(nodeView); // Move mouse on code node. var dispatcherOperation = View.Dispatcher.BeginInvoke(new Action( () => { nodeView.RaiseEvent( new MouseEventArgs(Mouse.PrimaryDevice, 0) { RoutedEvent = Mouse.MouseEnterEvent }); })); DispatcherUtil.DoEvents(); dispatcherOperation.Completed += (s, args) => Assert.IsTrue(nodeView.PreviewControl.IsCondensed); // Move mouse on code node preview. dispatcherOperation = View.Dispatcher.BeginInvoke(new Action( () => { nodeView.PreviewControl.RaiseEvent( new MouseEventArgs(Mouse.PrimaryDevice, 0) { RoutedEvent = Mouse.MouseEnterEvent }); })); DispatcherUtil.DoEvents(); dispatcherOperation.Completed += (s, args) => Assert.IsTrue(nodeView.PreviewControl.IsExpanded); }
public void PreviewBubble_NoCrashWithCodeBlock() { var code = new CodeBlockNodeModel(this.Model.LibraryServices); this.Model.AddNodeToCurrentWorkspace(code, true); this.Run(); var nodeView = NodeViewWithGuid(code.GUID.ToString()); Assert.IsNotNull(nodeView); View.Dispatcher.Invoke(() => { nodeView.RaiseEvent(new MouseEventArgs(Mouse.PrimaryDevice, 0) { RoutedEvent = Mouse.MouseEnterEvent }); nodeView.PreviewControl.RaiseEvent(new MouseEventArgs(Mouse.PrimaryDevice, 0) { RoutedEvent = Mouse.MouseEnterEvent }); }); DispatcherUtil.DoEvents(); Assert.IsTrue(nodeView.PreviewControl.IsHidden); }