/// <summary> /// Performs the arrangement. /// </summary> public virtual bool Arrange(FlowChart chart) { if (_ignoreArrowDirection && _root == null) return false; chart.UndoManager.onStartLayout("Tree layout"); // Build the graph FCGraph graph = null; if (_reversedArrows) { graph = new ReversedFCGraph(chart, _keepGroupLayout, _ignoreArrowDirection); } else { graph = new FCGraph(chart, _keepGroupLayout, _ignoreArrowDirection); } // Get the root node within the graph Layout.INode rootNode = null; if (_root != null) { foreach (FCNode node in graph.Nodes) { if (node.Node == _root) { rootNode = node; break; } } } CalculateAnchors(); // Split graph to subgraphs Layout.IGraph[] subgraphs = Layout.GraphSplitter.Split(graph, new FCGraphBuilder(chart, _reversedArrows)); float xOffset = this.XGap; foreach (FCGraph subgraph in subgraphs) { Layout.INode theRoot = null; if (subgraph.Nodes.Contains(rootNode)) { // The root node of the user is within this // subgraph - respect the user's wishes theRoot = rootNode; } else { // If the user specified a root node, // arrange only the graph, containing // that node if (rootNode != null) continue; // If the subgraph has root node, use it, // otherwise select the first node as root Layout.INode subRoot = subgraph.Root; if (subRoot == null) theRoot = subgraph.Nodes[0]; else theRoot = subRoot; } // Check if the root is layoutable if ((theRoot as FCNode).Node.Frozen) continue; Layout.LayoutProgress progress = null; if (_progress != null) progress = new Layout.LayoutProgress( this.OnLayoutProgress); Layout.TreeLayoutInfo info = new Layout.TreeLayoutInfo(); info.Direction = (Layout.TreeLayoutDirection)this.Direction; info.KeepRootPosition = this.KeepRootPosition; info.LevelDistance = this.LevelDistance; info.NodeDistance = this.NodeDistance; info.StretchFactor = this.StretchFactor; info.XGap = this.XGap; info.YGap = this.YGap; if (_layoutNode != null) { // remember the old positions foreach (FCNode node in subgraph.Nodes) node.Node.setData(Constants.OLD_BOUNDS, node.Node.BoundingRect); } // Arrange the subgraph switch (_type) { case TreeLayoutType.Cascading: new Layout.BorderTreeLayout().Arrange( theRoot, info, progress); break; case TreeLayoutType.Centered: new Layout.CenterTreeLayout().Arrange( theRoot, info, progress); break; case TreeLayoutType.Radial: new Layout.RadialTreeLayout().Arrange( theRoot, info, progress); break; } // If the root is not at fixed position, translate the whole tree if (!this.KeepRootPosition) { RectangleF graphBounds = subgraph.GetBounds(false); float xToMove = xOffset - graphBounds.X; float yToMove = this.YGap - graphBounds.Y; foreach (FCNode node in subgraph.Nodes) { RectangleF nodeBounds = node.Bounds; nodeBounds.X += xToMove; nodeBounds.Y += yToMove; node.Bounds = nodeBounds; } xOffset += graphBounds.Width + this.XGap; } if (_layoutNode != null) { // raise the LayoutNode event for each node foreach (FCNode node in subgraph.Nodes) _layoutNode(node.Node, (RectangleF)node.Node.getData(Constants.OLD_BOUNDS)); chart.clearRuntimeData(Constants.OLD_BOUNDS); } // Update the arrows in this particular subgraph ArrayList visitedLinks = new ArrayList(); ArrayList nodes = new ArrayList(); nodes.Add(theRoot); while (nodes.Count > 0) { FCNode node = nodes[0] as FCNode; nodes.RemoveAt(0); if (node.Node.Frozen) continue; foreach (FCLink link in node.OutLinks) { if (!visitedLinks.Contains(link)) { visitedLinks.Add(link); if (!link.Arrow.IgnoreLayout) { if (link.Destination == node) { UpdateArrow(link.Arrow, true); nodes.Add(link.Origin); } else { UpdateArrow(link.Arrow, false); nodes.Add(link.Destination); } if (_layoutLink != null) _layoutLink(link.Arrow); } } } } } chart.Invalidate(); chart.UndoManager.onEndLayout(); return true; }