public void Add(IEnumerable <ConversationNode> nodes, IEnumerable <NodeGroup> groups, ILocalizationEngine localization) { nodes = nodes.Evaluate(); groups = groups.ToList(); SimpleUndoPair addActions = InnerAddNodes(nodes, groups, localization); bool addedNodes = nodes.Any(); bool addedGroups = groups.Any(); if (addedNodes && addedGroups) { UndoableFile.Change(new GenericUndoAction(addActions.Undo, addActions.Redo, "Added nodes and groups")); } else if (addedNodes) { UndoableFile.Change(new GenericUndoAction(addActions.Undo, addActions.Redo, "Added nodes")); } else if (addedGroups) { UndoableFile.Change(new GenericUndoAction(addActions.Undo, addActions.Redo, "Added groups")); } else { throw new InternalLogicException("why would you do this?"); } }
//TODO: MouseController and this should use the same logic (not sure which is better) public void Move(IEnumerable <ValueTuple <ConversationNode, PointF> > move) { //TODO: What happens if the nodes are in groups? Action undo = () => { }; Action redo = () => { }; foreach ((var node, var point) in move) { PointF origin = node.Renderer.Area.Center(); undo += () => node.Renderer.MoveTo(origin); redo += () => node.Renderer.MoveTo(point); } UndoableFile.Change(new GenericUndoAction(undo, redo, "Moved nodes")); }
public Tuple <IEnumerable <ConversationNode>, IEnumerable <NodeGroup> > InsertInto(IEnumerable <GraphAndUI <NodeUIData> > nodeData, IEnumerable <NodeGroup> groups, PointF location, ILocalizationEngine localization) { var nodes = nodeData.Select(gnu => MakeNode(gnu.GraphData, gnu.UIData)).Evaluate(); var area = NodeSet.GetArea(nodes.Concat <IRenderable <IGui> >(groups)); PointF offset = location.Take(area.Center()); foreach (var node in nodes) { node.Renderer.Offset(offset); } foreach (var group in groups) { group.Renderer.Offset(offset); } SimpleUndoPair addActions = InnerAddNodes(nodes, groups, localization); UndoableFile.Change(new GenericUndoAction(addActions, "Pasted")); return(Tuple.Create(nodes, groups)); }
public void RemoveLinks(Output o) { UndoableFile.Change(new GenericUndoAction(o.DisconnectAllActions(), "Removed links")); }
public bool Remove(IEnumerable <ConversationNode> nodes, IEnumerable <NodeGroup> groups, ILocalizationEngine localization) { nodes = nodes.ToList(); groups = groups.ToList(); bool removeNodes = nodes.Any(); bool removeGroups = groups.Any(); List <Action> undoActions = new List <Action>(); List <Action> redoActions = new List <Action>(); if (nodes.Any(n => !CanRemoveFromData(n, () => false))) { if (!PromptNodeDeletion()) { return(false); } } if (removeNodes) { //Make sure all the nodes are added before trying to link them foreach (var node in nodes) { var n = node; undoActions.Add(() => { m_nodes.Add(n); }); } foreach (var node in nodes) { var n = node; var actions = n.GetNodeRemoveActions(); //Ensure that the localization engine is up to date in terms of usage of localized data foreach (var parameter in n.Data.Parameters.OfType <ILocalizedStringParameter>()) { SimpleUndoPair clearLocalization = localization.ClearLocalizationAction(Id <LocalizedStringType> .ConvertFrom(parameter.TypeId), parameter.Value); undoActions.Add(clearLocalization.Undo); redoActions.Add(clearLocalization.Redo); } var containingGroups = m_groups.Where(g => g.Contents.Contains(n.Data.NodeId)).Evaluate(); undoActions.Add(() => { actions.Undo(); //Connect after adding the node foreach (var group in containingGroups) { group.Contents.Add(n.Data.NodeId); } m_audioProvider.UpdateUsage(n); }); redoActions.Add(() => { actions.Redo(); //Disconnect before removing the node m_nodes.Remove(n); foreach (var group in containingGroups) { group.Contents.Remove(n.Data.NodeId); } NodesDeleted.Execute(); }); } } if (removeGroups) { List <NodeGroup> toAdd = new List <NodeGroup>(groups); undoActions.Add(() => { m_groups.AddRange(toAdd); }); redoActions.Add(() => { m_groups.RemoveRange(toAdd); }); } Action undo = () => { using (m_audioProvider.SuppressUpdates()) foreach (Action action in undoActions) { action(); } }; Action redo = () => { using (m_audioProvider.SuppressUpdates()) foreach (Action action in redoActions) { action(); } }; string message; if (removeNodes && removeGroups) { message = "Deleted elements"; } else if (removeNodes) { message = "Deleted nodes"; } else if (removeGroups) { message = "Removed groupings"; } else { throw new InternalLogicException("Something went wrong :("); } UndoableFile.Change(new GenericUndoAction(undo, redo, message)); return(true); }
//TODO: Duplicate the IEditable with a new ID (must be deep copy of parameters) public Tuple <IEnumerable <ConversationNode>, IEnumerable <NodeGroup> > DuplicateInto(IEnumerable <GraphAndUI <NodeUIData> > nodeData, IEnumerable <NodeGroup> groups, PointF location, ILocalizationEngine localization) { var nodes = nodeData.Select(gnu => MakeNode(gnu.GraphData, gnu.UIData)).Evaluate(); IEnumerable <IDynamicEnumParameter> localDynamicEnumerationParameters = nodes.SelectMany(n => n.Data.Parameters.OfType <IDynamicEnumParameter>().Where(p => p.Local)); foreach (var ldep in localDynamicEnumerationParameters) { ldep.MergeInto(m_getDocumentSource(ldep)); } if (nodes.Any() || groups.Any()) { List <Action> undoActions = new List <Action>(); List <Action> redoActions = new List <Action>(); //Changes to these nodes don't need to be undoable as they're new nodes foreach (var node in nodes) { //Duplicate the id of any localized string parameters to avoid the new node using the same id(s) as the old one foreach (var p in node.Data.Parameters.OfType <LocalizedStringParameter>()) { var result = localization.DuplicateActions(p.Value); var action = p.SetValueAction(result.Item1); if (action != null) { //Don't this node is a duplicate of another. The old node's localization data is irrelevant. We should never return to it. //undoActions.Add(action.Value.Undo); redoActions.Add(action.Value.Redo); action.Value.Redo(); //Change the value immediately. The old value is irrelevant and we should never return to it. } result.Item2.Redo(); //Add the localization immediately. Otherwise when adding the node we won't know how to undo deletion of the node's localization data. undoActions.Add(result.Item2.Undo); redoActions.Add(result.Item2.Redo); } //TODO: AUDIO: Do we want to treat audio parameters like strings in that they have a meaningful value // or like localized strings in that they are a key into another system? //foreach (var p in node.Data.Parameters.OfType<IAudioParameter>()) //{ // //No need to update audio usage as this will occur when the node is added/removed // var audio = m_generateAudio(this); // var actions = p.SetValueAction(audio); //TODO: AUDIO: Investigate what happens to Audio parameter usage if you duplicate a node and then undo // undoActions.Add(actions.Value.Undo); // redoActions.Add(actions.Value.Redo); //} //foreach (var p in node.Data.Parameters.OfType<IAudioParameter>()) //{ // var action = p.SetValueAction(new Audio(Guid.NewGuid().ToString())); // if (action != null) // action.Value.Redo(); //If we undo the whole operation the parameter wont exist so no need to ever undo this value change. //} var oldID = node.Data.NodeId; node.Data.ChangeId(Id <NodeTemp> .New()); foreach (var group in groups) { if (group.Contents.Contains(oldID)) { group.Contents.Remove(oldID); group.Contents.Add(node.Data.NodeId); } } } var area = NodeSet.GetArea(nodes.Concat <IRenderable <IGui> >(groups)); PointF offset = location.Take(area.Center()); foreach (var node in nodes) { node.Renderer.Offset(offset); } foreach (var group in groups) { group.Renderer.Offset(offset); } SimpleUndoPair addActions = InnerAddNodes(nodes, groups, localization); Action undo = () => { undoActions.ForEach(a => a()); addActions.Undo(); }; Action redo = () => { redoActions.ForEach(a => a()); addActions.Redo(); }; UndoableFile.Change(new GenericUndoAction(undo, redo, "Pasted")); } return(Tuple.Create(nodes, groups)); }