private void DomNode_AttributeChanged(object sender, AttributeEventArgs e) { if (IsCircuitItem(e.DomNode, e.DomNode.Parent)) { NotifyObjectChanged(e.DomNode); //required for Layers. http://tracker.ship.scea.com/jira/browse/WWSATF-1389 // Editing the subgraph may cause changes in the parent graph, such as reordering group pins in a group needs // to change the pin indexes of the external edges in the parent graph. // Each circuit or group has its own local editing context, and the default TransactionContext implementation // only responds to these Dom changes from the adapted DomNode and below. // We need to catch changes up in the hierarchy too for proper undo/redo. var circuitValidator = DomNode.GetRoot().As <CircuitValidator>(); if (circuitValidator != null) { var transactionContext = circuitValidator.ActiveHistoryContext; if (transactionContext != null && transactionContext != this) { if (transactionContext.DomNode.Ancestry.FirstOrDefault(x => x == e.DomNode.Parent) != null) { if (transactionContext.InTransaction) { transactionContext.AddOperation(new AttributeChangedOperation(e)); #if DEBUG_VERBOSE var parent = transactionContext.DomNode.Ancestry.FirstOrDefault(x => x == e.DomNode.Parent); Trace.TraceInformation("PARENT GRAPH {0} element {1} -- Attribute {2} changed from {3} to {4}", CircuitUtil.GetDomNodeName(parent), CircuitUtil.GetDomNodeName(e.DomNode), e.AttributeInfo.Name, e.OldValue, e.NewValue); #endif } } } } } }
private bool CanDoTogglePinVisibility(object context, object target) { if (target.Is <Group>()) { if (CircuitUtil.IsTemplateTargetMissing(target)) { return(false); } var viewingContext = context.Cast <CircuitEditingContext>().DomNode.Cast <ViewingContext>(); if (viewingContext.Control != null) { var contextMenu = viewingContext.Control.As <ContextMenuAdapter>(); if (CommandService.ContextMenuIsTriggering && contextMenu != null) { foreach (var pickingAdapter in viewingContext.Control.AsAll <IPickingAdapter2>()) { DiagramHitRecord hitRecord = pickingAdapter.Pick(contextMenu.TriggeringLocation.GetValueOrDefault()); if (hitRecord.Part.Is <GroupPin>()) { return(true); } if (hitRecord.SubPart.Is <GroupPin>()) { return(true); } if (hitRecord.SubPart.Is <ElementType.Pin>()) { return(true); } } } } } return(false); }
/// <summary> /// Performs initialization when the adapter's node is set</summary> protected override void OnNodeSet() { m_subGraphs = new HashSet <Group>(); m_circuits = new HashSet <Circuit>(); m_historyContexts = new HashSet <HistoryContext>(); foreach (DomNode node in DomNode.Subtree) { if (CircuitUtil.IsGroupTemplateInstance(node)) { var template = CircuitUtil.GetGroupTemplate(node); m_templateInstances.Add(template.DomNode, node); m_subGraphs.Add(template); } else if (node.Is <Group>()) { m_subGraphs.Add(node.Cast <Group>()); } else if (node.Is <Circuit>()) { m_circuits.Add(node.Cast <Circuit>()); } } base.OnNodeSet(); }
private void ToggleHideUnconnectedPins() { if (m_targetRef == null || m_targetRef.Target == null) { return; } var group = m_targetRef.Target.Cast <Group>(); if (CircuitUtil.IsGroupTemplateInstance(m_targetRef.Target)) { group = CircuitUtil.GetGroupTemplate(m_targetRef.Target); // for template instances, force update template group pin connectivity // because currently the pin connectivity of a group template is computed on-demand var graphValidator = m_targetRef.Target.Cast <DomNode>().GetRoot().As <CircuitValidator>(); if (graphValidator == null) // it is possible to hold on a templated instance that is converting to a copy instance during rapid mouse clicks, // where the templated instance is no longer a child of root node { return; } graphValidator.UpdateTemplateInfo(group); } // CTE does not set group’s parent during deserialization, // this call ensures the group pins’ external connectivity updated group.UpdateGroupPinInfo(); if (m_allUnconnectedHidden) { foreach (var grpPin in group.InputGroupPins) { foreach (var childGroupPin in grpPin.SinkChain(true)) { childGroupPin.Visible = true; } } foreach (var grpPin in group.OutputGroupPins) { foreach (var childGroupPin in grpPin.SinkChain(false)) { childGroupPin.Visible = true; } } } else { foreach (var grpPin in group.InputGroupPins) { grpPin.Visible = grpPin.Info.ExternalConnected; } foreach (var grpPin in group.OutputGroupPins) { grpPin.Visible = grpPin.Info.ExternalConnected; } } }
private void DomNode_AttributeChanged(object sender, AttributeEventArgs e) { Dirty = true; #if DEBUG_VERBOSE if (e.DomNode == DomNode || e.DomNode.Parent == DomNode) { Trace.TraceInformation("{0} element {1} -- Attribute {2} changed from {3} to {4}", CircuitUtil.GetDomNodeName(DomNode), CircuitUtil.GetDomNodeName(e.DomNode), e.AttributeInfo.Name, e.OldValue, e.NewValue); } #endif }
/// <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; }
private void DomNode_ChildRemoved(object sender, ChildEventArgs e) { Dirty = true; #if DEBUG_VERBOSE if (e.Parent == DomNode) { Trace.TraceInformation("{0} -- Removed {1} from parent {2}", CircuitUtil.GetDomNodeName(DomNode), CircuitUtil.GetDomNodeName(e.Child), CircuitUtil.GetDomNodeName(e.Parent)); } #endif }
/// <summary> /// Updates the pin external connectivity of the connecting group</summary> /// <param name="wire">Wire that has been added or removed in the DOM node tree</param> private void UpdateGroupPinConnectivity(Wire wire) { if (wire.InputElement == null || wire.OutputElement == null) { return; } var updatedNodes = new List <DomNode>(); // need to update the pin external connectivity of the connecting group if (CircuitUtil.IsGroupTemplateInstance(wire.InputElement.DomNode)) { var template = CircuitUtil.GetGroupTemplate(wire.InputElement.DomNode); if (template != null) // if the template is not missing { updatedNodes.Add(template.DomNode); UpdateTemplateInfo(template); } } else if (wire.InputElement.DomNode.Is <Group>()) { updatedNodes.Add(wire.InputElement.DomNode); wire.InputElement.DomNode.Cast <Group>().UpdateGroupPinInfo(); } if (CircuitUtil.IsGroupTemplateInstance(wire.OutputElement.DomNode)) { var template = CircuitUtil.GetGroupTemplate(wire.OutputElement.DomNode); if (template != null) // if the template is not missing { updatedNodes.Add(template.DomNode); UpdateTemplateInfo(template); } } else if (wire.OutputElement.DomNode.Is <Group>()) { updatedNodes.Add(wire.OutputElement.DomNode); wire.OutputElement.DomNode.Cast <Group>().UpdateGroupPinInfo(); } // let's assume all the updated groups have changed connectivity, and notify the graph adaptors foreach (var group in updatedNodes) { var editingContext = group.As <CircuitEditingContext>(); if (editingContext != null) { editingContext.NotifyObjectChanged(group); } } }
/// <summary> /// AttributeChanged event handler for document DomNode</summary> /// <param name="sender">Sender (root DOM node)</param> /// <param name="e">Attribute change event args</param> protected virtual void OnDocumentNodeAttributeChanged(object sender, AttributeEventArgs e) { var group = e.DomNode.As <Group>(); if (group != null && (group.IsNameAttribute(e.AttributeInfo))) { // update ControlInfo.Name for all group controls foreach (var circuitControl in m_circuitNodeControls) { if (circuitControl.Key.Is <Group>()) { circuitControl.Value.Second.Name = CircuitUtil.GetGroupPath(circuitControl.Key.Cast <Group>()); } } } }
private void RemoveSubtree(DomNode root) { foreach (DomNode node in root.Subtree) { if (CircuitUtil.IsGroupTemplateInstance(node)) { var template = CircuitUtil.GetGroupTemplate(node); m_templateInstances.Remove(template.DomNode, node); } else if (node.Is <Group>()) { m_subGraphs.Remove(node.Cast <Group>()); } else if (node.Is <Circuit>()) { m_circuits.Remove(node.Cast <Circuit>()); } } }
private void DomNode_ChildInserted(object sender, ChildEventArgs e) { AddNode(e.Child); if (IsCircuitItem(e.Child, e.Parent)) { if (e.Child.Is <Wire>()) { var connection = e.Child.Cast <Wire>(); if (connection.InputElement.Is <Group>()) // set dirty to force update group pin connectivity { connection.InputElement.Cast <Group>().Dirty = true; } if (connection.OutputElement.Is <Group>()) { connection.OutputElement.Cast <Group>().Dirty = true; } } OnObjectInserted(new ItemInsertedEventArgs <object>(e.Index, e.Child, e.Parent)); var circuitValidator = DomNode.GetRoot().As <CircuitValidator>(); if (circuitValidator != null) { var transactionContext = circuitValidator.ActiveHistoryContext; if (transactionContext != null && transactionContext != this) { if (transactionContext.DomNode.Ancestry.FirstOrDefault(x => x == e.Parent) != null) { if (transactionContext.InTransaction) { transactionContext.AddOperation(new ChildInsertedOperation(e)); #if DEBUG_VERBOSE var parent = transactionContext.DomNode.Ancestry.FirstOrDefault(x => x == e.Parent); Trace.TraceInformation("PARENT GRAPH {0} -- Added {1} to parent {2}", CircuitUtil.GetDomNodeName(parent), CircuitUtil.GetDomNodeName(e.Child), CircuitUtil.GetDomNodeName(e.Parent)); #endif } } } } } }
private void AddSubtree(DomNode root) { foreach (DomNode node in root.Subtree) { if (CircuitUtil.IsGroupTemplateInstance(node)) { var template = CircuitUtil.GetGroupTemplate(node); if (template != null) // if the template is not missing { m_templateInstances.Add(template.DomNode, node); m_subGraphs.Add(template); } } else if (node.Is <Group>()) { m_subGraphs.Add(node.Cast <Group>()); } else if (node.Is <Circuit>()) { m_circuits.Add(node.Cast <Circuit>()); } } }
/// <summary> /// Performs initialization when the adapter's node is set</summary> protected override void OnNodeSet() { m_subGraphs = new HashSet <Group>(); m_circuits = new HashSet <Circuit>(); m_historyContexts = new HashSet <HistoryContext>(); foreach (DomNode node in DomNode.Subtree) { if (CircuitUtil.IsGroupTemplateInstance(node)) { var template = CircuitUtil.GetGroupTemplate(node); if (template != null) { m_templateInstances.Add(template.DomNode, node); m_subGraphs.Add(template); } } else if (node.Is <Group>()) { m_subGraphs.Add(node.Cast <Group>()); } else if (node.Is <Circuit>()) { m_circuits.Add(node.Cast <Circuit>()); } } base.OnNodeSet(); // Since templates may be externally referenced & edited, better to validate and fix the dangling wires // that were connected to already deleted sub-nodes of a template if (m_templateInstances.Keys.Any()) { UpdateWires(m_subGraphs); UpdateWires(m_circuits); } }
/// <summary> /// Gets the current rendering style for an item</summary> /// <param name="item">Rendered item</param> /// <returns>Rendering style set by SetStyle, Normal if no override is set</returns> public virtual DiagramDrawingStyle GetStyle(object item) { DiagramDrawingStyle result = DiagramDrawingStyle.Normal; // no override if (m_visibilityContext != null && !m_visibilityContext.IsVisible(item)) { result = DiagramDrawingStyle.Hidden; } else if (item == m_hoverObject || item == m_hoverSubObject) { if (CircuitUtil.IsGroupTemplateInstance(item)) { result = DiagramDrawingStyle.TemplatedInstance; } else if (item.Is <Group>()) { result = DiagramDrawingStyle.CopyInstance; } else { result = DiagramDrawingStyle.Hot; } } else if (m_selectionContext != null && m_selectionContext.SelectionContains(item)) { if (m_selectionContext.LastSelected.Equals(item)) { result = DiagramDrawingStyle.LastSelected; } else { result = DiagramDrawingStyle.Selected; } } else if (m_selectionPathProvider != null && m_selectionPathProvider.IncludedPath(item) != null) { if (CircuitUtil.IsGroupTemplateInstance(item)) { result = DiagramDrawingStyle.TemplatedInstance; } else if (item.Is <Group>()) { result = DiagramDrawingStyle.CopyInstance; } else { result = DiagramDrawingStyle.Hot; } } else if (m_renderer.RouteConnecting != null) { // connection context cue: highlight edges that connect to the starting node if (item.Is <TEdge>()) { var edge = item.Cast <TEdge>(); if (m_renderer.RouteConnecting.StartNode.Equals(edge.FromNode) || m_renderer.RouteConnecting.StartNode.Equals(edge.ToNode)) { result = DiagramDrawingStyle.Hot; } } } return(result); }
/// <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; } }
/// <summary> /// Updates command state for given command</summary> /// <param name="commandTag">Command</param> /// <param name="state">Command state to update</param> public void UpdateCommand(object commandTag, CommandState state) { if (commandTag is CommandTag) { if (commandTag.Equals(CommandTag.ResetGroupPinNames)) { if (m_targetRef != null && m_targetRef.Target != null) { object target = m_targetRef.Target; if (target.Is <Group>() && !CircuitUtil.IsTemplateTargetMissing(target)) { var group = target.Cast <Group>(); state.Text = string.Format("Reset Pin Names on \"{0}\"".Localize(), group.Name); } } } else if (commandTag.Equals(CommandTag.ShowExpandedGroupPins)) { if (m_targetRef != null && m_targetRef.Target != null) { object target = m_targetRef.Target; if (target.Is <Group>()) { var group = target.Cast <Group>(); state.Check = group.Info.ShowExpandedGroupPins; state.Text = string.Format("Show Expanded Group Pins on \"{0}\"".Localize(), group.Name); } } } else if (commandTag.Equals(CommandTag.HideUnconnectedPins)) { if (m_targetRef != null && m_targetRef.Target != null) { object target = m_targetRef.Target; if (target.Is <Group>() && !CircuitUtil.IsTemplateTargetMissing(target)) { var group = target.Cast <Group>(); var graphContainer = group.ParentGraph.As <ICircuitContainer>(); if (graphContainer != null) { // check if all unconnected pins are hidden m_allUnconnectedHidden = true; foreach (var grpPin in group.InputGroupPins) { bool externalConectd = graphContainer.Wires.FirstOrDefault( x => x.InputPinTarget.FullyEquals(grpPin.PinTarget)) != null; if (!externalConectd && grpPin.Visible) { m_allUnconnectedHidden = false; break; } } if (m_allUnconnectedHidden) { foreach (var grpPin in group.OutputGroupPins) { bool externalConectd = graphContainer.Wires.FirstOrDefault( x => x.OutputPinTarget.FullyEquals(grpPin.PinTarget)) != null; if (!externalConectd && grpPin.Visible) { m_allUnconnectedHidden = false; break; } } } state.Check = m_allUnconnectedHidden; state.Text = string.Format("Hide Unconnected Pins on \"{0}\"".Localize(), group.Name); } } } } } }
/// <summary> /// Can the client do the command?</summary> /// <param name="commandTag">Command</param> /// <returns>True iff client can do the command</returns> public virtual bool CanDoCommand(object commandTag) { bool enabled = false; var context = m_contextRegistry.GetActiveContext <CircuitEditingContext>(); if (context != null) { var selectionContext = m_contextRegistry.GetActiveContext <ISelectionContext>(); if (commandTag is StandardCommand) { if (StandardCommand.EditGroup.Equals(commandTag)) { var lastSelected = selectionContext.GetLastSelected <Element>(); enabled = (lastSelected != null) && // at least one Module selected selectionContext.Selection.All( // selected elements should have a common parent(i.e. belong to the same container) x => x.Is <Element>() && x.Cast <DomNode>().Parent == lastSelected.DomNode.Parent); if (enabled) { enabled = selectionContext.Selection.All(x => !CircuitUtil.IsTemplateTargetMissing(x)); } if (enabled && (!context.SupportsNestedGroup)) { // if nested group is not supported, then neither any selected item can be a group, // nor any of their ancestry enabled = selectionContext.Selection.All(x => !x.Is <Group>()); if (enabled) { enabled = lastSelected.DomNode.Ancestry.All(x => !x.Is <Group>()); } } } else if (StandardCommand.EditUngroup.Equals(commandTag)) { enabled = selectionContext.Selection.Any() && selectionContext.Selection.All(x => x.Is <Group>() && // selected are all are group instances !(x.Is <IReference <DomNode> >()) // disallow template instances as they suppose acting atomically ); } } else if (commandTag is CommandTag) { if (CommandTag.ShowExpandedGroupPins.Equals(commandTag)) { enabled = m_targetRef != null && m_targetRef.Target.Is <Group>() && m_targetRef.Target.Cast <Group>().Expanded; } else if (CommandTag.ResetGroupPinNames.Equals(commandTag)) { enabled = m_targetRef != null && m_targetRef.Target.Is <Group>() && (m_targetRef.Target is IReference <DomNode>); if (enabled) { enabled = !CircuitUtil.IsTemplateTargetMissing(m_targetRef.Target); } } else if (CommandTag.HideUnconnectedPins.Equals(commandTag)) { enabled = m_targetRef != null && m_targetRef.Target.Is <Group>(); if (enabled) { enabled = !CircuitUtil.IsTemplateTargetMissing(m_targetRef.Target); } } else if (CommandTag.TogglePinVisibility.Equals(commandTag)) { enabled = m_targetRef != null && CanDoTogglePinVisibility(context, m_targetRef.Target); } else if (CommandTag.EdgeStyleDefault.Equals(commandTag)) { enabled = true; } else if (CommandTag.EdgeStyleDirectCurve.Equals(commandTag)) { enabled = true; } } } return(enabled); }