using System; using System.Drawing; using System.Collections; using DevComponents.DotNetBar; using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Collections.Generic; namespace DevComponents.AdvTree.Layout { /// /// Summary description for NodeLayout. /// internal abstract class NodeLayout { #region Private Variables protected int m_Height=0; protected int m_Width=0; protected AdvTree m_Tree=null; protected Rectangle m_ClientArea; //protected int m_ExpandAreaWidth=8; protected Size m_ExpandPartSize=new Size(8,8); private Size _CachedExpandPartSize = Size.Empty; 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=3; private int m_NodeHorizontalSpacing=0; private CellLayout m_CellLayout=null; private Graphics m_Graphics=null; #endregion public NodeLayout(AdvTree treeControl, Rectangle clientArea, LayoutSettings layoutSettings) { m_Tree=treeControl; m_ClientArea=clientArea; _LayoutSettings = layoutSettings; } /// /// Performs layout of the nodes inside of the tree control. /// public virtual void PerformLayout() { } public virtual void UpdateTopLevelColumnsWidth() { } private LayoutSettings _LayoutSettings; /// /// Gets or sets layout settings. /// public LayoutSettings LayoutSettings { get { return _LayoutSettings; } set { _LayoutSettings = value; } } /// /// 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 g=this.GetGraphics(); SmoothingMode sm = g.SmoothingMode; TextRenderingHint th = g.TextRenderingHint; if (m_Tree.AntiAlias) { g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; //g.TextRenderingHint = DisplayHelp.AntiAliasTextRenderingHint; } try { NodeLayoutContextInfo layoutInfo=this.GetDefaultNodeLayoutContextInfo(g); layoutInfo.ContextNode=node; if (node.IsDragNode) layoutInfo.DefaultColumns = new NodeColumnInfo(new List(), false); LayoutNode(layoutInfo); } finally { if (m_Tree.AntiAlias) { g.SmoothingMode = sm; //g.TextRenderingHint = th; } g.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(); 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); } if (_LayoutSettings != null) _CachedExpandPartSize = Dpi.Size(_LayoutSettings.ExpandPartSize); else _CachedExpandPartSize = Dpi.Size(m_ExpandPartSize); } /// /// Returns default top-level columns for tree control. /// /// Returns array list of ColumnInfo objects. protected virtual NodeColumnInfo GetDefaultColumnInfo() { List ci = new List(); NodeColumnInfo info = new NodeColumnInfo(ci, false); ColumnHeaderCollection columns=m_Tree.Columns; //int treeWidth = m_Tree.ClientRectangle.Width; if(columns!=null) { for (int i = 0; i < columns.Count; i++) { int columnIndex = columns.DisplayIndexMap[i]; ColumnHeader h = columns[columnIndex]; ColumnInfo columnInfo = new ColumnInfo(h.Bounds.Width, h.Visible, h, columnIndex); ci.Add(columnInfo); info.HasAutoSizeColumn |= columnInfo.AutoSize; } } return info; } /// /// 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 NodeColumnInfo GetNodeColumnInfo(Node node) { if (!node.HasColumns) { return null; } List ci = new List(); NodeColumnInfo info = new NodeColumnInfo(ci, false); ColumnHeaderCollection columns = node.NodesColumns; for (int i = 0; i < columns.Count; i++) { int columnIndex = columns.DisplayIndexMap[i]; ColumnHeader h = columns[columnIndex]; ColumnInfo columnInfo = new ColumnInfo(h.Bounds.Width, h.Visible, h, columnIndex); ci.Add(columnInfo); info.HasAutoSizeColumn |= columnInfo.AutoSize; } return info; } ///// ///// 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; } //private int _ExpandAreaWidth = 24; ///// ///// Returns width of the expand button area. Default is 24 pixels. ///// //public virtual int ExpandAreaWidth //{ // get { return _ExpandAreaWidth; } // set // { // _ExpandAreaWidth = value; // } //} ///// ///// 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.LayoutSettings.CommandAreaWidth - ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Right),layoutInfo.ContextNode.ContentBounds.Y+ ElementStyleLayout.StyleSpacing(nodeStyle,eSpacePart.Border,eStyleSide.Top), this.LayoutSettings.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, int x) { Node node=layoutInfo.ContextNode; Size partSize=GetExpandPartSize(); Rectangle bounds=new Rectangle(x,0,partSize.Width,partSize.Height); if (node.ExpandPartVerticalAlignment == eVerticalAlign.Middle) bounds.Y = (node.BoundsRelative.Height - bounds.Height) / 2; else if (node.ExpandPartVerticalAlignment == eVerticalAlign.Top) bounds.Y = Dpi.Height3; else bounds.Y = node.BoundsRelative.Height - partSize.Height - Dpi.Height3; if (bLeftNode || layoutInfo.ExpandPartAlignedLeft && layoutInfo.LeftToRight) bounds.X += (layoutInfo.ExpandAreaWidth - bounds.Width) / 2; else bounds.X = node.BoundsRelative.Right - layoutInfo.ExpandAreaWidth + (layoutInfo.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 _CachedExpandPartSize; //if (_LayoutSettings != null) // return _LayoutSettings.ExpandPartSize; //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) { bool bHasExpandPart=this.HasExpandPart(layoutInfo); bool bHasCommandPart=this.HasCommandPart(layoutInfo); Node node=layoutInfo.ContextNode; Rectangle nodeRect = new Rectangle(layoutInfo.Left, layoutInfo.Top, 0, 0); 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.LeftToRight; // (layoutInfo.MapPositionNear && layoutInfo.LeftToRight); layoutInfo.LayoutNodeExpandPartWidth = 0; if(bLeftNode && bHasExpandPart || this.ReserveExpandPartSpace) { layoutInfo.LayoutNodeExpandPartWidth = (layoutInfo.ExpandAreaWidth + this.GetCellLayout().CellPartSpacing); width += (layoutInfo.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); y+=ElementStyleLayout.TopWhiteSpace(nodeStyle); 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 && !this.ReserveExpandPartSpace) width += layoutInfo.ExpandAreaWidth; if(bHasCommandPart) { width += this.LayoutSettings.CommandAreaWidth; nodeContentRect.Width += this.LayoutSettings.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 || this.ReserveExpandPartSpace) LayoutExpandPart(layoutInfo,bLeftNode, 0); 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 internal virtual CellLayout GetCellLayout() { if (m_CellLayout == null) m_CellLayout = new CellLayout(this.LayoutSettings); 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.Indent = m_Tree.Indent; layoutInfo.Left=0; layoutInfo.Top=0; layoutInfo.LeftMargin = m_Tree.BackgroundStyle.PaddingLeft; layoutInfo.DefaultFont=m_Tree.Font; layoutInfo.LeftToRight=(this.LeftRight==System.Windows.Forms.LeftRightAlignment.Left); layoutInfo.Graphics=graphics; layoutInfo.Styles=m_Tree.Styles; layoutInfo.FullRowBackgroundNodes = new ArrayList(); 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; layoutInfo.ExpandPartWidth = Dpi.Width(this.Tree.ExpandWidth); layoutInfo.View = this.Tree.View; layoutInfo.TileSize = Dpi.Size(this.Tree.TileSize); layoutInfo.ColumnStyle = this.Tree.ColumnStyleNormal; layoutInfo.ExpandAreaWidth = Dpi.Width(this.LayoutSettings.ExpandAreaWidth); 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 /// Initializes a new instance of the NodeColumnInfo structure. /// /// /// public NodeColumnInfo(List columnInfo, bool hasAutoSizeColumn) { ColumnInfo = columnInfo; HasAutoSizeColumn = hasAutoSizeColumn; } /// /// Gets or sets the list of column info object for the columns. /// public List ColumnInfo; /// /// Gets or sets whether columns have auto-size column. /// public bool HasAutoSizeColumn; } }