package org.jgraph.example;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import org.jgraph.graph.AbstractCellView;
import org.jgraph.graph.CellView;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphLayoutCache;
import org.jgraph.graph.VertexView;
/**
* Vertex view that supports visual vertex nesting to show inclusion edges in a
* compound graph. Differs from a selection group view as follows:
* <ul>
* <li>Bounds of a compound vertex view with child views are not determined
* directly by the child views' bounds, but rather are independently settable
* like the bounds of a leaf vertex.</li>
* <li>Scaling a parent compound vertex view does not scale its child views,
* although translating a parent compound vertex view does translate its child
* views.</li>
* </ul>
* Like any {@link VertexView}, the bounds of a compound vertex view are forced
* to always remain large enough to enclose its child views. Otherwise, no
* restrictions are placed on the bounds.
*
* @author J. Pulley
*/
public class CompoundVertexView extends VertexView {
/**
* Initializes a new view for a compound vertex.
*/
public CompoundVertexView() {
}
/**
* Initializes a new view for a compound vertex with the specified model
* object.
*
* @param cell
* model object
*/
public CompoundVertexView(Object cell) {
super(cell);
}
/**
* Retrieve this view's bounds from the view's attributes.
*
* @return view's bounds
*/
public Rectangle2D getBounds() {
return GraphConstants.getBounds(getAllAttributes());
}
/**
* Set this view's bounds in the view's attributes. If the new bounds do not
* completely enclose any child vertices, the new bounds are set to the
* {@link Rectangle2D#createUnion(Rectangle2D) union} of the child vertices'
* bounds and the argument.
*
* @param newBounds
* new bounds
*/
public void setBounds(Rectangle2D newBounds) {
GraphConstants.setBounds(getAllAttributes(), newBounds);
checkChildBounds();
}
/**
* Update attributes for this view and indicate to the parent this child has
* been updated.
*/
public void update(GraphLayoutCache cache) {
super.update(cache);
checkChildBounds();
}
/**
* Translate this view and all child views by <code>dx, dy</code>.
*
* @param dx
* x-axis translation
* @param dy
* y-axis translation
*/
public void translate(double dx, double dy) {
getAllAttributes().translate(dx, dy);
int moveableAxis = GraphConstants.getMoveableAxis(getAllAttributes());
if (moveableAxis == GraphConstants.X_AXIS) {
dy = 0;
} else if (moveableAxis == GraphConstants.Y_AXIS) {
dx = 0;
}
Iterator it = childViews.iterator();
while (it.hasNext()) {
Object view = it.next();
if (view instanceof AbstractCellView) {
AbstractCellView child = (AbstractCellView) view;
child.translate(dx, dy);
}
}
}
/**
* Scale this view by <code>sx</code> and <code>sy</code>, relative to
* <code>origin</code>. Child views are not scaled.
*
* @param sx
* x scaling factor
* @param sy
* y scaling factor
*/
public void scale(double sx, double sy, Point2D origin) {
getAllAttributes().scale(sx, sy, origin);
checkChildBounds();
}
/**
* If this view's current bounds do not completely enclose all child vertex
* views, sets this view's bounds to the union of the current bounds and the
* childrens' bounds.
*/
private void checkChildBounds() {
if (!isLeaf()) {
Rectangle2D bounds = GraphConstants.getBounds(getAllAttributes());
if (bounds == null) {
bounds = new Rectangle2D.Double();
}
CellView[] childViewArray = (CellView[]) childViews
.toArray(new CellView[0]);
Rectangle2D childBounds = AbstractCellView
.getBounds(childViewArray);
if (!bounds.contains(childBounds)) {
bounds = bounds.createUnion(childBounds);
GraphConstants.setBounds(getAllAttributes(), bounds);
}
}
}
}