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 { /** The pixels anchored to the left. */ private final boolean myAnchorX1; /** The pixels anchored to the right. */ private final boolean myAnchorX2; /** The pixels anchored to the top. */ private final boolean myAnchorY1; /** The pixels anchored to the bottom. */ private final boolean myAnchorY2; /** The component that is anchored. */ private final Component myComponent; /** The initially specified width of the component. */ private final int myCX; /** The initially specified height of the component. */ private final int myCY; /** The amount of inset from the left. */ private final int myInsetX1; /** The amount of inset from the right. */ private final int myInsetX2; /** The amount of inset from the top. */ private final int myInsetY1; /** The amount of inset from the bottom. */ private final int myInsetY2; /** The percent from the left. */ private final float myPercentX1; /** The percent from the right. */ private final float myPercentX2; /** The percent from the top. */ private final float myPercentY1; /** The percent from the bottom. */ 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) { assert null != flags; assert null != 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 flag that indicates the component should anchor to the bottom. */ public static final String ANCHOR_BOTTOM = "B"; /** An flag that indicates the component should anchor to the left. */ public static final String ANCHOR_LEFT = "L"; /** An flag that indicates the component should anchor to the right. */ public static final String ANCHOR_RIGHT = "R"; /** An flag that indicates the component should anchor to the top. */ public static final String ANCHOR_TOP = "T"; /** The anchored components. */ 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); } }