package bluej.utility;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;

import javax.swing.SizeRequirements;


| A handy layout manager, similar to BoxLayout but which handles component alignment | in a more useful fashion and which provides some handy additional functionality. | | @author Davin McCall | public class DBoxLayout implements LayoutManager2{ public static int X_AXIS = 0; public static int Y_AXIS = 1;
| A SizeRequirements object representing an empty size | private static final SizeRequirements noSize = new SizeRequirements(0, 0, 0, 0.5f); private int axis;
| minimum required space between components | private int minComponentSpacing;
| preferred space between components | private int componentSpacing;
| Size requirements in the X axis for each component | private SizeRequirements [] sizeReqsX;
| Size requirements in the Y axis for each component | private SizeRequirements [] sizeReqsY;
| Total size requirements in the X axis | private SizeRequirements totalReqsX;
| Total size requirements in the Y axis | private SizeRequirements totalReqsY;
| The number of visible components | private int visibleCount; | During a layout, this keeps track of the current layout position | private int curPos; private boolean needSpaces; private int spacingNumerator; private int spacingDenom; private int spacingRemainder;
| The insets of the parent component (due to its border) | private Insets insets;
| Construct a DBoxLayout to layout components along the given axis. | @param axis either X_AXIS or Y_AXIS | public DBoxLayout(int axis) { this.axis = axis; }
| Construct a DBoxLayout to layout components along the given axis, | with the given amount of space between each components. The minimum | space requirement will always be inserted between each component; the | preferred spacing will be provided only when all components are able | to reach their preferred size. | | @param axis | @param minSpacing | @param prefSpacing | public DBoxLayout(int axis, int minSpacing, int prefSpacing) { this.axis = axis; minComponentSpacing = minSpacing; componentSpacing = prefSpacing; }
| Return true if the X_AXIS is the primary layout axis. | public boolean isXPrimaryAxis() { return axis == X_AXIS; }
| This is called during a layout operation. It places a component | at the current position and updates the current position. | | @param c The component to place | @param space The amount of space given to the component in the | primary axis direction | @param opposedPos The position in the opposed axis | @param opposedSize The size on the opposed axis | private void placeComponent(Component c, int space, int opposedPos, int opposedSize) { int advance = spacingNumerator / spacingDenom; spacingRemainder += spacingNumerator % spacingDenom; if (spacingRemainder > spacingDenom) { spacingRemainder -= spacingDenom; advance++; } if (axis == X_AXIS) { c.setBounds(curPos + insets.left, opposedPos + insets.top, space, opposedSize); curPos += space + advance; } else { c.setBounds(opposedPos + insets.left, curPos + insets.top, opposedSize, space); curPos += space + advance; } }
| Make sure all size requirement calculations are up-to-date. | @param target The container whose layout we are managing | private void calcSizeReqs(Container target) { if (sizeReqsX == null || sizeReqsY == null) { Component [] components = target.getComponents(); recalcSizeReqs(components); } }
| Make sure all size requirement calculations are up-to-date. | Sets needSpaces variable according to whether spacing may need to | be added between components (i.e. there is more than one visible | component, and the spacing amount is non-zero) and visibleCount | to the number of visible components. | | @param components The components in the container whose layout we | are managing | private void calcSizeReqs(Component [] components) { if (sizeReqsX == null || sizeReqsY == null) { recalcSizeReqs(components); } }
| Force re-calculation of all size requirements. A dummy requirement | for the spacing between components is also calculated if necessary. | | @param components The components in the target container | private void recalcSizeReqs(Component [] components) { int extra = (componentSpacing != 0 && components.length != 0) ? 1 : 0; sizeReqsX = new SizeRequirements[components.length + extra]; sizeReqsY = new SizeRequirements[components.length + extra]; totalReqsX = new SizeRequirements(0, 0, 0, 0.5f); totalReqsY = new SizeRequirements(0, 0, 0, 0.5f); visibleCount = 0; int i; for (i = 0; i < components.length; i++) { Component component = components[i]; if (component.isVisible()) { visibleCount++; Dimension min = component.getMinimumSize(); Dimension pref = component.getPreferredSize(); Dimension max = component.getMaximumSize(); sizeReqsX[i] = new SizeRequirements(min.width, pref.width, max.width, component.getAlignmentX()); sizeReqsY[i] = new SizeRequirements(min.height, pref.height, max.height, component.getAlignmentY()); if (isXPrimaryAxis()) { addReqs(totalReqsX, sizeReqsX[i]); maxReq(totalReqsY, sizeReqsY[i]); } else { addReqs(totalReqsY, sizeReqsY[i]); maxReq(totalReqsX, sizeReqsX[i]); } } else { sizeReqsX[i] = noSize; sizeReqsY[i] = noSize; } } if (visibleCount > 1 && componentSpacing != 0) { needSpaces = true; sizeReqsX[i] = new SizeRequirements(); sizeReqsY[i] = new SizeRequirements(); int minSpacing = (visibleCount - 1) * minComponentSpacing; int spacing = (visibleCount - 1) * componentSpacing; if (isXPrimaryAxis()) { sizeReqsX[i].minimum = minSpacing; sizeReqsX[i].preferred = spacing; sizeReqsX[i].maximum = spacing; addReqs(totalReqsX, sizeReqsX[i]); } else { sizeReqsY[i].minimum = minSpacing; sizeReqsY[i].preferred = spacing; sizeReqsY[i].maximum = spacing; addReqs(totalReqsY, sizeReqsY[i]); } } else { needSpaces = false; } }
| Adjust a dimension to take the given insets into account. | | @param d The dimension to adjust (will be modified) | @param insets The insets to consider | @return The adjusted Dimension object | private Dimension adjustSizeForInsets(Dimension d, Insets insets) { d.width = (int) Math.min((long) d.width + insets.left + insets.right, Integer.MAX_VALUE); d.height = (int) Math.min((long) d.height + insets.top + insets.bottom, Integer.MAX_VALUE); return d; }
| Add one set of size requirements (b) to another (a). | @param a The first set of size requirements (will be modified) | @param b The second set of size requirements | private void addReqs(SizeRequirements a, SizeRequirements b) { a.minimum = restrictedAdd(a.minimum, b.minimum); a.preferred = restrictedAdd(a.preferred, b.preferred); a.maximum = restrictedAdd(a.maximum, b.maximum); }
| Add two integers, but cap the result at Integer.MAX_VALUE | @param a The first integer | @param b The second integer | @return The result | private int restrictedAdd(int a, int b) { return (int) Math.min((long) a + b, Integer.MAX_VALUE); }
| Make sure that one set of size requirements (a) is at least as | large as another (b). | @param a The first set of size requirements | @param b The second set of size requirements | private void maxReq(SizeRequirements a, SizeRequirements b) { a.minimum = Math.max(a.minimum, b.minimum); a.preferred = Math.max(a.preferred, b.preferred); a.maximum = Math.max(a.maximum, b.maximum); }
| Get the "A-size" from a dimension - that is the size along the |* layout axis. * * @param d The dimension to get the "A-size" from * @return The "A-size" of the dimension */ private int getASize(Dimension d) { if (axis == X_AXIS) { return d.width; } else { return d.height; | |} | |} | |/** | Get the "B-size" from a dimension - that is the size along the |* opposing axis of the layout axis. * * @param d The dimension to get the "B-size" from * @return The "A-size" of the dimension */ private int getBSize(Dimension d) { if (axis == X_AXIS) { return d.height; } else { return d.width; | |} | |} | |/** | Return the alignment value of a component along the opposed axis | @param component | @return | private float getOpposedAlignment(Component component) { if (axis == X_AXIS) { return component.getAlignmentY(); } else { return component.getAlignmentX(); } }
| Make a duplicate of a SizeRequirements object. | @param src The object to duplicate | @return The duplicate object | private SizeRequirements copySizeReqs(SizeRequirements src) { return new SizeRequirements(src.minimum, src.preferred, src.maximum, src.alignment); } @Override public void addLayoutComponent(Component comp, Object constraints) { } @Override public float getLayoutAlignmentX(Container target) { return 0.5f; } @Override public float getLayoutAlignmentY(Container target) { return 0.5f; } @Override public void invalidateLayout(Container target) { sizeReqsX = null; sizeReqsY = null; } @Override public void addLayoutComponent(String name, Component comp) { } @Override public void removeLayoutComponent(Component comp) { } @Override public Dimension minimumLayoutSize(Container parent) { calcSizeReqs(parent); Insets insets = parent.getInsets(); Dimension d = new Dimension(totalReqsX.minimum, totalReqsY.minimum); return adjustSizeForInsets(d, insets); } @Override public Dimension preferredLayoutSize(Container parent) { calcSizeReqs(parent); Insets insets = parent.getInsets(); Dimension d = new Dimension(totalReqsX.preferred, totalReqsY.preferred); return adjustSizeForInsets(d, insets); } @Override public Dimension maximumLayoutSize(Container parent) { calcSizeReqs(parent); Insets insets = parent.getInsets(); Dimension d = new Dimension(totalReqsX.maximum, totalReqsY.maximum); return adjustSizeForInsets(d, insets); } @Override public void layoutContainer(Container parent) { Component [] components = parent.getComponents(); if (components.length == 0) { return; } calcSizeReqs(components); insets = parent.getInsets(); Dimension parentSize = parent.getSize(); int availSize = getASize(parentSize); int opposedSize = getBSize(parentSize); SizeRequirements [] sizeReqs; SizeRequirements [] opposedReqs; SizeRequirements totalSizeReqs; if (isXPrimaryAxis()) { sizeReqs = sizeReqsX; opposedReqs = sizeReqsY; totalSizeReqs = copySizeReqs(totalReqsX); availSize -= insets.left + insets.right; opposedSize -= insets.top + insets.bottom; } else { sizeReqs = sizeReqsY; opposedReqs = sizeReqsX; totalSizeReqs = copySizeReqs(totalReqsY); availSize -= insets.top + insets.bottom; opposedSize -= insets.left + insets.right; } int numComponents = components.length + (needSpaces ? 1 : 0); int [] space = new int[numComponents]; int [] diffs = new int[numComponents]; boolean [] fixed = new boolean[numComponents]; if (needSpaces) { int minSpace = minComponentSpacing * (visibleCount - 1); int prefSpace = componentSpacing * (visibleCount - 1); SizeRequirements spaceReqs = sizeReqs[numComponents - 1]; totalSizeReqs.minimum += minSpace - spaceReqs.minimum; totalSizeReqs.preferred += prefSpace - spaceReqs.preferred; totalSizeReqs.maximum += prefSpace - spaceReqs.maximum; spaceReqs.minimum = minSpace; spaceReqs.preferred = prefSpace; spaceReqs.maximum = prefSpace; } if (totalSizeReqs.maximum < availSize) { curPos = (availSize - totalSizeReqs.maximum) / 2; availSize = totalSizeReqs.maximum; } else { curPos = 0; } boolean needAnotherPass; adjust: do { needAnotherPass = false; if (totalSizeReqs.minimum >= availSize) { for (int i = 0; i < numComponents; i++) { space[i] = sizeReqs[i].minimum; } } else if (totalSizeReqs.preferred >= availSize) { if (needSpaces) { SizeRequirements spaceReqs = sizeReqs[numComponents - 1]; int diff = spaceReqs.preferred - spaceReqs.minimum; if (diff + availSize >= totalSizeReqs.preferred) { spaceReqs.preferred -= totalSizeReqs.preferred - availSize; totalSizeReqs.preferred = availSize; } else { spaceReqs.preferred = spaceReqs.minimum; totalSizeReqs.preferred -= diff; } } int totalDiffs = 0; int discrepancy = availSize - totalSizeReqs.minimum; for (int i = 0; i < numComponents; i++) { if (! fixed[i]) { diffs[i] = sizeReqs[i].preferred - sizeReqs[i].minimum; totalDiffs += diffs[i]; } } for (int i = 0; i < numComponents; i++) { if (! fixed[i]) { if (diffs[i] != 0) { space[i] = (int) (sizeReqs[i].minimum + (long) diffs[i] * discrepancy / totalDiffs); } else { space[i] = sizeReqs[i].minimum; fixed[i] = true; } if (space[i] > sizeReqs[i].maximum) { space[i] = sizeReqs[i].maximum; totalSizeReqs.minimum -= sizeReqs[i].minimum; totalSizeReqs.preferred -= sizeReqs[i].preferred; totalSizeReqs.maximum -= sizeReqs[i].maximum; fixed[i] = true; availSize -= space[i]; needAnotherPass = true; continue adjust; } } } } else { if (totalSizeReqs.preferred == 0) { int numComponentsLeft = 0; int unassignedSpace = availSize; for (int i = 0; i < numComponents; i++) { if (! fixed[i]) { numComponentsLeft++; unassignedSpace -= sizeReqs[i].preferred; } } for (int i = 0; i < numComponents; i++) { if (! fixed[i]) { int toAssign = unassignedSpace / numComponentsLeft; space[i] = restrictedAdd(sizeReqs[i].preferred, toAssign); if (space[i] > sizeReqs[i].maximum) { space[i] = sizeReqs[i].maximum; totalSizeReqs.minimum -= sizeReqs[i].minimum; totalSizeReqs.preferred -= sizeReqs[i].preferred; totalSizeReqs.maximum -= sizeReqs[i].maximum; fixed[i] = true; availSize -= space[i]; needAnotherPass = true; continue adjust; } } } } else { for (int i = 0; i < numComponents; i++) { if (! fixed[i]) { space[i] = (int) ((long) sizeReqs[i].preferred * availSize / totalSizeReqs.preferred); if (space[i] > sizeReqs[i].maximum) { space[i] = sizeReqs[i].maximum; totalSizeReqs.minimum -= sizeReqs[i].minimum; totalSizeReqs.preferred -= sizeReqs[i].preferred; totalSizeReqs.maximum -= sizeReqs[i].maximum; fixed[i] = true; availSize -= space[i]; needAnotherPass = true; continue adjust; } } } } } } while (needAnotherPass){; if (needSpaces) { spacingNumerator = space[numComponents - 1]; } spacingDenom = visibleCount - 1; } else { spacingNumerator = 0; spacingDenom = 1; } spacingRemainder = 0; for (int i = 0; i < components.length; i++) { if (components[i].isVisible()) { if (opposedReqs[i].maximum < opposedSize) { float alignment = getOpposedAlignment(components[i]); int offset = (int)((opposedSize - opposedReqs[i].maximum) * alignment); placeComponent(components[i], space[i], offset, opposedReqs[i].maximum); } else { placeComponent(components[i], space[i], 0, opposedSize); } } } } }
top, use, map, class DBoxLayout

.   DBoxLayout
.   DBoxLayout
.   isXPrimaryAxis
.   placeComponent
.   calcSizeReqs
.   calcSizeReqs
.   recalcSizeReqs
.   adjustSizeForInsets
.   addReqs
.   restrictedAdd
.   maxReq
.   getOpposedAlignment
.   copySizeReqs
.   addLayoutComponent
.   getLayoutAlignmentX
.   getLayoutAlignmentY
.   invalidateLayout
.   addLayoutComponent
.   removeLayoutComponent
.   minimumLayoutSize
.   preferredLayoutSize
.   maximumLayoutSize
.   layoutContainer




749 neLoCode + 72 LoComm