package edu.hawaii.ics.yucheng;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
/**
* A layout manager that supports anchored components. Each component added can
* be anchored to the left, top, right, bottom, or a combination of these
* locations. When the container is resized, the components will stretch or move
* accordingly.
*/
class AnchorLayoutManager implements LayoutManager {
/**
* An anchor associated with a component.
*/
final class Anchor {
private final boolean myAnchorX1;
private final boolean myAnchorX2;
private final boolean myAnchorY1;
private final boolean myAnchorY2;
private final Component myComponent;
private final int myCX;
private final int myCY;
private final int myInsetX1;
private final int myInsetX2;
private final int myInsetY1;
private final int myInsetY2;
private final float myPercentX1;
private final float myPercentX2;
private final float myPercentY1;
private final float myPercentY2;
/**
* Initializes a new instance of the class.
*
* @param flags The anchor flags.
*
* @param component The component.
*/
public Anchor(final String flags, final Component component) {
myComponent = component;
myAnchorX1 = flags.contains(ANCHOR_LEFT);
myAnchorY1 = flags.contains(ANCHOR_TOP);
myAnchorX2 = flags.contains(ANCHOR_RIGHT);
myAnchorY2 = flags.contains(ANCHOR_BOTTOM);
myCX = component.getWidth();
myCY = component.getHeight();
myInsetX1 = component.getX();
myInsetY1 = component.getY();
myInsetX2 = myMinimumCX - (myInsetX1 + myCX);
myInsetY2 = myMinimumCY - (myInsetY1 + myCY);
myPercentX1 = (float) myInsetX1 / myMinimumCX;
myPercentY1 = (float) myInsetY1 / myMinimumCY;
myPercentX2 = (float) myInsetX2 / myMinimumCX;
myPercentY2 = (float) myInsetY2 / myMinimumCY;
}
/**
* Sets the bounds on the assigned component based on the size of the
* container.
*
* @param containerCX The width of the container.
*
* @param containerCY The height of the container.
*/
public void setBounds(final int containerCX, final int containerCY) {
int x1, x2;
if (myAnchorX1) {
x1 = myInsetX1;
x2 = myAnchorX2 ? containerCX - myInsetX2 : x1 + myCX;
} else if (myAnchorX2) {
x2 = containerCX - myInsetX2;
x1 = x2 - myCX;
} else {
x1 = (int) (myPercentX1 * containerCX);
x2 = containerCX - (int) (myPercentX2 * containerCX);
}
int y1, y2;
if (myAnchorY1) {
y1 = myInsetY1;
y2 = myAnchorY2 ? containerCY - myInsetY2 : y1 + myCY;
} else if (myAnchorY2) {
y2 = containerCY - myInsetY2;
y1 = y2 - myCY;
} else {
y1 = (int) (myPercentY1 * containerCY);
y2 = containerCY - (int) (myPercentY2 * containerCY);
}
final Rectangle bounds = new Rectangle(x1, y1, x2 - x1, y2 - y1);
myComponent.setBounds(bounds);
}
}
/**
* An anchor flag that indicates the component should anchor to the bottom of
* the container.
*/
public static final String ANCHOR_BOTTOM = "B";
/**
* An anchor flag that indicates the component should anchor to the left of
* the container.
*/
public static final String ANCHOR_LEFT = "L";
/**
* An anchor flag that indicates the component should anchor to the right of
* the container.
*/
public static final String ANCHOR_RIGHT = "R";
/**
* An anchor flag that indicates the component should anchor to the top of the
* container.
*/
public static final String ANCHOR_TOP = "T";
private final Collection<Anchor> myAnchors = new ArrayList<Anchor>();
/**
* The minimum width of the container.
*/
final int myMinimumCX;
/**
* The minimum height of the container.
*/
final int myMinimumCY;
/**
* Initializes a new instance of the class. The minimum width and height must
* be at least one.
*
* @param minimumCX The minimum and initial width of the container.
*
* @param minimumCY The minimum and initial height of the container.
*
* @throws IllegalArgumentException Thrown if either of the minimum sizes are
* less than one.
*/
public AnchorLayoutManager(final int minimumCX, final int minimumCY) {
if (minimumCX < 1)
throw new IllegalArgumentException("minimumCX");
if (minimumCY < 1)
throw new IllegalArgumentException("minimumCY");
myMinimumCX = minimumCX;
myMinimumCY = minimumCY;
}
/**
* Adds a component to the layout manager.
*
* @param flags The layout flags.
*
* @param component The component to add.
*
* @throws NullPointerException Thrown if either of the arguments are null.
*/
public void addLayoutComponent(final String flags, final Component component) {
if (null == flags)
throw new NullPointerException("flags");
if (null == component)
throw new NullPointerException("component");
myAnchors.add(new Anchor(flags, component));
}
/**
* Positions each component added to the layout based on the size of the
* specified container.
*
* @param container The container.
*
* @throws NullPointerException Thrown if the container is null.
*/
public void layoutContainer(final Container container) {
if (null == container)
throw new NullPointerException("container");
final int cx = Math.max(myMinimumCX, container.getWidth());
final int cy = Math.max(myMinimumCY, container.getHeight());
for (final Anchor anchor : myAnchors)
anchor.setBounds(cx, cy);
}
/**
* Returns the minimum layout size. This is the size specified in the
* constructor.
*
* @param container The container (not used).
*
* @return The minimum size.
*
* @throws NullPointerException Thrown if the container is null.
*/
public Dimension minimumLayoutSize(final Container container) {
if (null == container)
throw new NullPointerException("container");
return new Dimension(myMinimumCX, myMinimumCY);
}
/**
* Returns the preferred layout size. This is the maximum of the minimum size
* and the current size of the container.
*
* @param container The container.
*
* @return The preferred size.
*
* @throws NullPointerException Thrown if the container is null.
*/
public Dimension preferredLayoutSize(final Container container) {
if (null == container)
throw new NullPointerException("container");
final int cx = Math.max(myMinimumCX, container.getWidth());
final int cy = Math.max(myMinimumCY, container.getHeight());
return new Dimension(cx, cy);
}
/**
* Removes a component from the layout.
*
* @param component The component to remove.
*
* @throws NullPointerException Thrown if the component is null.
*/
public void removeLayoutComponent(final Component component) {
if (null == component)
throw new NullPointerException("component");
myAnchors.remove(component);
}
}