using System;
using System.Drawing;
using System.Collections;
namespace DevComponents.Tree.Layout
{
///
/// Represents the class that performs node map layout.
///
internal class NodeMapLayout:NodeLayout
{
#region Private Variables
private eMapFlow m_MapFlow=eMapFlow.Spread;
#endregion
public NodeMapLayout(TreeGX treeControl, Rectangle clientArea):base(treeControl,clientArea)
{
}
public override void PerformLayout()
{
this.PrepareStyles();
// Get default Columns
//ArrayList defaultColInfoList=this.GetDefaultColumnInfo();
if(m_Tree.Nodes.Count==0)
return;
Graphics graphics=this.GetGraphics();
try
{
// Loop through each top-level node
Node[] topLevelNodes=this.GetTopLevelNodes();
NodeLayoutContextInfo layoutInfo=this.GetDefaultNodeLayoutContextInfo(graphics);
foreach(Node childNode in topLevelNodes)
{
if(!childNode.Visible)
continue;
layoutInfo.ContextNode=childNode;
ProcessRootNode(layoutInfo);
break;
}
EmptyBoundsUnusedNodes(topLevelNodes);
}
finally
{
if(this.DisposeGraphics)
graphics.Dispose();
}
}
private void ProcessRootNode(NodeLayoutContextInfo layoutInfo)
{
Node node=layoutInfo.ContextNode;
// Center root node on the screen
//if(node.SizeChanged)
//{
// Calculate size of the node itself...
LayoutNode(layoutInfo);
// Calculate size and location of node column header if any
// if(node.NodesColumnHeaderVisible)
// {
// LayoutColumnHeader(layoutInfo);
// }
//}
// if(node.Expanded && node.NodesColumnHeaderVisible && node.NodesColumns.Count>0)
// layoutInfo.Top+=node.ColumnHeaderHeight;
NodesMapPosition positions=NodesMapPosition.Empty;
bool bNear=false;
if(node.Expanded)
{
if(node.NodesColumns.Count>0)
layoutInfo.ChildColumns=GetNodeColumnInfo(node);
foreach(Node childNode in node.Nodes)
{
if(!childNode.Visible)
continue;
layoutInfo.ContextNode=childNode;
if(bNear || childNode.MapSubRootPosition==eMapPosition.Near)
{
layoutInfo.MapPositionNear=true;
ProcessSubNode(layoutInfo);
positions.Near.Add(childNode);
positions.NearHeight+=GetTotalChildNodeHeight(childNode);
bNear=true;
}
else if(childNode.MapSubRootPosition==eMapPosition.Far)
{
layoutInfo.MapPositionNear=false;
ProcessSubNode(layoutInfo);
positions.Far.Add(childNode);
positions.FarHeight+=GetTotalChildNodeHeight(childNode);
}
else
positions.Default.Add(childNode);
}
// Assign nodes to appropriate sides....
if(positions.Default.Count>0)
{
int toFar=0;
if(positions.Near.Count==0 && positions.Far.Count==0 && positions.Default.Count<=4)
toFar=2;
else
toFar=(int)Math.Ceiling((double)((positions.Near.Count-positions.Far.Count+positions.Default.Count))/2);
for(int i=0;i0)
layoutInfo.ChildColumns=GetNodeColumnInfo(node);
Rectangle childNodesBounds=Rectangle.Empty;
foreach(Node childNode in node.Nodes)
{
if(!childNode.Visible)
continue;
layoutInfo.ContextNode=childNode;
layoutInfo.ChildColumns=childColumns;
ProcessSubNode(layoutInfo);
childNodesBounds.Height+=(Math.Max(childNode.BoundsRelative.Height,childNode.ChildNodesBounds.Height)+this.NodeVerticalSpacing);
childNodesBounds.Width=Math.Max(childNodesBounds.Width,childNode.BoundsRelative.Width+(childNode.ChildNodesBounds.Width>0?this.NodeHorizontalSpacing+childNode.ChildNodesBounds.Width:0));
}
layoutInfo.ChildColumns=parentColumns;
if(childNodesBounds.Height>0)
childNodesBounds.Height-=this.NodeVerticalSpacing;
node.ChildNodesBounds=childNodesBounds;
layoutInfo.ChildColumns=null;
layoutInfo.ContextNode=node;
}
else node.ChildNodesBounds=Rectangle.Empty;
}
private void PositionCenterRootSubNodes(Node root, NodesMapPosition positions)
{
Rectangle area=Rectangle.Empty;
area=Rectangle.Union(area,root.BoundsRelative);
// Far Nodes Layout
//int y=0, x=0;
int areaHeight=positions.FarHeight;
int minAreaHeight=this.GetMinimumAreaHeight(positions.Far,root.BoundsRelative.Height);
if(areaHeightnode.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
y+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
if(root.BoundsRelative.IntersectsWith(new Rectangle(root.BoundsRelative.X,y,node.BoundsRelative.Width,node.BoundsRelative.Height)))
{
if(twoNodesLayout && index==1)
{
y = root.BoundsRelative.Bottom + this.NodeVerticalSpacing/2;
if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
y+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
}
else
{
// Change X position since Y is already accounted for...
x+=(root.BoundsRelative.Right-x+this.NodeHorizontalSpacing);
}
}
x+=node.Offset;
OffsetNodeLocation(node,x,y);
if(node.Expanded && node.AnyVisibleNodes)
{
PositionSubNodes(node,true);
usedArea=Rectangle.Union(usedArea,node.ChildNodesBounds);
}
else
usedArea=Rectangle.Union(usedArea,node.BoundsRelative);
if(node.Expanded && node.AnyVisibleNodes)
{
top+=(Math.Max(node.ChildNodesBounds.Height,node.BoundsRelative.Height)+this.NodeVerticalSpacing);
}
else
{
top+=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
}
}
}
private void PositionNearSubRootNodes(Node root, ArrayList nearNodes, int areaRightMost, int areaTop, int areaHeight, ref Rectangle usedArea)
{
int bottom=areaTop+(areaHeight+this.NodeVerticalSpacing*(nearNodes.Count-1));
bool twoNodesLayout = (nearNodes.Count == 2);
for(int index=0;indexnode.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
y=bottom-((node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2+node.BoundsRelative.Height);
else
y=bottom-node.BoundsRelative.Height;
// Rectangle rNodeArea=new Rectangle(x,y,node.Bounds.Width,node.Bounds.Height);
// rNodeArea.Inflate(this.NodeHorizontalSpacing,this.NodeVerticalSpacing);
//if(root.Bounds.IntersectsWith(rNodeArea))
if(root.BoundsRelative.IntersectsWith(new Rectangle(root.BoundsRelative.X,y,node.BoundsRelative.Width,node.BoundsRelative.Height)))
{
if(twoNodesLayout && index==1)
{
y = root.BoundsRelative.Top - this.NodeVerticalSpacing/2;
if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && node.AnyVisibleNodes)
y-=((node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2+node.BoundsRelative.Height);
else
y-=node.BoundsRelative.Height;
}
else
{
// Change X position since Y is already accounted for...
x-=(x+node.BoundsRelative.Width-root.BoundsRelative.X+this.NodeHorizontalSpacing);
}
}
x-=node.Offset;
OffsetNodeLocation(node,x,y);
if(node.Expanded && node.AnyVisibleNodes)
{
PositionSubNodes(node,false);
usedArea = Rectangle.Union(usedArea, node.BoundsRelative);
usedArea=Rectangle.Union(usedArea,node.ChildNodesBounds);
}
else
usedArea=Rectangle.Union(usedArea,node.BoundsRelative);
if(node.Expanded && node.AnyVisibleNodes)
{
bottom-=(Math.Max(node.ChildNodesBounds.Height,node.BoundsRelative.Height)+this.NodeVerticalSpacing);
}
else
{
bottom-=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
}
}
}
private void PositionSubNodes(Node parentNode,bool bFar)
{
if(parentNode.Nodes.Count==0)
return;
int y=parentNode.BoundsRelative.Y-(parentNode.ChildNodesBounds.Height-parentNode.BoundsRelative.Height)/2;
int x=parentNode.BoundsRelative.Right+this.SubNodeHorizontalSpacing;
if(!bFar)
{
x=parentNode.BoundsRelative.X-this.SubNodeHorizontalSpacing;
}
Rectangle rChildBounds=Rectangle.Empty;
bool bFirst=true;
if(bFar)
{
foreach(Node node in parentNode.Nodes)
{
if(!node.Visible)
continue;
int top=y;
bool anyVisibleNodes=node.AnyVisibleNodes;
if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && anyVisibleNodes)
top+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
OffsetNodeLocation(node,x,top);
if(node.Expanded && anyVisibleNodes)
{
PositionSubNodes(node,bFar);
y+=(Math.Max(node.BoundsRelative.Height,node.ChildNodesBounds.Height)+this.NodeVerticalSpacing);
}
else y+=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
if(bFirst)
{
rChildBounds=node.BoundsRelative;
bFirst=false;
}
else
rChildBounds=Rectangle.Union(rChildBounds,node.BoundsRelative);
if(!node.ChildNodesBounds.IsEmpty)
rChildBounds=Rectangle.Union(rChildBounds,node.ChildNodesBounds);
}
}
else
{
foreach(Node node in parentNode.Nodes)
{
if(!node.Visible)
continue;
int left=x-node.BoundsRelative.Width;
int top=y;
bool anyVisibleNodes=node.AnyVisibleNodes;
if(node.ChildNodesBounds.Height>node.BoundsRelative.Height && node.Expanded && anyVisibleNodes)
top+=(node.ChildNodesBounds.Height-node.BoundsRelative.Height)/2;
OffsetNodeLocation(node,left,top);
if(node.Expanded && anyVisibleNodes)
{
PositionSubNodes(node,bFar);
y+=(Math.Max(node.BoundsRelative.Height,node.ChildNodesBounds.Height)+this.NodeVerticalSpacing);
}
else y+=(node.BoundsRelative.Height+this.NodeVerticalSpacing);
if(bFirst)
{
rChildBounds=node.BoundsRelative;
bFirst=false;
}
else
rChildBounds=Rectangle.Union(rChildBounds,node.BoundsRelative);
if(!node.ChildNodesBounds.IsEmpty)
rChildBounds=Rectangle.Union(rChildBounds,node.ChildNodesBounds);
}
}
parentNode.ChildNodesBounds=rChildBounds;
}
private int GetMinimumAreaHeight(ArrayList nodes, int rootHeight)
{
int height=rootHeight;
if(nodes.Count==1)
height+=(((Node)nodes[0]).BoundsRelative.Height*2);
else if(nodes.Count>=2)
height+=(((Node)nodes[0]).BoundsRelative.Height+((Node)nodes[1]).BoundsRelative.Height);
return height;
}
private int SubNodeHorizontalSpacing
{
get {return 24;}
}
///
/// Returns true if root node should have expanded part
///
protected override bool RootHasExpandedPart
{
get {return false;}
}
private struct NodesMapPosition
{
public ArrayList Near;
public ArrayList Far;
public ArrayList Default;
public int NearHeight;
public int FarHeight;
public static NodesMapPosition Empty
{
get
{
NodesMapPosition m;
m.Default=new ArrayList();
m.Near=new ArrayList();
m.Far=new ArrayList();
m.NearHeight=0;
m.FarHeight=0;
return m;
}
}
}
///
/// Gets or sets the flow of the sub-root nodes for Map layout.
///
public eMapFlow MapFlow
{
get{return m_MapFlow;}
set{m_MapFlow=value;}
}
}
}