/// <summary> /// Moves the given nodes into a container</summary> /// <param name="newParent">New container</param> /// <param name="movingObjects">Nodes to move</param> void IEditableGraphContainer <Element, Wire, ICircuitPin> .Move(object newParent, IEnumerable <object> movingObjects) { if (newParent == null) { newParent = this; } var movingItems = movingObjects.ToArray(); var moduleSet = new HashSet <Element>(); var movingNodes = movingItems.AsIEnumerable <Element>().ToArray(); var newContainer = newParent.Cast <ICircuitContainer>(); var oldContainer = movingNodes.First().DomNode.Parent.Cast <ICircuitContainer>(); Debug.Assert(oldContainer != newContainer); // all relevant (internal to the old container) edges before the moving var internalConnections = new List <Wire>(); var incomingConnections = new List <Wire>(); var outgoingConnections = new List <Wire>(); CircuitUtil.GetSubGraph(oldContainer, movingItems, moduleSet, internalConnections, incomingConnections, outgoingConnections); var graphValidator = DomNode.GetRoot().Cast <CircuitValidator>(); graphValidator.Suspended = true; // transfer modules foreach (var module in movingNodes) { oldContainer.Elements.Remove(module); newContainer.Elements.Add(module); } // transfer internal connections (those between grouped modules) foreach (Wire connection in internalConnections) { oldContainer.Wires.Remove(connection); newContainer.Wires.Add(connection); } // locaton transformation of moved modules var offset = GetRelativeOffset(oldContainer, newContainer); foreach (var module in movingNodes) { var relLoc = module.Bounds.Location; relLoc.Offset(offset); module.Bounds = new Rectangle(relLoc, module.Bounds.Size); } graphValidator.Suspended = false; graphValidator.MovingCrossContainer = true; }
/// <summary> /// Populates a newly created group with circuit elements that are currently in the given graph</summary> /// <param name="newGroup">A new group, empty of circuit elements</param> /// <param name="elementsToGroup">The circuit elements to move into 'newGroup'.</param> /// <param name="graphContainer">The container for the circuit elements and their wires. These will /// be removed from 'graphContainer' and placed into 'newGroup'.</param> /// <remarks>This method is intended to help with persistence of circuit groups.</remarks> public static void CreateGroup(Group newGroup, IEnumerable <object> elementsToGroup, ICircuitContainer graphContainer) { // get the selected modules and the connections between them HashSet <Element> modules = new HashSet <Element>(); List <Wire> internalConnections = new List <Wire>(); List <Wire> externalConnections = new List <Wire>(); CircuitUtil.GetSubGraph(graphContainer, elementsToGroup, modules, internalConnections, externalConnections, externalConnections); // the group must be added before transferring modules and connections to it, // so that the history mechanism will capture all the changes. graphContainer.Elements.Add(newGroup); // transfer modules foreach (Element module in modules) { graphContainer.Elements.Remove(module); newGroup.Elements.Add(module); } // auto-generate sub-graph group pins to support the external connections to group // group pins may have multiple external connections newGroup.UpdateGroupPins(modules, internalConnections, externalConnections); // transfer internal connections (those between grouped modules) foreach (Wire connection in internalConnections) { graphContainer.Wires.Remove(connection); newGroup.Wires.Add(connection); } // initalize group pin's index and pinY newGroup.InitializeGroupPinIndexes(internalConnections); if (graphContainer.Is <Group>()) // making a group inside a group { // remap group pins in the parent group var parentGroup = graphContainer.Cast <Group>(); // remap parent group pins that reference the new group's subnodes to the new group foreach (var grpPin in parentGroup.InputGroupPins) { if (modules.Contains(grpPin.InternalElement)) { // adjust the internal pin index first for (int j = 0; j < newGroup.Inputs.Count; ++j) { var newGrpPin = newGroup.Inputs[j] as GroupPin; if (newGrpPin.InternalElement.DomNode == grpPin.InternalElement.DomNode && newGrpPin.InternalPinIndex == grpPin.InternalPinIndex) { grpPin.InternalPinIndex = j; newGrpPin.Name = grpPin.Name; //grpPin.Name = newGroup.Name + ":" + newGrpPin.Name; break; } } // now update node references for the parent group pin grpPin.InternalElement = newGroup; } } foreach (var grpPin in parentGroup.OutputGroupPins) { if (modules.Contains(grpPin.InternalElement)) { // adjust the internal pin index first for (int j = 0; j < newGroup.Outputs.Count; ++j) { var newGrpPin = newGroup.Outputs[j] as GroupPin; if (newGrpPin.InternalElement.DomNode == grpPin.InternalElement.DomNode && newGrpPin.InternalPinIndex == grpPin.InternalPinIndex) { grpPin.InternalPinIndex = j; newGrpPin.Name = grpPin.Name; //grpPin.Name = newGroup.Name + ":" + newGrpPin.Name; break; } } // now update node references for the parent group pin grpPin.InternalElement = newGroup; } } } newGroup.OnChanged(EventArgs.Empty); // notify the change( derived class of Group may need custom actions) // Remap external connections from grouped modules to group. foreach (Wire connection in externalConnections) { var groupInputPin = newGroup.MatchedGroupPin(connection.InputElement, connection.InputPin.Index, true); if (groupInputPin != null) { groupInputPin.SetPinTarget(true); // reroute original edge connection.SetInput(newGroup, groupInputPin); connection.InputPinTarget = groupInputPin.PinTarget; } var groupOutputPin = newGroup.MatchedGroupPin(connection.OutputElement, connection.OutputPin.Index, false); if (groupOutputPin != null) { groupOutputPin.SetPinTarget(false); // reroute original edge connection.SetOutput(newGroup, groupOutputPin); connection.OutputPinTarget = groupOutputPin.PinTarget; } } // find upper-left corner of the subnodes Point minLocation = new Point(int.MaxValue, int.MaxValue); foreach (var module in newGroup.Elements) { if (minLocation.X > module.Bounds.Location.X) { minLocation.X = module.Bounds.Location.X; } if (minLocation.Y > module.Bounds.Location.Y) { minLocation.Y = module.Bounds.Location.Y; } } // offset sub-nodes location so they are relative to the parent foreach (var module in newGroup.Elements) { var relLoc = module.Bounds.Location; relLoc.Offset(-minLocation.X, -minLocation.Y); module.Bounds = new Rectangle(relLoc, module.Bounds.Size); module.Position = module.Bounds.Location; } }