Esempio n. 1
0
            public ModelModificationUndoHelper(UndoRedoRecorder recorder, IEnumerable <ModelBase> models)
            {
                this.recorder = recorder;

                this.models         = new List <ModelBase>(models);
                existingConnectors  = new Dictionary <Guid, XmlElement>();
                existingPins        = new Dictionary <Guid, XmlElement>();
                remainingConnectors = new Dictionary <Guid, ConnectorModel>();

                var allConnectors = new List <ConnectorModel>();

                using (this.recorder.BeginActionGroup())
                {
                    // Assuming no connectors will be modified as part of this, we
                    // record the node prior to it being modified. If for some
                    // reason connectors are dropped/created along the way, then
                    // this particular action group will be pop off the undo stack.
                    //
                    foreach (var model in this.models)
                    {
                        var nodeModel = model as NodeModel;
                        if (nodeModel != null)
                        {
                            allConnectors.AddRange(nodeModel.AllConnectors);
                        }

                        this.recorder.RecordModificationForUndo(model);
                    }
                }

                // Record the existing connectors...
                foreach (var connectorModel in allConnectors)
                {
                    var element = connectorModel.Serialize(
                        recorder.document, SaveContext.Undo);

                    existingConnectors[connectorModel.GUID] = element;

                    foreach (var connectorPin in connectorModel.ConnectorPinModels)
                    {
                        var pinElement = connectorPin.Serialize(
                            recorder.document, SaveContext.Undo);

                        existingPins[connectorPin.GUID] = pinElement;
                    }
                }
            }
Esempio n. 2
0
        protected WorkspaceModel(
            String name, IEnumerable<NodeModel> e, IEnumerable<ConnectorModel> c, double x, double y)
        {
            Name = name;

            Nodes = new TrulyObservableCollection<NodeModel>(e);
            Connectors = new TrulyObservableCollection<ConnectorModel>(c);
            Notes = new ObservableCollection<NoteModel>();
            X = x;
            Y = y;

            HasUnsavedChanges = false;
            LastSaved = DateTime.Now;

            WorkspaceSaved += OnWorkspaceSaved;
            WorkspaceVersion = AssemblyHelper.GetDynamoVersion();
            undoRecorder = new UndoRedoRecorder(this);
        }
Esempio n. 3
0
        private void CommitChanges(UndoRedoRecorder recorder)
        {
            // Code block editor can lose focus in many scenarios (e.g. switching 
            // of tabs or application), if there has not been any changes, do not
            // commit the change.
            // 
            if (!codeBlockNode.Code.Equals(InnerTextEditor.Text))
            {
                UpdateNodeValue("Code");
            }

            if (createdForNewCodeBlock)
            {
                // If this editing was started due to a new code block node, 
                // then by this point there would have been two action groups 
                // recorded on the undo-stack: one for node creation, and 
                // another for node editing (as part of ExecuteCommand above).
                // Pop off the two action groups...
                // 
                recorder.PopFromUndoGroup(); // Pop off modification action.

                // Note that due to various external factors a code block node 
                // loaded from file may be created empty. In such cases, the 
                // creation step would not have been recorded (there was no 
                // explicit creation of the node, it was created from loading 
                // of a file), and nothing should be popped off of the undo stack.
                if (recorder.CanUndo)
                    recorder.PopFromUndoGroup(); // Pop off creation action.

                // ... and record this new node as new creation.
                using (recorder.BeginActionGroup())
                {
                    recorder.RecordCreationForUndo(codeBlockNode);
                }
            }
        }
Esempio n. 4
0
        protected WorkspaceModel(
            IEnumerable<NodeModel> e, 
            IEnumerable<NoteModel> n,
            IEnumerable<AnnotationModel> a,
            WorkspaceInfo info, 
            NodeFactory factory,
            IEnumerable<PresetModel> presets)
        {
            guid = Guid.NewGuid();

            nodes = new ObservableCollection<NodeModel>(e);
            notes = new ObservableCollection<NoteModel>(n);

            annotations = new ObservableCollection<AnnotationModel>(a);         

            // Set workspace info from WorkspaceInfo object
            Name = info.Name;
            X = info.X;
            Y = info.Y;
            FileName = info.FileName;
            Zoom = info.Zoom;

            HasUnsavedChanges = false;
            LastSaved = DateTime.Now;

            WorkspaceVersion = AssemblyHelper.GetDynamoVersion();
            undoRecorder = new UndoRedoRecorder(this);

            NodeFactory = factory;

            this.presets = new List<PresetModel>(presets);
            // Update ElementResolver from nodeGraph.Nodes (where node is CBN)
            ElementResolver = new ElementResolver();
            foreach (var node in nodes)
            {
                RegisterNode(node);

                var cbn = node as CodeBlockNodeModel;
                if (cbn != null && cbn.ElementResolver != null)
                {
                    ElementResolver.CopyResolutionMap(cbn.ElementResolver);
                }
            }

            foreach (var connector in Connectors)
                RegisterConnector(connector);

            SetModelEventOnAnnotation();
        }
Esempio n. 5
0
 protected virtual bool HandleModelEventCore(string eventName, UndoRedoRecorder recorder)
 {
     return false; // Base class does not handle this.
 }
Esempio n. 6
0
        internal static void RecordModelsForUndo(Dictionary<ModelBase, UndoRedoRecorder.UserAction> models, UndoRedoRecorder recorder)
        {
            if (null == recorder)
                return;
            if (!ShouldProceedWithRecording(models))
                return;

            using (recorder.BeginActionGroup())
            {
                foreach (var modelPair in models)
                {
                    switch (modelPair.Value)
                    {
                        case UndoRedoRecorder.UserAction.Creation:
                            recorder.RecordCreationForUndo(modelPair.Key);
                            break;
                        case UndoRedoRecorder.UserAction.Deletion:
                            recorder.RecordDeletionForUndo(modelPair.Key);
                            break;
                        case UndoRedoRecorder.UserAction.Modification:
                            recorder.RecordModificationForUndo(modelPair.Key);
                            break;
                    }
                }
            }
        }
Esempio n. 7
0
 // See RecordModelsForModification below for more details.
 internal static void RecordModelForModification(ModelBase model, UndoRedoRecorder recorder)
 {
     if (null != model)
     {
         var models = new List<ModelBase> { model };
         RecordModelsForModification(models, recorder);
     }
 }
        private void RecordModels(UndoRedoRecorder recorder)
        {
            if (model.InPorts.Count == 0)
                return;

            var connectors = model.InPorts.Last().Connectors;
            if (connectors.Count != 0)
            {
                if (connectors.Count != 1)
                {
                    throw new InvalidOperationException(
                        "There should be only one connection to an input port");
                }
                var models = new Dictionary<ModelBase, UndoRedoRecorder.UserAction>
                {
                    { connectors[0], UndoRedoRecorder.UserAction.Deletion },
                    { model, UndoRedoRecorder.UserAction.Modification }
                };
                WorkspaceModel.RecordModelsForUndo(models, recorder);
            }
            else
                WorkspaceModel.RecordModelForModification(model, recorder);
        }
 internal DummyWorkspace()
 {
     undoRecorder = new UndoRedoRecorder(this);
 }
Esempio n. 10
0
            public ModelModificationUndoHelper(UndoRedoRecorder recorder, IEnumerable<ModelBase> models)
            {
                this.recorder = recorder;

                this.models = new List<ModelBase>(models);
                existingConnectors = new Dictionary<Guid, XmlElement>();
                remainingConnectors = new Dictionary<Guid, ConnectorModel>();

                var allConnectors = new List<ConnectorModel>();
                using (this.recorder.BeginActionGroup())
                {
                    // Assuming no connectors will be modified as part of this, we 
                    // record the node prior to it being modified. If for some 
                    // reason connectors are dropped/created along the way, then 
                    // this particular action group will be pop off the undo stack.
                    // 
                    foreach (var model in this.models)
                    {
                        var nodeModel = model as NodeModel;
                        if (nodeModel != null)
                            allConnectors.AddRange(nodeModel.AllConnectors);

                        this.recorder.RecordModificationForUndo(model);
                    }
                }

                // Record the existing connectors...
                foreach (var connectorModel in allConnectors)
                {
                    var element = connectorModel.Serialize(
                        recorder.document, SaveContext.Undo);

                    existingConnectors[connectorModel.GUID] = element;
                }
            }
Esempio n. 11
0
 public ModelModificationUndoHelper(UndoRedoRecorder recorder, ModelBase model)
     : this(recorder, new [] { model })
 {
 }
Esempio n. 12
0
 public ActionGroupDisposable(UndoRedoRecorder recorder)
 {
     this.recorder = recorder;
 }
Esempio n. 13
0
 /// <summary>
 /// This method is currently used as a way to send an event to ModelBase
 /// derived objects. Its primary use is in DynamoNodeButton class, which
 /// sends this event when clicked, to change the number of ports in a
 /// VariableInputNode.
 /// </summary>
 /// <param name="eventName">The name of the event.</param>
 /// <param name="value">For the SetInPortCount event, the number of
 /// ports desired.  Ignored for other events.</param>
 /// <param name="recorder"></param>
 /// <returns>Returns true if the call has been handled, or false otherwise.
 /// </returns>
 internal bool HandleModelEvent(string eventName, int value, UndoRedoRecorder recorder)
 {
     return HandleModelEventCore(eventName, value, recorder);
 }
Esempio n. 14
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);
        }
Esempio n. 15
0
        private void DiscardChangesAndOptionallyRemoveNode(UndoRedoRecorder recorder)
        {
            if (!string.IsNullOrEmpty(InnerTextEditor.Text))
            {
                throw new InvalidOperationException(
                    "This method is meant only for empty text box");
            }

            if (createdForNewCodeBlock)
            {
                // If this editing was started due to a new code block node, 
                // then by this point the creation of the node would have been 
                // recorded, we need to pop that off the undo stack. Note that 
                // due to various external factors a code block node loaded 
                // from file may be created empty. In such cases, the creation 
                // step would not have been recorded (there was no explicit 
                // creation of the node, it was created from loading of a file),
                // and nothing should be popped off of the undo stack.
                // 
                if (recorder.CanUndo)
                    recorder.PopFromUndoGroup(); // Pop off creation action.               
            }
            else
            {
                // If the editing was started for an existing code block node,
                // and user deletes the text contents, it should be restored to 
                // the original codes.
                InnerTextEditor.Text = nodeModel.Code;               
            }
        }
 public void SetupTests()
 {
     workspace = new DummyWorkspace();
     recorder = workspace.Recorder;
 }
 public void TestConstructor()
 {
     Assert.Throws<ArgumentNullException>(() =>
     {
         UndoRedoRecorder temp = new UndoRedoRecorder(null);
     });
 }
Esempio n. 18
0
 public ActionGroupDisposable(UndoRedoRecorder recorder)
 {
     this.recorder = recorder;
 }
 internal override bool HandleModelEventCore(string eventName, int value, UndoRedoRecorder recorder)
 {
     return VariableInputController.HandleModelEventCore(eventName, value, recorder)
         || base.HandleModelEventCore(eventName, value, recorder);
 }
Esempio n. 20
0
 public ModelModificationUndoHelper(UndoRedoRecorder recorder, ModelBase model)
     : this(recorder, new [] { model })
 {
 }
        internal bool HandleModelEventCore(string eventName, int value, UndoRedoRecorder recorder)
        {
            switch (eventName)
            {
                case "AddInPort":
                    AddInputToModel();
                    return true; // Handled here.
                case "RemoveInPort":
                    RemoveInputFromModel();
                    return true; // Handled here.
                case "SetInPortCount":
                    SetNumInputs(value);
                    return true; // Handled here.
            }

            return false; // base.HandleModelEventCore(eventName);
        }
Esempio n. 22
0
        internal bool HandleModelEventCore(string eventName, UndoRedoRecorder recorder)
        {
            if (eventName == "AddInPort")
            {
                AddInputToModel();
                model.RegisterAllPorts();
                return true; // Handled here.
            }

            if (eventName == "RemoveInPort")
            {
                RemoveInputFromModel();
                model.RegisterAllPorts();
                return true; // Handled here.
            }

            return false; // base.HandleModelEventCore(eventName);
        }
Esempio n. 23
0
        /// <summary>
        /// TODO(Ben): This method is exposed this way for external codes (e.g. 
        /// the DragCanvas) to record models before they are modified. This is 
        /// by no means ideal. The ideal case of course is for ALL codes that 
        /// end up modifying models to be folded back into WorkspaceViewModel in 
        /// the form of commands. These commands then internally record those
        /// affected models before updating them. We need this method to be gone
        /// sooner than later.
        /// </summary>
        /// <param name="models">The models to be recorded for undo.</param>
        /// <param name="recorder"></param>
        internal static void RecordModelsForModification(List<ModelBase> models, UndoRedoRecorder recorder)
        {
            if (null == recorder)
                return;
            if (!ShouldProceedWithRecording(models))
                return;

            using (recorder.BeginActionGroup())
            {
                foreach (var model in models)
                    recorder.RecordModificationForUndo(model);
            }
        }
Esempio n. 24
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);
        }
Esempio n. 25
0
        protected WorkspaceModel(
            IEnumerable<NodeModel> nodes, 
            IEnumerable<NoteModel> notes,
            IEnumerable<AnnotationModel> annotations,
            WorkspaceInfo info, 
            NodeFactory factory,
            IEnumerable<PresetModel> presets,
            ElementResolver resolver)
        {
            guid = Guid.NewGuid();

            this.nodes = new List<NodeModel>(nodes);
            this.notes = new List<NoteModel>(notes);

            this.annotations = new List<AnnotationModel>(annotations);         

            // Set workspace info from WorkspaceInfo object
            Name = info.Name;
            Description = info.Description;
            X = info.X;
            Y = info.Y;
            FileName = info.FileName;
            Zoom = info.Zoom;

            HasUnsavedChanges = false;
            LastSaved = DateTime.Now;

            WorkspaceVersion = AssemblyHelper.GetDynamoVersion();
            undoRecorder = new UndoRedoRecorder(this);

            NodeFactory = factory;

            this.presets = new List<PresetModel>(presets);
            ElementResolver = resolver;

            foreach (var node in this.nodes)
                RegisterNode(node);

            foreach (var connector in Connectors)
                RegisterConnector(connector);

            SetModelEventOnAnnotation();
        }
Esempio n. 26
0
 /// <summary>
 /// This method is currently used as a way to send an event to ModelBase 
 /// derived objects. Its primary use is in DynamoNodeButton class, which 
 /// sends this event when clicked.
 /// </summary>
 /// <param name="eventName">The name of the event.</param>
 /// <param name="recorder"></param>
 /// <returns>Returns true if the call has been handled, or false otherwise.
 /// </returns>
 public bool HandleModelEvent(string eventName, UndoRedoRecorder recorder)
 {
     return HandleModelEventCore(eventName, recorder);
 }