/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.swing;
import java.awt.*;
import java.io.
Serializable;
/**
* For the convenience of layout managers,
* calculates information about the size and position of components.
* All size and position calculation methods are class methods
* that take arrays of SizeRequirements as arguments.
* The SizeRequirements class supports two types of layout:
*
* <blockquote>
* <dl>
* <dt> tiled
* <dd> The components are placed end-to-end,
* starting either at coordinate 0 (the leftmost or topmost position)
* or at the coordinate representing the end of the allocated span
* (the rightmost or bottommost position).
*
* <dt> aligned
* <dd> The components are aligned as specified
* by each component's X or Y alignment value.
* </dl>
* </blockquote>
*
* <p>
*
* Each SizeRequirements object contains information
* about either the width (and X alignment)
* or height (and Y alignment)
* of a single component or a group of components:
*
* <blockquote>
* <dl>
* <dt> <code>minimum</code>
* <dd> The smallest reasonable width/height of the component
* or component group, in pixels.
*
* <dt> <code>preferred</code>
* <dd> The natural width/height of the component
* or component group, in pixels.
*
* <dt> <code>maximum</code>
* <dd> The largest reasonable width/height of the component
* or component group, in pixels.
*
* <dt> <code>alignment</code>
* <dd> The X/Y alignment of the component
* or component group.
* </dl>
* </blockquote>
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans™
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @see Component#getMinimumSize
* @see Component#getPreferredSize
* @see Component#getMaximumSize
* @see Component#getAlignmentX
* @see Component#getAlignmentY
*
* @author Timothy Prinzing
*/
public class
SizeRequirements implements
Serializable {
/**
* The minimum size required.
* For a component <code>comp</code>, this should be equal to either
* <code>comp.getMinimumSize().width</code> or
* <code>comp.getMinimumSize().height</code>.
*/
public int
minimum;
/**
* The preferred (natural) size.
* For a component <code>comp</code>, this should be equal to either
* <code>comp.getPreferredSize().width</code> or
* <code>comp.getPreferredSize().height</code>.
*/
public int
preferred;
/**
* The maximum size allowed.
* For a component <code>comp</code>, this should be equal to either
* <code>comp.getMaximumSize().width</code> or
* <code>comp.getMaximumSize().height</code>.
*/
public int
maximum;
/**
* The alignment, specified as a value between 0.0 and 1.0,
* inclusive.
* To specify centering, the alignment should be 0.5.
*/
public float
alignment;
/**
* Creates a SizeRequirements object with the minimum, preferred,
* and maximum sizes set to zero and an alignment value of 0.5
* (centered).
*/
public
SizeRequirements() {
minimum = 0;
preferred = 0;
maximum = 0;
alignment = 0.5f;
}
/**
* Creates a SizeRequirements object with the specified minimum, preferred,
* and maximum sizes and the specified alignment.
*
* @param min the minimum size >= 0
* @param pref the preferred size >= 0
* @param max the maximum size >= 0
* @param a the alignment >= 0.0f && <= 1.0f
*/
public
SizeRequirements(int
min, int
pref, int
max, float
a) {
minimum =
min;
preferred =
pref;
maximum =
max;
alignment =
a > 1.0f ? 1.0f :
a < 0.0f ? 0.0f :
a;
}
/**
* Returns a string describing the minimum, preferred, and maximum
* size requirements, along with the alignment.
*
* @return the string
*/
public
String toString() {
return "[" +
minimum + "," +
preferred + "," +
maximum + "]@" +
alignment;
}
/**
* Determines the total space necessary to
* place a set of components end-to-end. The needs
* of each component in the set are represented by an entry in the
* passed-in SizeRequirements array.
* The returned SizeRequirements object has an alignment of 0.5
* (centered). The space requirement is never more than
* Integer.MAX_VALUE.
*
* @param children the space requirements for a set of components.
* The vector may be of zero length, which will result in a
* default SizeRequirements object instance being passed back.
* @return the total space requirements.
*/
public static
SizeRequirements getTiledSizeRequirements(
SizeRequirements[]
children) {
SizeRequirements total = new
SizeRequirements();
for (int
i = 0;
i <
children.length;
i++) {
SizeRequirements req =
children[
i];
total.
minimum = (int)
Math.
min((long)
total.
minimum + (long)
req.
minimum,
Integer.
MAX_VALUE);
total.
preferred = (int)
Math.
min((long)
total.
preferred + (long)
req.
preferred,
Integer.
MAX_VALUE);
total.
maximum = (int)
Math.
min((long)
total.
maximum + (long)
req.
maximum,
Integer.
MAX_VALUE);
}
return
total;
}
/**
* Determines the total space necessary to
* align a set of components. The needs
* of each component in the set are represented by an entry in the
* passed-in SizeRequirements array. The total space required will
* never be more than Integer.MAX_VALUE.
*
* @param children the set of child requirements. If of zero length,
* the returns result will be a default instance of SizeRequirements.
* @return the total space requirements.
*/
public static
SizeRequirements getAlignedSizeRequirements(
SizeRequirements[]
children) {
SizeRequirements totalAscent = new
SizeRequirements();
SizeRequirements totalDescent = new
SizeRequirements();
for (int
i = 0;
i <
children.length;
i++) {
SizeRequirements req =
children[
i];
int
ascent = (int) (
req.
alignment *
req.
minimum);
int
descent =
req.
minimum -
ascent;
totalAscent.
minimum =
Math.
max(
ascent,
totalAscent.
minimum);
totalDescent.
minimum =
Math.
max(
descent,
totalDescent.
minimum);
ascent = (int) (
req.
alignment *
req.
preferred);
descent =
req.
preferred -
ascent;
totalAscent.
preferred =
Math.
max(
ascent,
totalAscent.
preferred);
totalDescent.
preferred =
Math.
max(
descent,
totalDescent.
preferred);
ascent = (int) (
req.
alignment *
req.
maximum);
descent =
req.
maximum -
ascent;
totalAscent.
maximum =
Math.
max(
ascent,
totalAscent.
maximum);
totalDescent.
maximum =
Math.
max(
descent,
totalDescent.
maximum);
}
int
min = (int)
Math.
min((long)
totalAscent.
minimum + (long)
totalDescent.
minimum,
Integer.
MAX_VALUE);
int
pref = (int)
Math.
min((long)
totalAscent.
preferred + (long)
totalDescent.
preferred,
Integer.
MAX_VALUE);
int
max = (int)
Math.
min((long)
totalAscent.
maximum + (long)
totalDescent.
maximum,
Integer.
MAX_VALUE);
float
alignment = 0.0f;
if (
min > 0) {
alignment = (float)
totalAscent.
minimum /
min;
alignment =
alignment > 1.0f ? 1.0f :
alignment < 0.0f ? 0.0f :
alignment;
}
return new
SizeRequirements(
min,
pref,
max,
alignment);
}
/**
* Creates a set of offset/span pairs representing how to
* lay out a set of components end-to-end.
* This method requires that you specify
* the total amount of space to be allocated,
* the size requirements for each component to be placed
* (specified as an array of SizeRequirements), and
* the total size requirement of the set of components.
* You can get the total size requirement
* by invoking the getTiledSizeRequirements method. The components
* will be tiled in the forward direction with offsets increasing from 0.
*
* @param allocated the total span to be allocated >= 0.
* @param total the total of the children requests. This argument
* is optional and may be null.
* @param children the size requirements for each component.
* @param offsets the offset from 0 for each child where
* the spans were allocated (determines placement of the span).
* @param spans the span allocated for each child to make the
* total target span.
*/
public static void
calculateTiledPositions(int
allocated,
SizeRequirements total,
SizeRequirements[]
children,
int[]
offsets,
int[]
spans) {
calculateTiledPositions(
allocated,
total,
children,
offsets,
spans, true);
}
/**
* Creates a set of offset/span pairs representing how to
* lay out a set of components end-to-end.
* This method requires that you specify
* the total amount of space to be allocated,
* the size requirements for each component to be placed
* (specified as an array of SizeRequirements), and
* the total size requirement of the set of components.
* You can get the total size requirement
* by invoking the getTiledSizeRequirements method.
*
* This method also requires a flag indicating whether components
* should be tiled in the forward direction (offsets increasing
* from 0) or reverse direction (offsets decreasing from the end
* of the allocated space). The forward direction represents
* components tiled from left to right or top to bottom. The
* reverse direction represents components tiled from right to left
* or bottom to top.
*
* @param allocated the total span to be allocated >= 0.
* @param total the total of the children requests. This argument
* is optional and may be null.
* @param children the size requirements for each component.
* @param offsets the offset from 0 for each child where
* the spans were allocated (determines placement of the span).
* @param spans the span allocated for each child to make the
* total target span.
* @param forward tile with offsets increasing from 0 if true
* and with offsets decreasing from the end of the allocated space
* if false.
* @since 1.4
*/
public static void
calculateTiledPositions(int
allocated,
SizeRequirements total,
SizeRequirements[]
children,
int[]
offsets,
int[]
spans,
boolean
forward) {
// The total argument turns out to be a bad idea since the
// total of all the children can overflow the integer used to
// hold the total. The total must therefore be calculated and
// stored in long variables.
long
min = 0;
long
pref = 0;
long
max = 0;
for (int
i = 0;
i <
children.length;
i++) {
min +=
children[
i].
minimum;
pref +=
children[
i].
preferred;
max +=
children[
i].
maximum;
}
if (
allocated >=
pref) {
expandedTile(
allocated,
min,
pref,
max,
children,
offsets,
spans,
forward);
} else {
compressedTile(
allocated,
min,
pref,
max,
children,
offsets,
spans,
forward);
}
}
private static void
compressedTile(int
allocated, long
min, long
pref, long
max,
SizeRequirements[]
request,
int[]
offsets, int[]
spans,
boolean
forward) {
// ---- determine what we have to work with ----
float
totalPlay =
Math.
min(
pref -
allocated,
pref -
min);
float
factor = (
pref -
min == 0) ? 0.0f :
totalPlay / (
pref -
min);
// ---- make the adjustments ----
int
totalOffset;
if(
forward ) {
// lay out with offsets increasing from 0
totalOffset = 0;
for (int
i = 0;
i <
spans.length;
i++) {
offsets[
i] =
totalOffset;
SizeRequirements req =
request[
i];
float
play =
factor * (
req.
preferred -
req.
minimum);
spans[
i] = (int)(
req.
preferred -
play);
totalOffset = (int)
Math.
min((long)
totalOffset + (long)
spans[
i],
Integer.
MAX_VALUE);
}
} else {
// lay out with offsets decreasing from the end of the allocation
totalOffset =
allocated;
for (int
i = 0;
i <
spans.length;
i++) {
SizeRequirements req =
request[
i];
float
play =
factor * (
req.
preferred -
req.
minimum);
spans[
i] = (int)(
req.
preferred -
play);
offsets[
i] =
totalOffset -
spans[
i];
totalOffset = (int)
Math.
max((long)
totalOffset - (long)
spans[
i], 0);
}
}
}
private static void
expandedTile(int
allocated, long
min, long
pref, long
max,
SizeRequirements[]
request,
int[]
offsets, int[]
spans,
boolean
forward) {
// ---- determine what we have to work with ----
float
totalPlay =
Math.
min(
allocated -
pref,
max -
pref);
float
factor = (
max -
pref == 0) ? 0.0f :
totalPlay / (
max -
pref);
// ---- make the adjustments ----
int
totalOffset;
if(
forward ) {
// lay out with offsets increasing from 0
totalOffset = 0;
for (int
i = 0;
i <
spans.length;
i++) {
offsets[
i] =
totalOffset;
SizeRequirements req =
request[
i];
int
play = (int)(
factor * (
req.
maximum -
req.
preferred));
spans[
i] = (int)
Math.
min((long)
req.
preferred + (long)
play,
Integer.
MAX_VALUE);
totalOffset = (int)
Math.
min((long)
totalOffset + (long)
spans[
i],
Integer.
MAX_VALUE);
}
} else {
// lay out with offsets decreasing from the end of the allocation
totalOffset =
allocated;
for (int
i = 0;
i <
spans.length;
i++) {
SizeRequirements req =
request[
i];
int
play = (int)(
factor * (
req.
maximum -
req.
preferred));
spans[
i] = (int)
Math.
min((long)
req.
preferred + (long)
play,
Integer.
MAX_VALUE);
offsets[
i] =
totalOffset -
spans[
i];
totalOffset = (int)
Math.
max((long)
totalOffset - (long)
spans[
i], 0);
}
}
}
/**
* Creates a bunch of offset/span pairs specifying how to
* lay out a set of components with the specified alignments.
* The resulting span allocations will overlap, with each one
* fitting as well as possible into the given total allocation.
* This method requires that you specify
* the total amount of space to be allocated,
* the size requirements for each component to be placed
* (specified as an array of SizeRequirements), and
* the total size requirements of the set of components
* (only the alignment field of which is actually used).
* You can get the total size requirement by invoking
* getAlignedSizeRequirements.
*
* Normal alignment will be done with an alignment value of 0.0f
* representing the left/top edge of a component.
*
* @param allocated the total span to be allocated >= 0.
* @param total the total of the children requests.
* @param children the size requirements for each component.
* @param offsets the offset from 0 for each child where
* the spans were allocated (determines placement of the span).
* @param spans the span allocated for each child to make the
* total target span.
*/
public static void
calculateAlignedPositions(int
allocated,
SizeRequirements total,
SizeRequirements[]
children,
int[]
offsets,
int[]
spans) {
calculateAlignedPositions(
allocated,
total,
children,
offsets,
spans, true );
}
/**
* Creates a set of offset/span pairs specifying how to
* lay out a set of components with the specified alignments.
* The resulting span allocations will overlap, with each one
* fitting as well as possible into the given total allocation.
* This method requires that you specify
* the total amount of space to be allocated,
* the size requirements for each component to be placed
* (specified as an array of SizeRequirements), and
* the total size requirements of the set of components
* (only the alignment field of which is actually used)
* You can get the total size requirement by invoking
* getAlignedSizeRequirements.
*
* This method also requires a flag indicating whether normal or
* reverse alignment should be performed. With normal alignment
* the value 0.0f represents the left/top edge of the component
* to be aligned. With reverse alignment, 0.0f represents the
* right/bottom edge.
*
* @param allocated the total span to be allocated >= 0.
* @param total the total of the children requests.
* @param children the size requirements for each component.
* @param offsets the offset from 0 for each child where
* the spans were allocated (determines placement of the span).
* @param spans the span allocated for each child to make the
* total target span.
* @param normal when true, the alignment value 0.0f means
* left/top; when false, it means right/bottom.
* @since 1.4
*/
public static void
calculateAlignedPositions(int
allocated,
SizeRequirements total,
SizeRequirements[]
children,
int[]
offsets,
int[]
spans,
boolean
normal) {
float
totalAlignment =
normal ?
total.
alignment : 1.0f -
total.
alignment;
int
totalAscent = (int)(
allocated *
totalAlignment);
int
totalDescent =
allocated -
totalAscent;
for (int
i = 0;
i <
children.length;
i++) {
SizeRequirements req =
children[
i];
float
alignment =
normal ?
req.
alignment : 1.0f -
req.
alignment;
int
maxAscent = (int)(
req.
maximum *
alignment);
int
maxDescent =
req.
maximum -
maxAscent;
int
ascent =
Math.
min(
totalAscent,
maxAscent);
int
descent =
Math.
min(
totalDescent,
maxDescent);
offsets[
i] =
totalAscent -
ascent;
spans[
i] = (int)
Math.
min((long)
ascent + (long)
descent,
Integer.
MAX_VALUE);
}
}
// This method was used by the JTable - which now uses a different technique.
/**
* Adjust a specified array of sizes by a given amount.
*
* @param delta an int specifying the size difference
* @param children an array of SizeRequirements objects
* @return an array of ints containing the final size for each item
*/
public static int[]
adjustSizes(int
delta,
SizeRequirements[]
children) {
return new int[0];
}
}