/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Del Myers -- initial API and implementation
*******************************************************************************/
package org.eclipse.zest.custom.sequence.visuals;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import org.eclipse.draw2d.AbstractLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
/**
* Abstract layout for setting packages up in a package tree.
* @author Del Myers
*/
public class PackageTreeLayout extends AbstractLayout {
private HashMap<IFigure, Object> constraints;
public static interface IPackageConstraint {
public List<IFigure> getChildFigures();
}
private class LayoutTree {
HashSet<LayoutNode> roots;
HashSet<IFigure> touched;
public LayoutTree() {
this.roots = new HashSet<LayoutNode>();
this.touched = new HashSet<IFigure>();
}
public void addToTree(IFigure figure) {
Object constraint = getConstraint(figure);
if (constraint instanceof IPackageConstraint) {
createNodes(figure);
}
}
/**
* @param figure
*/
private LayoutNode createNodes(IFigure figure) {
IPackageConstraint constraint = (IPackageConstraint) getConstraint(figure);
if (constraint == null) return null;
LayoutNode node = new LayoutNode(figure, constraint.getChildFigures());
if (touched.contains(figure)) {
return node;
}
touched.add(figure);
List<IFigure> children = constraint.getChildFigures();
if (children.size() == 0) {
roots.add(node);
} else {
for (IFigure child : children) {
LayoutNode childNode = createNodes(child);
if (childNode == null) continue;
if (roots.contains(childNode)) {
roots.remove(childNode);
roots.add(node);
}
}
}
return node;
}
}
private class LayoutNode {
IFigure nodeFigure;
List<IFigure> childFigures;
Collection<LayoutNode> childNodes;
public LayoutNode(IFigure data, List<IFigure> children) {
this.nodeFigure = data;
this.childFigures = children;
}
public boolean equals(Object that) {
if (!(that instanceof LayoutNode)) return false;
return (((LayoutNode)that).nodeFigure.equals(nodeFigure));
}
public int hashCode() {
return nodeFigure.hashCode();
}
public Collection<LayoutNode> getChildNodes() {
if (childNodes != null) {
return childNodes;
}
List<LayoutNode> children = new ArrayList<LayoutNode>();
for (IFigure child : childFigures) {
IPackageConstraint constraint = (IPackageConstraint) getConstraint(child);
LayoutNode node = new LayoutNode(child, constraint.getChildFigures());
children.add(node);
}
childNodes = children;
return childNodes;
}
}
public PackageTreeLayout() {
super();
constraints = new HashMap<IFigure, Object>();
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.AbstractLayout#calculatePreferredSize(org.eclipse.draw2d.IFigure, int, int)
*/
@Override
protected Dimension calculatePreferredSize(IFigure container, int wHint,
int hHint) {
LayoutTree tree = new LayoutTree();
for (Object child : container.getChildren()) {
tree.addToTree((IFigure)child);
}
Dimension d = calculateSize(tree.roots, 0, 0);
d.height += 10;
d.width+=10;
return d;
}
/**
* @param roots
* @return
*/
private Dimension calculateSize(Collection<LayoutNode> nodes, int top, int left) {
int height = 0; int width = 0; int heightCount = 0;
for (LayoutNode node : nodes) {
//get the maximum height.
height = Math.max(node.nodeFigure.getPreferredSize().height, height);
}
heightCount = height;
for (LayoutNode node : nodes) {
Collection<LayoutNode> children = node.getChildNodes();
Dimension size = node.nodeFigure.getPreferredSize();
//node.nodeFigure.setBounds(new Rectangle(left + width, top, size.width, size.height));
if (children.size() == 0) {
width += size.width+10;
} else {
Dimension childDim = calculateSize(node.getChildNodes(), top + height + 10, left + width);
if (height + childDim.height + 10 > heightCount)
heightCount = height + childDim.height + 10;
width += 10 + ((childDim.width > size.width) ? childDim.width : size.width);
}
}
return new Dimension(width, heightCount);
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.LayoutManager#layout(org.eclipse.draw2d.IFigure)
*/
public void layout(IFigure container) {
LayoutTree tree = new LayoutTree();
for (Object child : container.getChildren()) {
tree.addToTree((IFigure)child);
}
Rectangle bounds = new Rectangle();
layoutNodes(tree.roots, 0,10, bounds);
container.translateToParent(bounds);
}
private Dimension layoutNodes(Collection<LayoutNode> nodes, int top, int left, Rectangle bounds) {
int height = 0; int width = 0;
for (LayoutNode node : nodes) {
//get the maximum height.
height = Math.max(node.nodeFigure.getPreferredSize().height, height);
}
for (LayoutNode node : nodes) {
Collection<LayoutNode> children = node.getChildNodes();
Dimension size = node.nodeFigure.getPreferredSize();
Rectangle r = new Rectangle(left + width, top, size.width, size.height);
node.nodeFigure.translateToParent(r);
node.nodeFigure.setBounds(r);
if (bounds.isEmpty()) {
bounds.setBounds(r);
} else {
bounds = bounds.union(r);
}
if (children.size() == 0) {
width += size.width+10;
} else {
Dimension childDim = layoutNodes(node.getChildNodes(), top + height + 10, left + width, bounds);
if (childDim.width > size.width) {
width += childDim.width;
} else {
width += size.width;
}
width += 10;
}
}
return new Dimension(width, height);
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.AbstractLayout#setConstraint(org.eclipse.draw2d.IFigure, java.lang.Object)
*/
@Override
public void setConstraint(IFigure child, Object constraint) {
constraints.put(child, constraint);
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.AbstractLayout#getConstraint(org.eclipse.draw2d.IFigure)
*/
@Override
public Object getConstraint(IFigure child) {
return constraints.get(child);
}
}