using System; using System.Drawing; using System.Collections; namespace DevComponents.Tree.Layout { /// /// Summary description for NodeLayout. /// internal abstract class NodeLayout { #region Private Variables protected int m_Height=0; protected int m_Width=0; protected TreeGX m_Tree=null; protected Rectangle m_ClientArea; //protected int m_ExpandAreaWidth=8; private Size m_ExpandPartSize = new Size(9, 9); private int m_CommandAreaWidth=10; private int m_TreeLayoutChildNodeIndent = 16; private System.Windows.Forms.LeftRightAlignment m_LeftRight=System.Windows.Forms.LeftRightAlignment.Left; private int m_NodeVerticalSpacing=0; private int m_NodeHorizontalSpacing=0; private CellLayout m_CellLayout=null; private Graphics m_Graphics=null; #endregion public NodeLayout(TreeGX treeControl, Rectangle clientArea) { m_Tree=treeControl; m_ClientArea=clientArea; } /// /// Performs layout of the nodes inside of the tree control. /// public virtual void PerformLayout() { } /// /// Performs layout for single unassigned node. Node does not have to be part of the tree control. /// /// Node to perform layout on. public virtual void PerformSingleNodeLayout(Node node) { if(node==null) return; this.PrepareStyles(); // Get default Columns System.Drawing.Graphics graphics=this.GetGraphics(); try { NodeLayoutContextInfo layoutInfo=this.GetDefaultNodeLayoutContextInfo(graphics); layoutInfo.ContextNode=node; LayoutNode(layoutInfo); } finally { graphics.Dispose(); } } public int Width { get {return m_Width;} } public int Height { get {return m_Height;} } public Rectangle ClientArea { get {return m_ClientArea;} set {m_ClientArea=value;} } public Graphics Graphics { get { return m_Graphics;} set { m_Graphics = value;} } internal bool DisposeGraphics { get { return (m_Graphics == null); } } protected virtual System.Drawing.Graphics GetGraphics() { if(m_Graphics!=null) return m_Graphics; Graphics g=m_Tree.CreateGraphics(); if(m_Tree.AntiAlias) { g.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; } return g; } /// /// Resizes all styles and prepares them for layout. /// protected virtual void PrepareStyles() { // Resize styles if needed foreach(ElementStyle es in m_Tree.Styles) { if(es.SizeChanged) ElementStyleLayout.CalculateStyleSize(es,m_Tree.Font); } } /// /// Returns default top-level columns for tree control. /// /// Returns array list of ColumnInfo objects. protected virtual ArrayList GetDefaultColumnInfo() { ArrayList ci=new ArrayList(); ColumnHeaderCollection columns=m_Tree.Columns; if(columns!=null) { foreach(ColumnHeader h in columns) { ci.Add(new ColumnInfo(h.Bounds.Width, h.Visible)); } } return ci; } /// /// Returns column information for a given node. /// /// Node to return column information for /// Returns array list of ColumnInfo objects or null if there are no columns defined. protected virtual ArrayList GetNodeColumnInfo(Node node) { if(node.NodesColumns.Count==0) return null; ArrayList ci=new ArrayList(); foreach(ColumnHeader h in node.NodesColumns) { ci.Add(new ColumnInfo(h.Width.Absolute, h.Visible)); } return ci; } /// /// Gets or sets the vertical spacing between nodes in pixels. /// public virtual int NodeVerticalSpacing { get {return m_NodeVerticalSpacing;} set {m_NodeVerticalSpacing=value;} } /// /// Gets or sets the horizontal spacing between nodes in pixels. /// public virtual int NodeHorizontalSpacing { get {return m_NodeHorizontalSpacing;} set {m_NodeHorizontalSpacing=value;} } /// /// Gets or sets the child node indent in pixels. /// public virtual int TreeLayoutChildNodeIndent { get {return m_TreeLayoutChildNodeIndent; } set { m_TreeLayoutChildNodeIndent = value; } } /// /// Returns column header collection for the given column template name. /// /// Name of the column template. /// Column header collection or null if template name cannot be found. public virtual ColumnHeaderCollection GetColumnHeader(string name) { if(name=="" || name==null) return null; return m_Tree.Headers.GetByName(name).Columns; } /// /// Returns width of the expand button area. Default is 8 pixels. /// protected virtual int ExpandAreaWidth { get {return Dpi.Width(m_ExpandPartSize.Width);} } /// /// Gets or sets width of command button area. Default is 8 pixels. /// public virtual int CommandAreaWidth { get {return m_CommandAreaWidth;} set {m_CommandAreaWidth=value;} } /// /// Sets the position and size of the node command button. /// /// Node layout context information protected virtual void LayoutCommandPart(NodeLayoutContextInfo layoutInfo, ElementStyle nodeStyle) { // Command part is right-aligned just before the node border Rectangle bounds=new Rectangle(layoutInfo.ContextNode.ContentBounds.Right-this.CommandAreaWidth- ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Right),layoutInfo.ContextNode.ContentBounds.Y+ ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Top), this.CommandAreaWidth, layoutInfo.ContextNode.ContentBounds.Height- ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Top)- ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Bottom)); // Rectangle bounds=new Rectangle(layoutInfo.ContextNode.ContentBounds.Right-this.CommandAreaWidth- // ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Right),layoutInfo.ContextNode.ContentBounds.Y, // this.CommandAreaWidth, layoutInfo.ContextNode.ContentBounds.Height); layoutInfo.ContextNode.CommandBoundsRelative=bounds; } /// /// Determines the rectangle of the +/- part of the tree node that is used to expand node. /// /// Node layout context information protected virtual void LayoutExpandPart(NodeLayoutContextInfo layoutInfo, bool bLeftNode) { Node node=layoutInfo.ContextNode; Size partSize=Dpi.Size(GetExpandPartSize()); Rectangle bounds=new Rectangle(0,0,partSize.Width,partSize.Height); bounds.Y=(node.BoundsRelative.Height-bounds.Height)/2; if(bLeftNode) bounds.X=(this.ExpandAreaWidth-bounds.Width)/2; else bounds.X=node.BoundsRelative.Right-this.ExpandAreaWidth+(this.ExpandAreaWidth-partSize.Width)/2; node.SetExpandPartRectangle(bounds); } /// /// Returns the size of the node expand part. /// /// Size of the expand part, default 8,8. protected virtual Size GetExpandPartSize() { return m_ExpandPartSize; } /// /// Gets or sets the size of the expand part that is expanding/collapsing the node. Default value is 8,8. /// public System.Drawing.Size ExpandPartSize { get {return m_ExpandPartSize;} set { m_ExpandPartSize=value; } } /// /// Provides the layout for single node. /// /// Layout information. protected virtual void LayoutNode(NodeLayoutContextInfo layoutInfo) { // if(!layoutInfo.ContextNode.SizeChanged) // return; bool bHasExpandPart=this.HasExpandPart(layoutInfo); bool bHasCommandPart=this.HasCommandPart(layoutInfo); Node node=layoutInfo.ContextNode; Rectangle nodeRect=Rectangle.Empty; Rectangle nodeContentRect=Rectangle.Empty; // Node content rect excludes expand rect int height=0, width=0; // Left node relative to the main root node... bool bLeftNode=(layoutInfo.MapPositionNear && layoutInfo.LeftToRight); if(bLeftNode && bHasExpandPart || this.ReserveExpandPartSpace) { width+=(this.ExpandAreaWidth+this.GetCellLayout().CellPartSpacing); } int x=width; // relative to 0,0 of the node int y=0; // Relative to 0,0 of the node // Apply node style ElementStyle nodeStyle=null; if(node.Expanded && node.StyleExpanded!=null) nodeStyle=node.StyleExpanded; else if(node.Style!=null) nodeStyle=node.Style; else nodeStyle=layoutInfo.DefaultNodeStyle; nodeContentRect.X=x; if(nodeStyle!=null) { x+=ElementStyleLayout.LeftWhiteSpace(nodeStyle); // nodeStyle.MarginLeft+nodeStyle.PaddingLeft; y+=ElementStyleLayout.TopWhiteSpace(nodeStyle); //nodeStyle.MarginTop+nodeStyle.PaddingTop; nodeContentRect.X+=nodeStyle.MarginLeft; nodeContentRect.Y+=nodeStyle.MarginTop; } Size size=this.GetCellLayout().LayoutCells(layoutInfo,x,y); node.SetCellsBounds(new Rectangle(x,y,size.Width,size.Height)); height=size.Height; width+=size.Width; nodeContentRect.Width=size.Width; nodeContentRect.Height=size.Height; if(nodeStyle!=null) { nodeContentRect.Width+=(ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Left)+ ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Right)); nodeContentRect.Height+=(ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Top)+ ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Padding | eSpacePart.Border,eStyleSide.Bottom)); width+=(ElementStyleLayout.HorizontalStyleWhiteSpace(nodeStyle)); height+=(ElementStyleLayout.VerticalStyleWhiteSpace(nodeStyle)); } if(!bLeftNode && bHasExpandPart) width+=this.ExpandAreaWidth; if(bHasCommandPart) { width+=this.CommandAreaWidth; nodeContentRect.Width+=this.CommandAreaWidth; } nodeRect.Height=height; nodeRect.Width=width; node.SetBounds(nodeRect); node.SetContentBounds(nodeContentRect); if(bHasCommandPart) LayoutCommandPart(layoutInfo, nodeStyle); else node.CommandBoundsRelative=Rectangle.Empty; if(bHasExpandPart) LayoutExpandPart(layoutInfo,bLeftNode); else node.SetExpandPartRectangle(Rectangle.Empty); node.SizeChanged=false; // Calculate size and location of node column header if any //if(node.NodesColumnHeaderVisible) { //layoutInfo.Left+=this.NodeLevelOffset; LayoutColumnHeader(layoutInfo); //layoutInfo.Left-=this.NodeLevelOffset; } } /// /// Returns true if given node has expand part. /// /// Layout context information. /// protected virtual bool HasExpandPart(NodeLayoutContextInfo layoutInfo) { Node node=layoutInfo.ContextNode; if(node.ExpandVisibility==eNodeExpandVisibility.Auto) { if(IsRootNode(node) && !RootHasExpandedPart || !NodeOperations.GetAnyVisibleNodes(node)) return false; return true; } else return (node.ExpandVisibility==eNodeExpandVisibility.Visible); } /// /// Returns whether given node has command part. /// /// Layout context information. /// True if command part should be drawn otherwise false. protected virtual bool HasCommandPart(NodeLayoutContextInfo layoutInfo) { return layoutInfo.ContextNode.CommandButton; } /// /// Returns true if root node should have expanded part /// protected virtual bool RootHasExpandedPart { get {return true;} } /// /// Returns true if expand part space should be accounted for even if they expand part is not visible or need to be displayed. Default value is false. /// protected virtual bool ReserveExpandPartSpace { get { return false; } } /// /// Returns class responsible for cell layout. /// /// Cell layout class. protected virtual CellLayout GetCellLayout() { if(m_CellLayout==null) m_CellLayout=new CellLayout(); return m_CellLayout; } /// /// Offsets node location and location of it's child nodes bounds. /// /// Node to offset. /// Horizontal offset. /// Vertical offset. protected virtual void OffsetNodeLocation(Node node, int x, int y) { node.SetBounds(new Rectangle(node.BoundsRelative.X+x,node.BoundsRelative.Y+y,node.BoundsRelative.Width,node.BoundsRelative.Height)); if(node.Expanded) node.ChildNodesBounds=new Rectangle(node.ChildNodesBounds.X+x,node.ChildNodesBounds.Y+y,node.ChildNodesBounds.Width,node.ChildNodesBounds.Height); } protected virtual NodeLayoutContextInfo GetDefaultNodeLayoutContextInfo(System.Drawing.Graphics graphics) { NodeLayoutContextInfo layoutInfo=new NodeLayoutContextInfo(); layoutInfo.ClientRectangle=m_ClientArea; layoutInfo.DefaultColumns=this.GetDefaultColumnInfo(); layoutInfo.ChildColumns=null; layoutInfo.Left=0; layoutInfo.Top=0; // TODO: Include Columns if visible into this... layoutInfo.DefaultFont=m_Tree.Font; layoutInfo.LeftToRight=(this.LeftRight==System.Windows.Forms.LeftRightAlignment.Left); layoutInfo.Graphics=graphics; layoutInfo.Styles=m_Tree.Styles; if(m_Tree.CellLayout!=eCellLayout.Default) layoutInfo.CellLayout=m_Tree.CellLayout; if(m_Tree.CellPartLayout!=eCellPartLayout.Default) layoutInfo.CellPartLayout=m_Tree.CellPartLayout; if(m_Tree.NodeStyle!=null) layoutInfo.DefaultNodeStyle=m_Tree.NodeStyle; if(m_Tree.CellStyleDefault!=null) layoutInfo.DefaultCellStyle=m_Tree.CellStyleDefault; else layoutInfo.DefaultCellStyle=ElementStyle.GetDefaultCellStyle(layoutInfo.DefaultNodeStyle); // Determine size of the default Column Header if(m_Tree.ColumnStyleNormal!=null) { ElementStyleLayout.CalculateStyleSize(m_Tree.ColumnStyleNormal,layoutInfo.DefaultFont); layoutInfo.DefaultHeaderSize=m_Tree.ColumnStyleNormal.Size; } if(layoutInfo.DefaultHeaderSize.IsEmpty) layoutInfo.DefaultHeaderSize.Height=layoutInfo.DefaultFont.Height+4; return layoutInfo; } protected Node[] GetTopLevelNodes() { if(m_Tree.DisplayRootNode!=null) return new Node[] {m_Tree.DisplayRootNode}; else { Node[] nodes=new Node[m_Tree.Nodes.Count]; m_Tree.Nodes.CopyTo(nodes); return nodes; } } protected bool IsRootNode(Node node) { return NodeOperations.IsRootNode(m_Tree,node); } protected virtual void EmptyBoundsUnusedNodes(Node[] topLevelNodes) { if(m_Tree.DisplayRootNode!=null) { Node node=m_Tree.DisplayRootNode.PrevVisibleNode; while(node!=null) { node.SetBounds(Rectangle.Empty); node=node.PrevVisibleNode; } node=m_Tree.DisplayRootNode.NextNode; if(node==null) { node=m_Tree.DisplayRootNode.Parent; while(node!=null) { if(node.NextNode!=null) { node=node.NextNode; break; } else node=node.Parent; } } while(node!=null) { node.SetBounds(Rectangle.Empty); node=node.NextVisibleNode; } } else { for(int i=1;i