private void PinToNode(object parameters) { var nodeToPin = DynamoSelection.Instance.Selection .OfType <NodeModel>() .FirstOrDefault(); if (nodeToPin == null) { return; } var nodeGroup = WorkspaceViewModel.Annotations .FirstOrDefault(x => x.AnnotationModel.ContainsModel(nodeToPin)); if (nodeGroup != null) { nodeGroup.AnnotationModel.AddToSelectedModels(this.Model); } Model.PinnedNode = nodeToPin; MoveNoteAbovePinnedNode(); SubscribeToPinnedNode(); WorkspaceModel.RecordModelsForModification(new List <ModelBase> { this.Model }, WorkspaceViewModel.Model.UndoRecorder); WorkspaceViewModel.HasUnsavedChanges = true; }
/// <summary> /// Implements the MouseDown event handler for the manipulator /// </summary> /// <param name="sender"></param> /// <param name="mouseButtonEventArgs"></param> protected virtual void MouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs) { UpdatePosition(); GizmoInAction = null; //Reset Drag. var gizmos = GetGizmos(false); if (!Active || !IsEnabled() || null == gizmos || !gizmos.Any()) { return; } var ray = BackgroundPreviewViewModel.GetClickRay(mouseButtonEventArgs); if (ray == null) { return; } foreach (var item in gizmos) { object hitObject; if (item.HitTest(ray.GetOriginPoint(), ray.GetDirectionVector(), out hitObject)) { GizmoInAction = item; var nodes = OnGizmoClick(item, hitObject); if (null != nodes && nodes.Any()) { WorkspaceModel.RecordModelsForModification(nodes); } newPosition = GizmoInAction.Origin; return; } } }
public void TestRecordModelsForModificationWithEmptyInput() { WorkspaceModel workspace = Controller.DynamoViewModel.CurrentSpace; Assert.AreEqual(false, workspace.CanUndo); // Calling the method with a null argument. workspace.RecordModelsForModification(null); Assert.AreEqual(false, workspace.CanUndo); // Calling the method with an empty list. List<ModelBase> models = new List<ModelBase>(); workspace.RecordModelsForModification(models); Assert.AreEqual(false, workspace.CanUndo); // Calling the method with a list full of null. models.Add(null); models.Add(null); workspace.RecordModelsForModification(models); Assert.AreEqual(false, workspace.CanUndo); }
/// <summary> /// Implements the MouseDown event handler for the manipulator /// </summary> /// <param name="sender"></param> /// <param name="mouseButtonEventArgs"></param> protected virtual void MouseDown(object sender, MouseButtonEventArgs mouseButtonEventArgs) { if (!IsValidNode) { return; } active = UpdatePosition(); if (Origin != null) { originBeforeMove = Point.ByCoordinates(Origin.X, Origin.Y, Origin.Z); originAfterMove = Point.ByCoordinates(Origin.X, Origin.Y, Origin.Z); } GizmoInAction = null; //Reset Drag. var gizmos = GetGizmos(false); if (!IsEnabled() || null == gizmos || !gizmos.Any()) { return; } var ray = BackgroundPreviewViewModel.GetClickRay(mouseButtonEventArgs); if (ray == null) { return; } foreach (var item in gizmos) { using (var originPt = ray.GetOriginPoint()) using (var dirVec = ray.GetDirectionVector()) { object hitObject; if (item.HitTest(originPt, dirVec, out hitObject)) { GizmoInAction = item; var nodes = OnGizmoClick(item, hitObject).ToList(); if (nodes.Any()) { WorkspaceModel.RecordModelsForModification(nodes); } return; } } } }
private void RecordSelectionForUndo() { // This is where we attempt to store all the models in undo recorder // before they are modified (i.e. being dragged around the canvas). // Note that we only do this once when the first mouse-move occurs // after a mouse-down, because mouse-down can potentially be used // just to select a node (as opposed to moving the selected nodes), in // which case we don't want any of the nodes to be recorded for undo. // List <ModelBase> models = DynamoSelection.Instance.Selection. Where((x) => (x is ModelBase)).Cast <ModelBase>().ToList <ModelBase>(); WorkspaceModel.RecordModelsForModification(models, Model.UndoRecorder); DynamoViewModel.RaiseCanExecuteUndoRedo(); }
private void DoGraphAutoLayout(object o) { if (Model.Nodes.Count == 0) { return; } var graph = new GraphLayout.Graph(); var models = new Dictionary <ModelBase, UndoRedoRecorder.UserAction>(); foreach (NodeModel x in Model.Nodes) { graph.AddNode(x.GUID, x.Width, x.Height, x.Y); models.Add(x, UndoRedoRecorder.UserAction.Modification); } foreach (ConnectorModel x in Model.Connectors) { graph.AddEdge(x.Start.Owner.GUID, x.End.Owner.GUID, x.Start.Center.Y, x.End.Center.Y); models.Add(x, UndoRedoRecorder.UserAction.Modification); } WorkspaceModel.RecordModelsForModification(new List <ModelBase>(Model.Nodes), Model.UndoRecorder); // Sugiyama algorithm steps graph.RemoveCycles(); graph.AssignLayers(); graph.OrderNodes(); // Assign coordinates to node models graph.NormalizeGraphPosition(); foreach (var x in Model.Nodes) { var id = x.GUID; x.X = graph.FindNode(id).X; x.Y = graph.FindNode(id).Y; x.ReportPosition(); } // Fit view to the new graph layout DynamoSelection.Instance.ClearSelection(); ResetFitViewToggle(null); FitViewInternal(); }
public void AlignSelected(object parameter) { string alignType = parameter.ToString(); if (DynamoSelection.Instance.Selection.Count <= 1) { return; } // All the models in the selection will be modified, // record their current states before anything gets changed. SmartCollection <ISelectable> selection = DynamoSelection.Instance.Selection; IEnumerable <ModelBase> models = selection.OfType <ModelBase>(); WorkspaceModel.RecordModelsForModification(models.ToList(), Model.UndoRecorder); var toAlign = DynamoSelection.Instance.Selection.OfType <ILocatable>().ToList(); switch (alignType) { case "HorizontalCenter": { var xAll = GetSelectionAverageX(); toAlign.ForEach((x) => { x.CenterX = xAll; }); } break; case "HorizontalLeft": { var xAll = GetSelectionMinX(); toAlign.ForEach((x) => { x.X = xAll; }); } break; case "HorizontalRight": { var xAll = GetSelectionMaxX(); toAlign.ForEach((x) => { x.X = xAll - x.Width; }); } break; case "VerticalCenter": { var yAll = GetSelectionAverageY(); toAlign.ForEach((x) => { x.CenterY = yAll; }); } break; case "VerticalTop": { var yAll = GetSelectionMinY(); toAlign.ForEach((x) => { x.Y = yAll; }); } break; case "VerticalBottom": { var yAll = GetSelectionMaxY(); toAlign.ForEach((x) => { x.Y = yAll - x.Height; }); } break; case "VerticalDistribute": { if (DynamoSelection.Instance.Selection.Count <= 2) { return; } var yMin = GetSelectionMinY(); var yMax = GetSelectionMaxY(); var spacing = 0.0; var span = yMax - yMin; var nodeHeightSum = DynamoSelection.Instance.Selection.Where(y => y is ILocatable) .Cast <ILocatable>() .Sum((y) => y.Height); if (span > nodeHeightSum) { spacing = (span - nodeHeightSum) / (DynamoSelection.Instance.Selection.Count - 1); } var cursor = yMin; foreach (var node in toAlign.OrderBy(y => y.Y)) { node.Y = cursor; cursor += node.Height + spacing; } } break; case "HorizontalDistribute": { if (DynamoSelection.Instance.Selection.Count <= 2) { return; } var xMin = GetSelectionMinX(); var xMax = GetSelectionMaxX(); var spacing = 0.0; var span = xMax - xMin; var nodeWidthSum = DynamoSelection.Instance.Selection.Where((x) => x is ILocatable) .Cast <ILocatable>() .Sum((x) => x.Width); // If there is more span than total node width, // distribute the nodes with a gap. If not, leave // the spacing at 0 and the nodes will distribute // up against each other. if (span > nodeWidthSum) { spacing = (span - nodeWidthSum) / (DynamoSelection.Instance.Selection.Count - 1); } var cursor = xMin; foreach (var node in toAlign.OrderBy(x => x.X)) { node.X = cursor; cursor += node.Width + spacing; } } break; } toAlign.ForEach(x => x.ReportPosition()); }
private void DoGraphAutoLayout(object o) { if (Model.Nodes.Count() == 0) { return; } var graph = new GraphLayout.Graph(); var models = new Dictionary <ModelBase, UndoRedoRecorder.UserAction>(); foreach (AnnotationModel x in Model.Annotations) { // Treat a group as a graph layout node/vertex graph.AddNode(x.GUID, x.Width, x.Height, x.Y); models.Add(x, UndoRedoRecorder.UserAction.Modification); } foreach (NodeModel x in Model.Nodes) { AnnotationModel group = Model.Annotations.Where( s => s.SelectedModels.Contains(x)).ToList().FirstOrDefault(); // Do not process nodes within groups if (group == null) { graph.AddNode(x.GUID, x.Width, x.Height, x.Y); models.Add(x, UndoRedoRecorder.UserAction.Modification); } } foreach (ConnectorModel x in Model.Connectors) { AnnotationModel startGroup = null, endGroup = null; startGroup = Model.Annotations.Where( s => s.SelectedModels.Contains(x.Start.Owner)).ToList().FirstOrDefault(); endGroup = Model.Annotations.Where( s => s.SelectedModels.Contains(x.End.Owner)).ToList().FirstOrDefault(); // Connector does not belong to any group if ((startGroup == null) && (endGroup == null)) { graph.AddEdge(x.Start.Owner.GUID, x.End.Owner.GUID, x.Start.Center.Y, x.End.Center.Y); } // Connector starts from a node within a group else if ((startGroup != null) && (endGroup == null)) { graph.AddEdge(startGroup.GUID, x.End.Owner.GUID, x.Start.Center.Y, x.End.Center.Y); } // Connector ends at a node within a group else if ((startGroup == null) && (endGroup != null)) { graph.AddEdge(x.Start.Owner.GUID, endGroup.GUID, x.Start.Center.Y, x.End.Center.Y); } models.Add(x, UndoRedoRecorder.UserAction.Modification); } // Support undo for graph layout command WorkspaceModel.RecordModelsForModification(new List <ModelBase>(Model.Nodes), Model.UndoRecorder); // Sugiyama algorithm steps graph.RemoveCycles(); graph.AssignLayers(); graph.OrderNodes(); graph.NormalizeGraphPosition(); // Assign coordinates to nodes inside groups foreach (var x in Model.Annotations) { var id = x.GUID; double deltaX = graph.FindNode(id).X - x.X; double deltaY = graph.FindNode(id).Y - x.Y; foreach (var n in x.SelectedModels) { n.X += deltaX; n.Y += deltaY; n.ReportPosition(); } } // Assign coordinates to nodes outside groups foreach (var x in Model.Nodes) { var n = graph.FindNode(x.GUID); if (n != null) { x.X = n.X; x.Y = n.Y; x.ReportPosition(); } } // Fit view to the new graph layout DynamoSelection.Instance.ClearSelection(); ResetFitViewToggle(null); FitViewInternal(); }
public void AlignSelected(object parameter) { string alignType = parameter.ToString(); if (DynamoSelection.Instance.Selection.Count <= 1) { return; } // All the models in the selection will be modified, // record their current states before anything gets changed. SmartCollection <ISelectable> selection = DynamoSelection.Instance.Selection; IEnumerable <ModelBase> models = selection.OfType <ModelBase>(); _model.RecordModelsForModification(models.ToList()); var toAlign = DynamoSelection.Instance.Selection.Where((x) => x is ILocatable) .Cast <ILocatable>() .ToList(); if (alignType == "HorizontalCenter") // make vertial line of elements { var xAll = GetSelectionAverageX(); toAlign.ForEach((x) => { x.CenterX = xAll; }); } else if (alignType == "HorizontalLeft") { var xAll = GetSelectionMinX(); toAlign.ForEach((x) => { x.X = xAll; }); } else if (alignType == "HorizontalRight") { var xAll = GetSelectionMaxX(); toAlign.ForEach((x) => { x.X = xAll - x.Width; }); } else if (alignType == "VerticalCenter") { var yAll = GetSelectionAverageY(); toAlign.ForEach((x) => { x.CenterY = yAll; }); } else if (alignType == "VerticalTop") { var yAll = GetSelectionMinY(); toAlign.ForEach((x) => { x.Y = yAll; }); } else if (alignType == "VerticalBottom") { var yAll = GetSelectionMaxY(); toAlign.ForEach((x) => { x.Y = yAll - x.Height; }); } else if (alignType == "VerticalDistribute") { if (DynamoSelection.Instance.Selection.Count <= 2) { return; } var yMin = GetSelectionMinY(); var yMax = GetSelectionMaxTopY(); var spacing = (yMax - yMin) / (DynamoSelection.Instance.Selection.Count - 1); int count = 0; toAlign.OrderBy((x) => x.Y) .ToList() .ForEach((x) => x.Y = yMin + spacing * count++); } else if (alignType == "HorizontalDistribute") { if (DynamoSelection.Instance.Selection.Count <= 2) { return; } var xMin = GetSelectionMinX(); var xMax = GetSelectionMaxLeftX(); var spacing = (xMax - xMin) / (DynamoSelection.Instance.Selection.Count - 1); int count = 0; toAlign.OrderBy((x) => x.X) .ToList() .ForEach((x) => x.X = xMin + spacing * count++); } toAlign.ForEach(x => x.ReportPosition()); }