public override void RolePlayerChanged(RolePlayerChangedEventArgs e)
        {
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            if (e.NewRolePlayer != null)
            {
                foreach (PresentationElement pe in e.ElementLink.PresentationRolePlayers)
                {
                    // Fix up any binary link shapes attached to the element link.
                    BinaryLinkShape linkShape = pe as BinaryLinkShape;
                    if (linkShape != null)
                    {
                        if (linkShape.ToShape != null && linkShape.ToShape.ModelElement == e.OldRolePlayer)
                        {
                            // The role player on the "to" side of the link changed, point at new role player
                            PresentationElementMoveableCollection newToShapes = e.NewRolePlayer.PresentationRolePlayers;
                            linkShape.ToShape = newToShapes.Count > 0 ? newToShapes[0] as NodeShape : null;
                        }
                        else if (linkShape.FromShape != null && linkShape.FromShape.ModelElement == e.OldRolePlayer)
                        {
                            // The role player on the "from" side of the link changed, point at new role player
                            PresentationElementMoveableCollection newFromShapes = e.NewRolePlayer.PresentationRolePlayers;
                            linkShape.FromShape = newFromShapes.Count > 0 ? newFromShapes[0] as NodeShape : null;
                        }
                    }
                }
            }
        }
Example #2
0
        /// <summary>
        /// ChangeRule: typeof(ORMBaseBinaryLinkShape), FireTime=TopLevelCommit, Priority=DiagramFixupConstants.AutoLayoutShapesRulePriority;
        /// Keep relative child elements a fixed distance away from the fact
        /// when the shape changes.
        /// </summary>
        private static void LinkChangeRule(ElementPropertyChangedEventArgs e)
        {
            Guid attributeId = e.DomainProperty.Id;

            if (attributeId == ORMBaseBinaryLinkShape.EdgePointsDomainPropertyId)
            {
                ORMBaseBinaryLinkShape parentShape = e.ModelElement as ORMBaseBinaryLinkShape;
                LinkedElementCollection <ShapeElement> childShapes = parentShape.RelativeChildShapes;
                int childCount = childShapes.Count;
                for (int i = 0; i < childCount; ++i)
                {
                    LinkConnectorShape linkConnector = childShapes[i] as LinkConnectorShape;
                    if (linkConnector != null)
                    {
                        RectangleD bounds = parentShape.AbsoluteBoundingBox;
                        linkConnector.Location = new PointD(bounds.Width / 2, bounds.Height / 2);
                        ReadOnlyCollection <LinkConnectsToNode> links = DomainRoleInfo.GetElementLinks <LinkConnectsToNode>(linkConnector, LinkConnectsToNode.NodesDomainRoleId);
                        int linksCount = links.Count;
                        for (int j = 0; j < linksCount; ++j)
                        {
                            LinkConnectsToNode link      = links[j];
                            BinaryLinkShape    linkShape = link.Link as BinaryLinkShape;
                            if (linkShape != null)
                            {
                                // Changing the location is not reliably reconnecting all shapes, especially
                                // during load. Force the link to reconnect with a RecalculateRoute call
                                linkShape.RecalculateRoute();
                            }
                        }
                        break;
                    }
                }
            }
        }
Example #3
0
 /// <summary>
 /// ChangeRule: typeof(RingConstraint), FireTime=TopLevelCommit, Priority=DiagramFixupConstants.AddConnectionRulePriority;
 /// </summary>
 private static void RingConstraintPropertyChangeRule(ElementPropertyChangedEventArgs e)
 {
     if (e.DomainProperty.Id == RingConstraint.RingTypeDomainPropertyId)
     {
         RingConstraint ringConstraint = (RingConstraint)e.ModelElement;
         if (!ringConstraint.IsDeleted)
         {
             foreach (PresentationElement pel in PresentationViewsSubject.GetPresentation(ringConstraint))
             {
                 RingConstraintShape ringConstraintShape = pel as RingConstraintShape;
                 if (ringConstraintShape != null)
                 {
                     foreach (LinkConnectsToNode connection in DomainRoleInfo.GetElementLinks <LinkConnectsToNode>(ringConstraintShape, LinkConnectsToNode.NodesDomainRoleId))
                     {
                         BinaryLinkShape binaryLink = connection.Link as BinaryLinkShape;
                         if (binaryLink != null)
                         {
                             binaryLink.RecalculateRoute();
                         }
                     }
                     SizeD oldSize = ringConstraintShape.Size;
                     ringConstraintShape.AutoResize();
                     if (oldSize == ringConstraintShape.Size)
                     {
                         ringConstraintShape.InvalidateRequired(true);
                     }
                 }
             }
         }
     }
 }
        /// <summary>
        /// This method is called when the rule is fired, that is when a new connection is added to the model.
        /// </summary>
        /// <param name="e">the ElementAddedEventArgs</param>
        public override void ElementAdded(ElementAddedEventArgs e)
        {
            BinaryLinkShape c = e.ModelElement as BinaryLinkShape;

            if (c == null)
            {
                return;
            }

            CompartmentMappingUtil.RerouteCompartmentMappings(c);
        }
        /// <summary>
        /// Reroutes a compartment connection link shape.
        /// </summary>
        /// <remarks>
        /// The start and endpoints of the link will recalculated.
        /// </remarks>
        /// <param name="link">The BinaryLinkShape</param>
        public static void RerouteCompartmentMappings(BinaryLinkShape link)
        {
            // fill the cache
            FindAllCompartmentMappingRouter(link.GetType().Assembly);

            using (Transaction t = link.Store.TransactionManager.BeginTransaction("reroute compartment links"))
            {
                DoRerouteCompartmentMappings(link);
                t.Commit();
            }
        }
Example #6
0
        /// <summary>
        /// Reroutes a compartment connection link shape.
        /// </summary>
        /// <remarks>
        /// The start and endpoints of the link will recalculated.
        /// </remarks>
        /// <param name="link">The BinaryLinkShape</param>
        public static void RerouteCompartmentMappings(BinaryLinkShape link)
        {
            // fill the cache
            FindAllCompartmentMappingRouter(link.GetType().Assembly);

            using (Transaction t = link.Store.TransactionManager.BeginTransaction("reroute compartment links"))
            {
                DoRerouteCompartmentMappings(link);
                t.Commit();
            }
        }
Example #7
0
        /// <summary>
        /// Pulled directly from Reflector disassembly
        /// </summary>
        private static AnchorPoint TestHitAnchor(BinaryLinkShape linkShape, LineSegment segment, SizeD tolerance, PointD hitPoint)
        {
            RectangleD ed1    = new RectangleD(0, 0, tolerance.Width * 2, tolerance.Height * 2);
            NodeShape  shape1 = null;
            bool       flag1  = false;

            if (linkShape != null)
            {
                PointD td1;
                if (segment.IsStartSegment && segment.IsEndSegment)
                {
                    if (ClosestEnd(segment, hitPoint))
                    {
                        td1    = segment.StartPoint;
                        shape1 = linkShape.FromShape;
                        flag1  = true;
                    }
                    else
                    {
                        td1    = segment.EndPoint;
                        shape1 = linkShape.ToShape;
                    }
                }
                else if (segment.IsStartSegment)
                {
                    td1    = segment.StartPoint;
                    shape1 = linkShape.FromShape;
                    flag1  = true;
                }
                else if (segment.IsEndSegment)
                {
                    td1    = segment.EndPoint;
                    shape1 = linkShape.ToShape;
                }
                else
                {
                    return(null);
                }
                if ((shape1 != null) && !shape1.IsPort)
                {
                    ed1.Offset(td1.X - tolerance.Width, td1.Y - tolerance.Height);
                    if (ed1.Contains(hitPoint))
                    {
                        return(new AnchorPoint(linkShape, segment, shape1, tolerance, flag1));
                    }
                }
            }
            return(null);
        }
Example #8
0
        /// <summary>
        /// Reroutes a compartment connection link shape.
        /// </summary>
        /// <remarks>
        /// To call this method you have to fill the cache and start a transaction!
        /// </remarks>
        /// <param name="link">The BinaryLinkShape</param>
        private static void DoRerouteCompartmentMappings(BinaryLinkShape link)
        {
            if (link == null || link.ModelElement == null)
            {
                return;
            }

            Type connectionType = link.ModelElement.GetType();

            //do we have a ICompartmentMappingRouter instance for this type of connection?
            if (allCompartmentMappingRouter.ContainsKey(connectionType))
            {
                allCompartmentMappingRouter[connectionType].CorrectBinaryLinkShapeEndPoints(link);
            }
        }
Example #9
0
        /// <summary>
        /// Repaints the connector shapes for given links.
        /// </summary>
        private static void RepaintRelationshipConnectors <T>(ModelElement element) where T : ElementLink
        {
            var link = element as T;

            if (link != null)
            {
                foreach (var shape in PresentationViewsSubject.GetPresentation(link))
                {
                    BinaryLinkShape connector = shape as BinaryLinkShape;
                    if (connector != null)
                    {
                        connector.Invalidate();
                    }
                }
            }
        }
Example #10
0
        private static void AutoRouteConnector(BinaryLinkShape connector)
        {
            if (connector != null)
            {
                connector.ManuallyRouted = !connector.ManuallyRouted;
                connector.FixedFrom      = VGFixedCode.NotFixed;
                connector.FixedTo        = VGFixedCode.NotFixed;

                foreach (ShapeElement element in connector.RelativeChildShapes)
                {
                    if (element is LineLabelShape lineLabelShape)
                    {
                        lineLabelShape.ManuallySized  = false;
                        lineLabelShape.ManuallyPlaced = false;
                    }
                }

                connector.RecalculateRoute();
                connector.ManuallyRouted = !connector.ManuallyRouted;
            }
        }
Example #11
0
        // ReSharper disable once UnusedParameter.Local
        private static void DoGraphvizLayout(List <DotNode> vertices, List <DotEdge> edges, EFModelDiagram diagram)
        {
            // set up to be a bidirectional graph with the edges we found
            BidirectionalGraph <DotNode, DotEdge> graph = edges.ToBidirectionalGraph <DotNode, DotEdge>();

            // add all the vertices that aren't connected by edges
            graph.AddVertexRange(vertices.Except(edges.Select(e => e.Source).Union(edges.Select(e => e.Target))));

            // we'll process as Graphviz
            GraphvizAlgorithm <DotNode, DotEdge> graphviz = new GraphvizAlgorithm <DotNode, DotEdge>(graph);

            graphviz.GraphFormat.NodeSeparation = 1.0;
            graphviz.GraphFormat.Splines        = GraphvizSplineType.Ortho;
            graphviz.CommonVertexFormat.Shape   = GraphvizVertexShape.Record;

            // labels will be the Id of the underlying Shape
            graphviz.FormatVertex += (sender, args) =>
            {
                args.VertexFormat.Label = args.Vertex.Shape.ModelElement is ModelClass modelClass
                                                                     ? modelClass.Name
                                                                     : args.Vertex.Shape.ModelElement is ModelEnum modelEnum
                                                                        ? modelEnum.Name
                                                                        : args.Vertex.Shape.ModelElement.Id.ToString();
                args.VertexFormat.FixedSize = true;
                args.VertexFormat.Size      = new GraphvizSizeF((float)args.Vertex.Shape.Size.Width,
                                                                (float)args.Vertex.Shape.Size.Height);

                args.VertexFormat.Label = args.Vertex.Shape.Id.ToString();
            };

            graphviz.FormatEdge += (sender, args) =>
            {
                args.EdgeFormat.Label.Value = args.Edge.Shape.Id.ToString();
            };
            // generate the commands
            string dotCommands = graphviz.Generate(new DotEngine(), Path.Combine(Path.GetTempPath(), Path.GetTempFileName()));

            Debug.WriteLine(dotCommands);

            ProcessStartInfo dotStartInfo = new ProcessStartInfo(EFModelPackage.Options.DotExePath, "-T plain")
            {
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                UseShellExecute        = false,
                CreateNoWindow         = true
            };
            string graphOutput;

            using (Process dotProcess = Process.Start(dotStartInfo))
            {
                // stdin is redirected to our stream, so pump the commands in through that
                dotProcess.StandardInput.WriteLine(dotCommands);
                // closing the stream starts the process
                dotProcess.StandardInput.Close();
                // stdout is redirected too, so capture the output
                graphOutput = dotProcess.StandardOutput.ReadToEnd();
                dotProcess.WaitForExit();
            }
            Debug.WriteLine(graphOutput);

            // break it up into lines of text for processing
            string[] outputLines = graphOutput.Split(new [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            SizeD    graphSize   = SizeD.Empty;

            // ReSharper disable once LoopCanBePartlyConvertedToQuery
            foreach (string outputLine in outputLines)
            {
                // spaces aren't valid in any of the data, so we can treat them as delimiters
                string[] parts = outputLine.Split(' ');
                string   id;
                double   x, y;

                // graphviz coordinates have 0,0 at the bottom left, positive means moving up
                // our coordinates have 0,0 at the top left, positive means moving down
                // so we need to transform them
                switch (parts[0])
                {
                case "graph":
                    // 0     1 2      3
                    // graph 1 109.38 92.681
                    graphSize = new SizeD(double.Parse(parts[2]), double.Parse(parts[3]));
                    break;

                case "node":
                    // 0    1  2      3      4   5      6                                      7
                    // node 71 78.514 93.639 1.5 3.3056 "0f651fe7-da0f-453f-a08a-ec1d31ec0e71" solid record black lightgrey
                    id = parts[6].Trim('"');
                    DotNode dotNode = vertices.Single(v => v.Shape.Id.ToString() == id); // label

                    x = double.Parse(parts[2]);
                    y = graphSize.Height - double.Parse(parts[3]);

                    dotNode.Shape.Bounds = new RectangleD(x, y, dotNode.Shape.Size.Width, dotNode.Shape.Size.Height);

                    break;

                case "edge":
                    // 0    1 2  3 4      5      6      7      8      9     10     11    12
                    // edge 6 18 4 34.926 77.518 34.926 77.518 34.926 75.88 34.926 75.88 "567b5db7-7591-4aa7-845c-76635bf56f28" 36.083 77.16 solid black
                    id = parts[4 + int.Parse(parts[3]) * 2].Trim('"');
                    DotEdge edge = edges.Single(e => e.Shape.Id.ToString() == id);

                    // need to mark the connector as dirty. this is the easiest way to do this
                    BinaryLinkShape linkShape = edge.Shape;
                    linkShape.ManuallyRouted = !linkShape.ManuallyRouted;
                    linkShape.FixedFrom      = VGFixedCode.NotFixed;
                    linkShape.FixedTo        = VGFixedCode.NotFixed;

                    // make the labels follow the lines
                    foreach (LineLabelShape lineLabelShape in linkShape.RelativeChildShapes.OfType <LineLabelShape>())
                    {
                        lineLabelShape.ManuallySized  = false;
                        lineLabelShape.ManuallyPlaced = false;
                    }

                    linkShape.EdgePoints.Clear();

                    int pointCount = int.Parse(parts[3]);

                    for (int index = 4; index < 4 + pointCount * 2; index += 2)
                    {
                        x = double.Parse(parts[index]);
                        y = graphSize.Height - double.Parse(parts[index + 1]);
                        linkShape.EdgePoints.Add(new EdgePoint(x, y, VGPointType.Normal));
                    }

                    // since we're not changing the nodes this edge connects, this really doesn't do much.
                    // what it DOES do, however, is call ConnectEdgeToNodes, which is an internal method we'd otherwise
                    // be unable to access
                    linkShape.Connect(linkShape.FromShape, linkShape.ToShape);
                    linkShape.ManuallyRouted = false;

                    break;
                }
            }
        }
        /// <summary>
        /// Correct the end points of a compartment connection to correspond to the entries of the compartmetn shapes.
        /// </summary>
        /// <param name="linkShape">the BinaryLinkShape</param>
        public void CorrectBinaryLinkShapeEndPoints(BinaryLinkShape linkShape)
        {
            CONNECTION connection = (CONNECTION)linkShape.ModelElement;

            if (connection == null)
            {
                return;
            }

            // source
            CompartmentShape fromShape = linkShape.FromShape as CompartmentShape;
            RectangleD       fromRec   = linkShape.FromShape.AbsoluteBoundingBox;
            PointD           leftFrom;
            PointD           rightFrom;

            if (fromShape != null)
            {
                // vertical offset on source
                Double fromOffset = GetVerticalOffset <SOURCE_COMPARTMENT_ENTRY>(fromShape, connection, GetBuilderInstance().IsEntryConnectionSource);
                leftFrom  = new PointD(fromRec.Left, fromRec.Top + fromOffset);
                rightFrom = new PointD(fromRec.Right, fromRec.Top + fromOffset);
            }
            else
            {
                leftFrom = rightFrom = fromRec.Center;
            }

            // taget
            CompartmentShape toShape = linkShape.ToShape as CompartmentShape;
            RectangleD       toRec   = linkShape.ToShape.AbsoluteBoundingBox;
            PointD           leftTarget;
            PointD           rightTarget;

            if (toShape != null)
            {
                // vertical offset on source
                Double toOffset = GetVerticalOffset <TARGET_COMPARTMENT_ENTRY>(toShape, connection, GetBuilderInstance().IsEntryConnectionTarget);
                leftTarget  = new PointD(toRec.Left, toRec.Top + toOffset);
                rightTarget = new PointD(toRec.Right, toRec.Top + toOffset);
            }
            else
            {
                leftTarget = rightTarget = toRec.Center;
            }

            // determinate the connection points
            ConnectionPoints points = GetConnectionPoints(leftFrom, rightFrom, leftTarget, rightTarget);

            // set point for source
            if (fromShape != null)
            {
                linkShape.FromEndPoint = points.Source;
                linkShape.FixedFrom    = VGFixedCode.Caller;
            }

            // set point for taget
            if (toShape != null)
            {
                linkShape.ToEndPoint = points.Target;
                linkShape.FixedTo    = VGFixedCode.Caller;
            }
        }
		/// <summary>
		/// Pulled directly from Reflector disassembly
		/// </summary>
		private static AnchorPoint TestHitAnchor(BinaryLinkShape linkShape, LineSegment segment, SizeD tolerance, PointD hitPoint)
		{
			RectangleD ed1 = new RectangleD(0, 0, tolerance.Width * 2, tolerance.Height * 2);
			NodeShape shape1 = null;
			bool flag1 = false;
			if (linkShape != null)
			{
				PointD td1;
				if (segment.IsStartSegment && segment.IsEndSegment)
				{
					if (ClosestEnd(segment, hitPoint))
					{
						td1 = segment.StartPoint;
						shape1 = linkShape.FromShape;
						flag1 = true;
					}
					else
					{
						td1 = segment.EndPoint;
						shape1 = linkShape.ToShape;
					}
				}
				else if (segment.IsStartSegment)
				{
					td1 = segment.StartPoint;
					shape1 = linkShape.FromShape;
					flag1 = true;
				}
				else if (segment.IsEndSegment)
				{
					td1 = segment.EndPoint;
					shape1 = linkShape.ToShape;
				}
				else
				{
					return null;
				}
				if ((shape1 != null) && !shape1.IsPort)
				{
					ed1.Offset(td1.X - tolerance.Width, td1.Y - tolerance.Height);
					if (ed1.Contains(hitPoint))
					{
						return new AnchorPoint(linkShape, segment, shape1, tolerance, flag1);
					}
				}
			}
			return null;
		}
        /// <summary>
        /// Reroutes a compartment connection link shape.
        /// </summary>
        /// <remarks>
        /// To call this method you have to fill the cache and start a transaction!
        /// </remarks>
        /// <param name="link">The BinaryLinkShape</param>
        private static void DoRerouteCompartmentMappings(BinaryLinkShape link)
        {
            if (link == null || link.ModelElement == null)
                return;

            Type connectionType = link.ModelElement.GetType();
            //do we have a ICompartmentMappingRouter instance for this type of connection?
            if (allCompartmentMappingRouter.ContainsKey(connectionType))
                allCompartmentMappingRouter[connectionType].CorrectBinaryLinkShapeEndPoints(link);
        }
Example #15
0
        private static void UpdateConnectors(GeometryGraph graph)
        {
            foreach (Edge edge in graph.Edges)
            {
                BinaryLinkShape linkShape = (BinaryLinkShape)edge.UserData;

                // need to mark the connector as dirty. this is the easiest way to do this
                linkShape.ManuallyRouted = !linkShape.ManuallyRouted;
                linkShape.FixedFrom      = VGFixedCode.NotFixed;
                linkShape.FixedTo        = VGFixedCode.NotFixed;

                // make the labels follow the lines
                foreach (LineLabelShape lineLabelShape in linkShape.RelativeChildShapes.OfType <LineLabelShape>())
                {
                    lineLabelShape.ManuallySized  = false;
                    lineLabelShape.ManuallyPlaced = false;
                }

                linkShape.EdgePoints.Clear();

                // MSAGL deals in line segments; DSL deals in points
                // with the segments, tne end of one == the beginning of the next, so we can use just the beginning point
                // of each segment.
                // But we have to hang on to the end point so that, when we hit the last segment, we can finish off the
                // set of points
                if (edge.Curve is LineSegment lineSegment)
                {
                    // When curve is a single line segment.
                    linkShape.EdgePoints.Add(new EdgePoint(lineSegment.Start.X, lineSegment.Start.Y, VGPointType.Normal));
                    linkShape.EdgePoints.Add(new EdgePoint(lineSegment.End.X, lineSegment.End.Y, VGPointType.Normal));
                }
                else if (edge.Curve is Curve curve)
                {
                    //// When curve is a complex segment.
                    EdgePoint lastPoint = null;

                    foreach (ICurve segment in curve.Segments)
                    {
                        switch (segment.GetType().Name)
                        {
                        case "LineSegment":
                            LineSegment line = segment as LineSegment;
                            linkShape.EdgePoints.Add(new EdgePoint(line.Start.X, line.Start.Y, VGPointType.Normal));
                            lastPoint = new EdgePoint(line.End.X, line.End.Y, VGPointType.Normal);

                            break;

                        case "CubicBezierSegment":
                            CubicBezierSegment bezier = segment as CubicBezierSegment;

                            // there are 4 segments. Store all but the last one
                            linkShape.EdgePoints.Add(new EdgePoint(bezier.B(0).X, bezier.B(0).Y, VGPointType.Normal));
                            linkShape.EdgePoints.Add(new EdgePoint(bezier.B(1).X, bezier.B(1).Y, VGPointType.Normal));
                            linkShape.EdgePoints.Add(new EdgePoint(bezier.B(2).X, bezier.B(2).Y, VGPointType.Normal));
                            lastPoint = new EdgePoint(bezier.B(3).X, bezier.B(3).Y, VGPointType.Normal);

                            break;

                        case "Ellipse":
                            // rather than draw a curved line, we'll bust the curve into 5 parts and draw those as straight lines
                            Ellipse ellipse  = segment as Ellipse;
                            double  interval = (ellipse.ParEnd - ellipse.ParStart) / 5.0;
                            lastPoint = null;

                            for (double i = ellipse.ParStart; i <= ellipse.ParEnd; i += interval)
                            {
                                Point p = ellipse.Center
                                          + (Math.Cos(i) * ellipse.AxisA)
                                          + (Math.Sin(i) * ellipse.AxisB);

                                // we'll remember the one we just calculated, but store away the one we calculated last time around
                                // (if there _was_ a last time around). That way, when we're done, we'll have stored all of them except
                                // for the last one
                                if (lastPoint != null)
                                {
                                    linkShape.EdgePoints.Add(lastPoint);
                                }

                                lastPoint = new EdgePoint(p.X, p.Y, VGPointType.Normal);
                            }

                            break;
                        }
                    }

                    // finally tuck away the last one. Now we don't have duplicate points in our list
                    if (lastPoint != null)
                    {
                        linkShape.EdgePoints.Add(lastPoint);
                    }
                }

                // since we're not changing the nodes this edge connects, this really doesn't do much.
                // what it DOES do, however, is call ConnectEdgeToNodes, which is an internal method we'd otherwise
                // be unable to access
                linkShape.Connect(linkShape.FromShape, linkShape.ToShape);
                linkShape.ManuallyRouted = false;
            }
        }
Example #16
0
        private void AddElementsToActiveDiagram(List <ModelElement> newElements)
        {
            // TODO: Needs sped up
            int elementCount = newElements.Count;
            List <ShapeElement> newShapes = new List <ShapeElement>();

            using (Transaction t = Store.TransactionManager.BeginTransaction("adding diagram elements"))
            {
                for (int index = 0; index < elementCount; index++)
                {
                    ModelElement newElement = newElements[index];
                    StatusDisplay.Show($"Adding element {index + 1} of {elementCount}");

                    ForceAddShape = true;
                    FixUpAllDiagrams.FixUp(this, newElement);
                    newShapes.Add(newElement.GetFirstShapeElement());
                    ForceAddShape = false;
                }

                t.Commit();
            }

            using (Transaction t = Store.TransactionManager.BeginTransaction("adding diagram links"))
            {
                for (int index = 0; index < elementCount; index++)
                {
                    ModelElement newElement = newElements[index];
                    StatusDisplay.Show($"Linking {index + 1} of {elementCount}");

                    // find all element links that are attached to our element where the ends are in the diagram but the link isn't already in the diagram
                    List <ElementLink> elementLinks = Store.GetAll <ElementLink>()
                                                      .Where(link => link.LinkedElements.Contains(newElement) &&
                                                             link.LinkedElements.All(linkedElement => DisplayedElements.Contains(linkedElement)) &&
                                                             !DisplayedElements.Contains(link))
                                                      .ToList();

                    foreach (ElementLink elementLink in elementLinks)
                    {
                        BinaryLinkShape linkShape = CreateChildShape(elementLink) as BinaryLinkShape;
                        newShapes.Add(linkShape);
                        NestedChildShapes.Add(linkShape);

                        switch (elementLink)
                        {
                        case Association a:
                            linkShape.FromShape = a.Source.GetFirstShapeElement() as NodeShape;
                            linkShape.ToShape   = a.Target.GetFirstShapeElement() as NodeShape;

                            break;

                        case Generalization g:
                            linkShape.FromShape = g.Subclass.GetFirstShapeElement() as NodeShape;
                            linkShape.ToShape   = g.Superclass.GetFirstShapeElement() as NodeShape;

                            break;
                        }
                    }
                }

                AutoLayoutShapeElements(newShapes);
                t.Commit();
            }
        }