./swing-layout-1.0.4/ 0000755 0001750 0001750 00000000000 11330040023 013317 5 ustar yn158264 yn158264 ./swing-layout-1.0.4/nbproject/ 0000755 0001750 0001750 00000000000 11271524336 015327 5 ustar yn158264 yn158264 ./swing-layout-1.0.4/nbproject/project.properties 0000644 0001750 0001750 00000003731 11271604670 021117 0 ustar yn158264 yn158264 application.args=
application.title=swing-layout
application.vendor=jstola
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/swing-layout.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.src-java=src/java
file.reference.src-test=src/test
includes=**
jar.compress=false
java.dir=${file.reference.src-java}
javac.classpath=
# Space-separated list of extra javac options
javac.compilerargs=-g:lines
javac.deprecation=false
javac.source=1.4
javac.target=1.4
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}:\
${libs.junit.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
main.class=
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
# or test-sys-prop.name=value to set system properties for unit tests):
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
test.test.dir=${file.reference.src-test}
./swing-layout-1.0.4/nbproject/project.xml 0000644 0001750 0001750 00000001012 11271604246 017510 0 ustar yn158264 yn158264
LayoutStyle
for Gnome. This information
* comes from:
* http://developer.gnome.org/projects/gup/hig/2.0/design-window.html#window-layout-spacing
*
* @version $Revision: 1.7 $
*/
class GnomeLayoutStyle extends LayoutStyle {
public int getPreferredGap(JComponent source, JComponent target,
int type, int position, Container parent) {
// Invoke super to check arguments.
super.getPreferredGap(source, target, type, position, parent);
if (type == INDENT) {
if (position == SwingConstants.EAST || position == SwingConstants.WEST) {
int gap = getButtonChildIndent(source, position);
if (gap != 0) {
return gap;
}
// Indent group members 12 pixels to denote hierarchy and
// association.
return 12;
}
// Treat vertical INDENT as RELATED
type = RELATED;
}
// Between labels and associated components, leave 12 horizontal
// pixels.
if (position == SwingConstants.EAST ||
position == SwingConstants.WEST) {
boolean sourceLabel = (source.getUIClassID() == "LabelUI");
boolean targetLabel = (target.getUIClassID() == "LabelUI");
if ((sourceLabel && !targetLabel) ||
(!sourceLabel && targetLabel)) {
return 12;
}
}
// As a basic rule of thumb, leave space between user
// interface components in increments of 6 pixels, going up as
// the relationship between related elements becomes more
// distant. For example, between icon labels and associated
// graphics within an icon, 6 pixels are adequate. Between
// labels and associated components, leave 12 horizontal
// pixels. For vertical spacing between groups of components,
// 18 pixels is adequate.
//
// The first part of this is handled automatically by Icon (which
// won't give you 6 pixels).
if (type == RELATED) {
return 6;
}
return 12;
}
public int getContainerGap(JComponent component, int position,
Container parent) {
super.getContainerGap(component, position, parent);
// A general padding of 12 pixels is
// recommended between the contents of a dialog window and the
// window borders.
//
// Indent group members 12 pixels to denote hierarchy and association.
return 12;
}
}
./swing-layout-1.0.4/src/java/org/jdesktop/layout/GroupLayout.java 0000644 0001750 0001750 00000407237 10537546474 024165 0 ustar yn158264 yn158264 /*
* Copyright (C) 2005-2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.layout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import javax.swing.SwingConstants;
import javax.swing.JComponent;
import java.util.*;
/**
* GroupLayout is a LayoutManager that hierarchically groups components to
* achieve common, and not so common, layouts. Grouping is done by instances
* of the Group class. GroupLayout supports two types of groups:
*
Sequential: | A sequential group positions its child * elements sequentially, one after another. * |
Parallel: | A parallel group positions its child * elements in the same space on top of each other. Parallel groups * can also align the child elements along their baseline. * |
* The following code builds a simple layout consisting of two labels in * one column, followed by two textfields in the next column: *
* JComponent panel = ...; * GroupLayout layout = new GroupLayout(panel); * panel.setLayout(layout); * layout.setAutocreateGaps(true); * layout.setAutocreateContainerGaps(true); * GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); * hGroup.add(layout.createParallelGroup().add(label1).add(label2)). * add(layout.createParallelGroup().add(tf1).add(tf2)); * layout.setHorizontalGroup(hGroup); * GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); * vGroup.add(layout.createParallelGroup(GroupLayout.BASELINE).add(label1).add(tf1)). * add(layout.createParallelGroup(GroupLayout.BASELINE).add(label2).add(tf2)); * layout.setVerticalGroup(vGroup); **
* This layout consists of the following: *
add
methods.
* add
methods of Groups
return
* themselves. This allows for easy chaining of invocations. For
* example, group.add(label1).add(label2);
is equivalent to
* group.add(label1);group.add(label2);
.
* GroupLayout
.
* setAutocreateGaps()
method. Similarly you can use
* the setAutocreateContainerGaps()
method to insert gaps
* between the components and the container.
*
* @version $Revision: 1.25 $
* @author Tomas Pavek
* @author Jan Stola
* @author Scott Violet
*/
public class GroupLayout implements LayoutManager2 {
// Used in size calculations
private static final int MIN_SIZE = 0;
private static final int PREF_SIZE = 1;
private static final int MAX_SIZE = 2;
// Used by prepare, indicates min, pref or max isn't going to be used.
private static final int SPECIFIC_SIZE = 3;
private static final int UNSET = Integer.MIN_VALUE;
/**
* Possible argument when linking sizes of components. Specifies the
* the two component should share the same size along the horizontal
* axis.
*
* @see #linkSize(java.awt.Component[],int)
*/
public static final int HORIZONTAL = 1;
/**
* Possible argument when linking sizes of components. Specifies the
* the two component should share the same size along the vertical
* axis.
*
* @see #linkSize(java.awt.Component[],int)
*/
public static final int VERTICAL = 2;
private static final int NO_ALIGNMENT = 0;
/**
* Possible alignment type. Indicates the elements should be
* aligned to the origin. For the horizontal axis with a left to
* right orientation this means aligned to the left.
*
* @see #createParallelGroup(int)
*/
public static final int LEADING = 1;
/**
* Possible alignment type. Indicates the elements should be
* aligned to the end. For the horizontal axis with a left to
* right orientation this means aligned to the right.
*
* @see #createParallelGroup(int)
*/
public static final int TRAILING = 2;
/**
* Possible alignment type. Indicates the elements should centered in
* the spaced provided.
*
* @see #createParallelGroup(int)
*/
public static final int CENTER = 4;
/**
* Possible alignment type. Indicates the elements should aligned along
* their baseline.
*
* @see #createParallelGroup(int)
*/
public static final int BASELINE = 3;
/**
* Possible value for the add methods that takes a Component.
* Indicates the size from the component should be used.
*/
public static final int DEFAULT_SIZE = -1;
/**
* Possible value for the add methods that takes a Component.
* Indicates the preferred size should be used.
*/
public static final int PREFERRED_SIZE = -2;
// Whether or not we automatically try and create the preferred
// padding between components.
private boolean autocreatePadding;
// Whether or not we automatically try and create the preferred
// padding between containers
private boolean autocreateContainerPadding;
/**
* Group responsible for layout along the horizontal axis. This is NOT
* the user specified group, use getHorizontalGroup to dig that out.
*/
private Group horizontalGroup;
/**
* Group responsible for layout along the vertical axis. This is NOT
* the user specified group, use getVerticalGroup to dig that out.
*/
private Group verticalGroup;
// Maps from Component to ComponentInfo. This is used for tracking
// information specific to a Component.
private Map componentInfos;
// Container we're doing layout for.
private Container host;
// Used by areParallelSiblings, cached to avoid excessive garbage.
private Set tmpParallelSet;
// Indicates Springs have changed in some way since last change.
private boolean springsChanged;
// Indicates invalidateLayout has been invoked.
private boolean isValid;
// Whether or not any preferred padding (or container padding) springs exist
private boolean hasPreferredPaddingSprings;
/**
* The LayoutStyle instance to use, if null the sharedInstance is used.
*/
private LayoutStyle layoutStyle;
/**
* If true, components that are not visible are treated as though they
* aren't there.
*/
private boolean honorsVisibility;
private static void checkSize(int min, int pref, int max,
boolean isComponentSpring) {
checkResizeType(min, isComponentSpring);
if (!isComponentSpring && pref < 0) {
throw new IllegalArgumentException("Pref must be >= 0");
} else if (isComponentSpring) {
checkResizeType(pref, true);
}
checkResizeType(max, isComponentSpring);
checkLessThan(min, pref);
checkLessThan(pref, max);
}
private static void checkResizeType(int type, boolean isComponentSpring) {
if (type < 0 && ((isComponentSpring && type != DEFAULT_SIZE &&
type != PREFERRED_SIZE) ||
(!isComponentSpring && type != PREFERRED_SIZE))) {
throw new IllegalArgumentException("Invalid size");
}
}
private static void checkLessThan(int min, int max) {
if (min >= 0 && max >= 0 && min > max) {
throw new IllegalArgumentException(
"Following is not met: min<=pref<=max");
}
}
/**
* Creates a GroupLayout for the specified JComponent.
*
* @param host the Container to layout
* @throws IllegalArgumentException if host is null
*/
public GroupLayout(Container host) {
if (host == null) {
throw new IllegalArgumentException("Container must be non-null");
}
honorsVisibility = true;
this.host = host;
setHorizontalGroup(createParallelGroup(LEADING, true));
setVerticalGroup(createParallelGroup(LEADING, true));
componentInfos = new HashMap();
tmpParallelSet = new HashSet();
}
/**
* Sets whether component visiblity is considered when sizing and
* positioning components. A value of true
indicates that
* non-visible components should not be treated as part of the
* layout. A value of false
indicates that components should be
* positioned and sized regardless of visibility.
*
* A value of false
is useful when the visibility of components
* is dynamically adjusted and you don't want surrounding components and
* the sizing to change.
*
* The specified value is used for components that do not have an * explicit visibility specified. *
* The default is true
.
*
* @param honorsVisibility whether component visiblity is considered when
* sizing and positioning components
* @see #setHonorsVisibility(Component,Boolean)
*/
public void setHonorsVisibility(boolean honorsVisibility) {
if (this.honorsVisibility != honorsVisibility) {
this.honorsVisibility = honorsVisibility;
springsChanged = true;
isValid = false;
invalidateHost();
}
}
/**
* Returns whether component visiblity is considered when sizing and
* positioning components.
*
* @return whether component visiblity is considered when sizing and
* positioning components
*/
public boolean getHonorsVisibility() {
return honorsVisibility;
}
/**
* Sets whether the component's visiblity is considered for
* sizing and positioning. A value of Boolean.TRUE
* indicates that if component
is not visible it should
* not be treated as part of the layout. A value of false
* indicates that component
is positioned and sized
* regardless of it's visibility. A value of null
* indicates the value specified by the single argument method
* setHonorsVisibility
should be used.
*
* If component
is not a child of the Container
this
* GroupLayout
is managing, it will be added to the
* Container
.
*
* @param component the component
* @param honorsVisibility whether component
's visiblity should be
* considered for sizing and positioning
* @throws IllegalArgumentException if component
is null
* @see #setHonorsVisibility(boolean)
*/
public void setHonorsVisibility(Component component,
Boolean honorsVisibility) {
if (component == null) {
throw new IllegalArgumentException("Component must be non-null");
}
getComponentInfo(component).setHonorsVisibility(honorsVisibility);
springsChanged = true;
isValid = false;
invalidateHost();
}
/**
* Returns a textual description of this GroupLayout. The return value
* is intended for debugging purposes only.
*
* @return textual description of this GroupLayout
**/
public String toString() {
if (springsChanged) {
registerComponents(horizontalGroup, HORIZONTAL);
registerComponents(verticalGroup, VERTICAL);
}
StringBuffer buffer = new StringBuffer();
buffer.append("HORIZONTAL\n");
dump(buffer, horizontalGroup, " ", HORIZONTAL);
buffer.append("\nVERTICAL\n");
dump(buffer, verticalGroup, " ", VERTICAL);
return buffer.toString();
}
private void dump(StringBuffer buffer, Spring spring, String indent,
int axis) {
String origin = "";
String padding = "";
if (spring instanceof ComponentSpring) {
ComponentSpring cSpring = (ComponentSpring)spring;
origin = Integer.toString(cSpring.getOrigin()) + " ";
String name = cSpring.getComponent().getName();
if (name != null) {
origin = "name=" + name + ", ";
}
}
if (spring instanceof AutopaddingSpring) {
AutopaddingSpring paddingSpring = (AutopaddingSpring)spring;
padding = ", userCreated=" + paddingSpring.getUserCreated() +
", matches=" + paddingSpring.getMatchDescription();
}
buffer.append(indent + spring.getClass().getName() + " " +
Integer.toHexString(spring.hashCode()) + " " +
origin +
", size=" + spring.getSize() +
", alignment=" + spring.getAlignment() +
" prefs=[" + spring.getMinimumSize(axis) +
" " + spring.getPreferredSize(axis) +
" " + spring.getMaximumSize(axis) +
padding + "]\n");
if (spring instanceof Group) {
List springs = ((Group)spring).springs;
indent += " ";
for (int counter = 0; counter < springs.size(); counter++) {
dump(buffer, (Spring)springs.get(counter), indent, axis);
}
}
}
/**
* Sets whether or not a gap between components
* should automatically be created. For example, if this is true
* and you add two components to a SequentialGroup
a
* gap between the two will automatically be created. The default
* is false.
*
* @param autocreatePadding whether or not to automatically created a gap
* between components and the container
*/
public void setAutocreateGaps(boolean autocreatePadding) {
if (this.autocreatePadding != autocreatePadding) {
this.autocreatePadding = autocreatePadding;
invalidateHost();
}
}
/**
* Returns true if gaps between components are automatically be created.
*
* @return true if gaps between components should automatically be created
*/
public boolean getAutocreateGaps() {
return autocreatePadding;
}
/**
* Sets whether or not gaps between the container and the first/last
* components should automatically be created. The default
* is false.
*
* @param autocreatePadding whether or not to automatically create
* gaps between the container and first/last components.
*/
public void setAutocreateContainerGaps(boolean autocreatePadding) {
if (autocreatePadding != autocreateContainerPadding) {
autocreateContainerPadding = autocreatePadding;
horizontalGroup = createTopLevelGroup(getHorizontalGroup());
verticalGroup = createTopLevelGroup(getVerticalGroup());
invalidateHost();
}
}
/**
* Returns whether or not gaps between the container and the
* first/last components should automatically be created. The default
* is false.
*
* @return whether or not the gaps between the container and the
* first/last components should automatically be created
*/
public boolean getAutocreateContainerGaps() {
return autocreateContainerPadding;
}
/**
* Sets the Group
that is responsible for
* layout along the horizontal axis.
*
* @param group Group
responsible for layout along
* the horizontal axis
* @throws IllegalArgumentException if group is null
*/
public void setHorizontalGroup(Group group) {
if (group == null) {
throw new IllegalArgumentException("Group must be non-null");
}
horizontalGroup = createTopLevelGroup(group);
invalidateHost();
}
/**
* Returns the Group
that is responsible for
* layout along the horizontal axis.
*
* @return ParallelGroup
responsible for layout along
* the horizontal axis.
*/
public Group getHorizontalGroup() {
int index = 0;
if (horizontalGroup.springs.size() > 1) {
index = 1;
}
return (Group)horizontalGroup.springs.get(index);
}
/**
* Sets the Group
that is responsible for
* layout along the vertical axis.
*
* @param group Group
responsible for layout along
* the vertical axis.
* @throws IllegalArgumentException if group is null.
*/
public void setVerticalGroup(Group group) {
if (group == null) {
throw new IllegalArgumentException("Group must be non-null");
}
verticalGroup = createTopLevelGroup(group);
invalidateHost();
}
/**
* Returns the ParallelGroup
that is responsible for
* layout along the vertical axis.
*
* @return ParallelGroup
responsible for layout along
* the vertical axis.
*/
public Group getVerticalGroup() {
int index = 0;
if (verticalGroup.springs.size() > 1) {
index = 1;
}
return (Group)verticalGroup.springs.get(index);
}
/**
* Wraps the user specified group in a sequential group. If
* container gaps should be generate the necessary springs are
* added.
*/
private Group createTopLevelGroup(Group specifiedGroup) {
SequentialGroup group = createSequentialGroup();
if (getAutocreateContainerGaps()) {
group.addSpring(new ContainerAutopaddingSpring());
group.add(specifiedGroup);
group.addSpring(new ContainerAutopaddingSpring());
} else {
group.add(specifiedGroup);
}
return group;
}
/**
* Creates and returns a SequentialGroup
.
*
* @return a new SequentialGroup
*/
public SequentialGroup createSequentialGroup() {
return new SequentialGroup();
}
/**
* Creates and returns a ParallelGroup
with a
* LEADING
alignment. This is a cover method for the more
* general createParallelGroup(int)
method.
*
* @return a new ParallelGroup
* @see #createParallelGroup(int)
*/
public ParallelGroup createParallelGroup() {
return createParallelGroup(LEADING);
}
/**
* Creates and returns an ParallelGroup
. The alignment
* specifies how children elements should be positioned when the
* the parallel group is given more space than necessary. For example,
* if a ParallelGroup with an alignment of TRAILING is given 100 pixels
* and a child only needs 50 pixels, the child will be positioned at the
* position 50.
*
* @param alignment alignment for the elements of the Group, one
* of LEADING
, TRAILING
,
* CENTER
or BASELINE
.
* @throws IllegalArgumentException if alignment is not one of
* LEADING
, TRAILING
,
* CENTER
or BASELINE
* @return a new ParallelGroup
*/
public ParallelGroup createParallelGroup(int alignment) {
return createParallelGroup(alignment, true);
}
/**
* Creates and returns an ParallelGroup
. The alignment
* specifies how children elements should be positioned when the
* the parallel group is given more space than necessary. For example,
* if a ParallelGroup with an alignment of TRAILING is given 100 pixels
* and a child only needs 50 pixels, the child will be positioned at the
* position 50.
*
* @param alignment alignment for the elements of the Group, one
* of LEADING
, TRAILING
,
* CENTER
or BASELINE
.
* @param resizable whether or not the group is resizable. If the group
* is not resizable the min/max size will be the same as the
* preferred.
* @throws IllegalArgumentException if alignment is not one of
* LEADING
, TRAILING
,
* CENTER
or BASELINE
* @return a new ParallelGroup
*/
public ParallelGroup createParallelGroup(int alignment, boolean resizable) {
if (alignment == BASELINE) {
return new BaselineGroup(resizable);
}
return new ParallelGroup(alignment, resizable);
}
/**
* Creates and returns a ParallelGroup
that aligns it's
* elements along the baseline.
*
* @param resizable whether the group is resizable
* @param anchorBaselineToTop whether the baseline is anchored to
* the top or bottom of the group
* @see #createBaselineGroup
* @see ParallelGroup
*/
public ParallelGroup createBaselineGroup(boolean resizable,
boolean anchorBaselineToTop) {
return new BaselineGroup(resizable, anchorBaselineToTop);
}
/**
* Forces the set of components to have the same size.
* This can be used multiple times to force
* any number of components to share the same size.
*
* Linked Components are not be resizable.
*
* @param components Components to force to have same size.
* @throws IllegalArgumentException if components
is
* null, or contains null.
*/
public void linkSize(Component[] components) {
linkSize(components, HORIZONTAL | VERTICAL);
}
/**
* Forces the set of components to have the same size.
* This can be used multiple times to force
* any number of components to share the same size.
*
* Linked Components are not be resizable.
*
* @param components Components to force to have same size.
* @param axis Axis to bind size, one of HORIZONTAL, VERTICAL or
* HORIZONTAL | VERTICAL
* @throws IllegalArgumentException if components
is
* null, or contains null.
* @throws IllegalArgumentException if axis
does not
* contain HORIZONTAL
or VERTICAL
*/
public void linkSize(Component[] components, int axis) {
if (components == null) {
throw new IllegalArgumentException("Components must be non-null");
}
boolean horizontal = ((axis & HORIZONTAL) == HORIZONTAL);
boolean vertical = ((axis & VERTICAL) == VERTICAL);
if (!vertical && !horizontal) {
throw new IllegalArgumentException(
"Axis must contain HORIZONTAL or VERTICAL");
}
for (int counter = components.length - 1; counter >= 0; counter--) {
Component c = components[counter];
if (components[counter] == null) {
throw new IllegalArgumentException(
"Components must be non-null");
}
// Force the component to be added
getComponentInfo(c);
}
if (horizontal) {
linkSize0(components, HORIZONTAL);
}
if (vertical) {
linkSize0(components, VERTICAL);
}
invalidateHost();
}
private void linkSize0(Component[] components, int axis) {
LinkInfo master = getComponentInfo(
components[components.length - 1]).getLinkInfo(axis);
for (int counter = components.length - 2; counter >= 0; counter--) {
master.add(getComponentInfo(components[counter]));
}
}
/**
* Removes an existing component replacing it with the specified component.
*
* @param existingComponent the Component that should be removed and
* replaced with newComponent
* @param newComponent the Component to put in existingComponents place
* @throws IllegalArgumentException is either of the Components are null or
* if existingComponent is not being managed by this layout manager
*/
public void replace(Component existingComponent, Component newComponent) {
if (existingComponent == null || newComponent == null) {
throw new IllegalArgumentException("Components must be non-null");
}
// Make sure all the components have been registered, otherwise we may
// not update the correct Springs.
if (springsChanged) {
registerComponents(horizontalGroup, HORIZONTAL);
registerComponents(verticalGroup, VERTICAL);
}
ComponentInfo info = (ComponentInfo)componentInfos.
remove(existingComponent);
if (info == null) {
throw new IllegalArgumentException("Component must already exist");
}
host.remove(existingComponent);
if (newComponent.getParent() != host) {
host.add(newComponent);
}
info.setComponent(newComponent);
componentInfos.put(newComponent, info);
invalidateHost();
}
/**
* Sets the LayoutStyle this GroupLayout is to use. A value of null can
* be used to indicate the shared instance of LayoutStyle should be used.
*
* @param layoutStyle the LayoutStyle to use
*/
public void setLayoutStyle(LayoutStyle layoutStyle) {
this.layoutStyle = layoutStyle;
invalidateHost();
}
/**
* Returns the LayoutStyle instance to use
*
* @return the LayoutStyle instance to use
*/
public LayoutStyle getLayoutStyle() {
return layoutStyle;
}
private LayoutStyle getLayoutStyle0() {
LayoutStyle layoutStyle = getLayoutStyle();
if (layoutStyle == null) {
layoutStyle = LayoutStyle.getSharedInstance();
}
return layoutStyle;
}
private void invalidateHost() {
if (host instanceof JComponent) {
((JComponent)host).revalidate();
} else {
host.invalidate();
}
host.repaint();
}
//
// LayoutManager
//
/**
* Notification that a Component
has been added to
* the parent container. Developers should not invoke this method
* directly, instead you should use one of the Group
* methods to add a Component
.
*
* @param name the string to be associated with the component
* @param component the Component
to be added
*/
public void addLayoutComponent(String name, Component component) {
}
/**
* Notification that a Component
has been removed from
* the parent container. You should not invoke this method
* directly, instead invoke remove
on the parent
* Container
.
*
* @param component the component to be removed
* @see java.awt.Component#remove
*/
public void removeLayoutComponent(Component component) {
ComponentInfo info = (ComponentInfo)componentInfos.remove(component);
if (info != null) {
info.dispose();
springsChanged = true;
isValid = false;
}
}
/**
* Returns the preferred size for the specified container.
*
* @param parent the container to return size for
* @throws IllegalArgumentException if parent
is not
* the same Container
that this was created with
* @throws IllegalStateException if any of the components added to
* this layout are not in both a horizontal and vertical group
* @see java.awt.Container#getPreferredSize
*/
public Dimension preferredLayoutSize(Container parent) {
checkParent(parent);
prepare(PREF_SIZE);
return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL),
verticalGroup.getPreferredSize(VERTICAL));
}
/**
* Returns the minimum size for the specified container.
*
* @param parent the container to return size for
* @throws IllegalArgumentException if parent
is not
* the same Container
that this was created with
* @throws IllegalStateException if any of the components added to
* this layout are not in both a horizontal and vertical group
* @see java.awt.Container#getMinimumSize
*/
public Dimension minimumLayoutSize(Container parent) {
checkParent(parent);
prepare(MIN_SIZE);
return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL),
verticalGroup.getMinimumSize(VERTICAL));
}
/**
* Lays out the specified container.
*
* @param parent the container to be laid out
* @throws IllegalStateException if any of the components added to
* this layout are not in both a horizontal and vertical group
*/
public void layoutContainer(Container parent) {
// Step 1: Prepare for layout.
prepare(SPECIFIC_SIZE);
Insets insets = parent.getInsets();
int width = parent.getWidth() - insets.left - insets.right;
int height = parent.getHeight() - insets.top - insets.bottom;
boolean ltr = isLeftToRight();
if (getAutocreateGaps() || getAutocreateContainerGaps() ||
hasPreferredPaddingSprings) {
// Step 2: Calculate autopadding springs
calculateAutopadding(horizontalGroup, HORIZONTAL, SPECIFIC_SIZE, 0,
width);
calculateAutopadding(verticalGroup, VERTICAL, SPECIFIC_SIZE, 0,
height);
}
// Step 3: set the size of the groups.
horizontalGroup.setSize(HORIZONTAL, 0, width);
verticalGroup.setSize(VERTICAL, 0, height);
// Step 4: apply the size to the components.
Iterator componentInfo = componentInfos.values().iterator();
while (componentInfo.hasNext()) {
ComponentInfo info = (ComponentInfo)componentInfo.next();
Component c = info.getComponent();
info.setBounds(insets, width, ltr);
}
}
//
// LayoutManager2
//
/**
* Notification that a Component
has been added to
* the parent container. You should not invoke this method
* directly, instead you should use one of the Group
* methods to add a Component
.
*
* @param component The component added
* @param constraints Description of where to place the component.
*/
public void addLayoutComponent(Component component, Object constraints) {
}
/**
* Returns the maximum size for the specified container.
*
* @param parent the container to return size for
* @throws IllegalArgumentException if parent
is not
* the same Container
that this was created with
* @throws IllegalStateException if any of the components added to
* this layout are not in both a horizontal and vertical group
* @see java.awt.Container#getMaximumSize
*/
public Dimension maximumLayoutSize(Container parent) {
checkParent(parent);
prepare(MAX_SIZE);
return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL),
verticalGroup.getMaximumSize(VERTICAL));
}
/**
* Returns the alignment along the x axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*
* @param parent Container hosting this LayoutManager
* @throws IllegalArgumentException if parent
is not
* the same Container
that this was created with
* @return alignment
*/
public float getLayoutAlignmentX(Container parent) {
checkParent(parent);
return .5f;
}
/**
* Returns the alignment along the y axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*
* @param parent Container hosting this LayoutManager
* @throws IllegalArgumentException if parent
is not
* the same Container
that this was created with
* @return alignment
*/
public float getLayoutAlignmentY(Container parent) {
checkParent(parent);
return .5f;
}
/**
* Invalidates the layout, indicating that if the layout manager
* has cached information it should be discarded.
*
* @param parent Container hosting this LayoutManager
* @throws IllegalArgumentException if parent
is not
* the same Container
that this was created with
*/
public void invalidateLayout(Container parent) {
checkParent(parent);
// invalidateLayout is called from Container.invalidate, which
// does NOT grab the treelock. All other methods do. To make sure
// there aren't any possible threading problems we grab the tree lock
// here.
synchronized(parent.getTreeLock()) {
isValid = false;
}
}
private void prepare(int sizeType) {
boolean visChanged = false;
// Step 1: If not-valid, clear springs and update visibility.
if (!isValid) {
isValid = true;
horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET);
verticalGroup.setSize(VERTICAL, UNSET, UNSET);
for (Iterator cis = componentInfos.values().iterator();
cis.hasNext();) {
ComponentInfo ci = (ComponentInfo)cis.next();
if (ci.updateVisibility()) {
visChanged = true;
}
ci.clearCachedSize();
}
}
// Step 2: Make sure components are bound to ComponentInfos
if (springsChanged) {
registerComponents(horizontalGroup, HORIZONTAL);
registerComponents(verticalGroup, VERTICAL);
}
// Step 3: Adjust the autopadding. This removes existing
// autopadding, then recalculates where it should go.
if (springsChanged || visChanged) {
checkComponents();
horizontalGroup.removeAutopadding();
verticalGroup.removeAutopadding();
if (getAutocreateGaps()) {
insertAutopadding(true);
} else if (hasPreferredPaddingSprings ||
getAutocreateContainerGaps()) {
insertAutopadding(false);
}
springsChanged = false;
}
// Step 4: (for min/pref/max size calculations only) calculate the
// autopadding. This invokes for unsetting the calculated values, then
// recalculating them.
// If sizeType == SPECIFIC_SIZE, it indicates we're doing layout, this
// step will be done later on.
if (sizeType != SPECIFIC_SIZE && (getAutocreateGaps() ||
getAutocreateContainerGaps() || hasPreferredPaddingSprings)) {
calculateAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0, 0);
calculateAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0);
}
}
private void calculateAutopadding(Group group, int axis, int sizeType,
int origin, int size) {
group.unsetAutopadding();
switch(sizeType) {
case MIN_SIZE:
size = group.getMinimumSize(axis);
break;
case PREF_SIZE:
size = group.getPreferredSize(axis);
break;
case MAX_SIZE:
size = group.getMaximumSize(axis);
break;
}
group.setSize(axis, origin, size);
group.calculateAutopadding(axis);
}
private void checkComponents() {
Iterator infos = componentInfos.values().iterator();
while (infos.hasNext()) {
ComponentInfo info = (ComponentInfo)infos.next();
if (info.horizontalSpring == null) {
throw new IllegalStateException(info.component +
" is not attached to a horizontal group");
}
if (info.verticalSpring == null) {
throw new IllegalStateException(info.component +
" is not attached to a vertical group");
}
}
}
private void registerComponents(Group group, int axis) {
List springs = group.springs;
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = (Spring)springs.get(counter);
if (spring instanceof ComponentSpring) {
((ComponentSpring)spring).installIfNecessary(axis);
} else if (spring instanceof Group) {
registerComponents((Group)spring, axis);
}
}
}
private Dimension adjustSize(int width, int height) {
Insets insets = host.getInsets();
return new Dimension(width + insets.left + insets.right,
height + insets.top + insets.bottom);
}
private void checkParent(Container parent) {
if (parent != host) {
throw new IllegalArgumentException(
"GroupLayout can only be used with one Container at a time");
}
}
/**
* Returns the ComponentInfo
for the specified Component.
*/
private ComponentInfo getComponentInfo(Component component) {
ComponentInfo info = (ComponentInfo)componentInfos.get(component);
if (info == null) {
info = new ComponentInfo(component);
componentInfos.put(component, info);
if (component.getParent() != host) {
host.add(component);
}
}
return info;
}
/**
* Adjusts the autopadding springs for the horizontal and vertical
* groups. If insert
is true this will insert auto padding
* springs, otherwise this will only adjust the springs that
* comprise auto preferred padding springs.
*/
private void insertAutopadding(boolean insert) {
horizontalGroup.insertAutopadding(HORIZONTAL, new ArrayList(1),
new ArrayList(1), new ArrayList(1), new ArrayList(1), insert);
verticalGroup.insertAutopadding(VERTICAL, new ArrayList(1),
new ArrayList(1), new ArrayList(1), new ArrayList(1), insert);
}
/**
* Returns true if the two Components have a common ParallelGroup ancestor
* along the particular axis.
*/
private boolean areParallelSiblings(Component source, Component target,
int axis) {
ComponentInfo sourceInfo = getComponentInfo(source);
ComponentInfo targetInfo = getComponentInfo(target);
Spring sourceSpring;
Spring targetSpring;
if (axis == HORIZONTAL) {
sourceSpring = sourceInfo.horizontalSpring;
targetSpring = targetInfo.horizontalSpring;
} else {
sourceSpring = sourceInfo.verticalSpring;
targetSpring = targetInfo.verticalSpring;
}
Set sourcePath = tmpParallelSet;
sourcePath.clear();
Spring spring = sourceSpring.getParent();
while (spring != null) {
sourcePath.add(spring);
spring = spring.getParent();
}
spring = targetSpring.getParent();
while (spring != null) {
if (sourcePath.contains(spring)) {
sourcePath.clear();
while (spring != null) {
if (spring instanceof ParallelGroup) {
return true;
}
spring = spring.getParent();
}
return false;
}
spring = spring.getParent();
}
sourcePath.clear();
return false;
}
private boolean isLeftToRight() {
return host.getComponentOrientation().isLeftToRight();
}
/**
* Spring consists of a range: min, pref and max a value some where in
* the middle of that and a location. Subclasses must override
* methods to get the min/max/pref and will likely want to override
* the setSize
method. Spring automatically caches the
* min/max/pref. If the min/pref/max has internally changes, or needs
* to be updated you must invoked clear.
*/
abstract class Spring {
private int size;
private int min;
private int max;
private int pref;
private Spring parent;
private int alignment;
Spring() {
min = pref = max = UNSET;
}
/**
* Calculates and returns the minimum size.
*
* @param axis the axis of layout; one of HORIZONTAL or VERTICAL
* @return the minimum size
*/
abstract int calculateMinimumSize(int axis);
/**
* Calculates and returns the preferred size.
*
* @param axis the axis of layout; one of HORIZONTAL or VERTICAL
* @return the preferred size
*/
abstract int calculatePreferredSize(int axis);
/**
* Calculates and returns the minimum size.
*
* @param axis the axis of layout; one of HORIZONTAL or VERTICAL
* @return the minimum size
*/
abstract int calculateMaximumSize(int axis);
/**
* Sets the parent of this Spring.
*/
void setParent(Spring parent) {
this.parent = parent;
}
/**
* Returns the parent of this spring.
*/
Spring getParent() {
return parent;
}
// This is here purely as a conveniance for ParallelGroup to avoid
// having to track alignment separately.
void setAlignment(int alignment) {
this.alignment = alignment;
}
int getAlignment() {
return alignment;
}
/**
* Returns the minimum size.
*/
final int getMinimumSize(int axis) {
if (min == UNSET) {
min = constrain(calculateMinimumSize(axis));
}
return min;
}
/**
* Returns the preferred size.
*/
final int getPreferredSize(int axis) {
if (pref == UNSET) {
pref = constrain(calculatePreferredSize(axis));
}
return pref;
}
/**
* Returns the maximum size.
*/
final int getMaximumSize(int axis) {
if (max == UNSET) {
max = constrain(calculateMaximumSize(axis));
}
return max;
}
/**
* Resets the cached min/max/pref.
*/
void unset() {
size = min = pref = max = UNSET;
}
/**
* Sets the value and location of the spring. Subclasses
* will want to invoke super, then do any additional sizing.
*
* @param axis HORIZONTAL or VERTICAL
* @param origin of this Spring
* @param size of the Spring. If size is UNSET, this invokes
* clear.
*/
void setSize(int axis, int origin, int size) {
this.size = size;
if (size == UNSET) {
unset();
}
}
/**
* Returns the current size.
*/
int getSize() {
return size;
}
int constrain(int value) {
return Math.min(value, Short.MAX_VALUE);
}
int getBaseline() {
return -1;
}
int getBaselineResizeBehavior() {
return Baseline.BRB_OTHER;
}
final boolean isResizable(int axis) {
int min = getMinimumSize(axis);
int pref = getPreferredSize(axis);
return (min != pref || pref != getMaximumSize(axis));
}
/**
* Returns true if this Spring will ALWAYS have a zero size. This should
* NOT check the current size, rather it's meant to
* quickly test if this Spring will always have a zero size.
*/
abstract boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized);
}
/**
* Group provides for commonality between the two types of operations
* supported by GroupLayout
: laying out components one
* after another (SequentialGroup
) or layout on top
* of each other (ParallelGroup
). Use one of
* createSequentialGroup
or
* createParallelGroup
to create one.
*/
public abstract class Group extends Spring {
// private int origin;
// private int size;
List springs;
Group() {
springs = new ArrayList();
}
int indexOf(Spring spring) {
return springs.indexOf(spring);
}
/**
* Adds the Spring to the list of Spring
s and returns
* the receiver.
*/
Group addSpring(Spring spring) {
springs.add(spring);
spring.setParent(this);
if (!(spring instanceof AutopaddingSpring) ||
!((AutopaddingSpring)spring).getUserCreated()) {
springsChanged = true;
}
return this;
}
//
// Spring methods
//
void setSize(int axis, int origin, int size) {
super.setSize(axis, origin, size);
if (size == UNSET) {
for (int counter = springs.size() - 1; counter >= 0;
counter--) {
getSpring(counter).setSize(axis, origin, size);
}
} else {
setValidSize(axis, origin, size);
}
}
/**
* This is invoked from setSize
if passed a value
* other than UNSET.
*/
abstract void setValidSize(int axis, int origin, int size);
int calculateMinimumSize(int axis) {
return calculateSize(axis, MIN_SIZE);
}
int calculatePreferredSize(int axis) {
return calculateSize(axis, PREF_SIZE);
}
int calculateMaximumSize(int axis) {
return calculateSize(axis, MAX_SIZE);
}
/**
* Used to compute how the two values representing two springs
* will be combined. For example, a group that layed things out
* one after the next would return a + b
.
*/
abstract int operator(int a, int b);
/**
* Calculates the specified size. This is called from
* one of the getMinimumSize0
,
* getPreferredSize0
or
* getMaximumSize0
methods. This will invoke
* to operator
to combine the values.
*/
int calculateSize(int axis, int type) {
int count = springs.size();
if (count == 0) {
return 0;
}
if (count == 1) {
return getSpringSize(getSpring(0), axis, type);
}
int size = constrain(operator(getSpringSize(getSpring(0), axis, type),
getSpringSize(getSpring(1), axis, type)));
for (int counter = 2; counter < count; counter++) {
size = constrain(operator(size, getSpringSize(getSpring(counter),
axis, type)));
}
return size;
}
Spring getSpring(int index) {
return (Spring)springs.get(index);
}
int getSpringSize(Spring spring, int axis, int type) {
switch(type) {
case MIN_SIZE:
return spring.getMinimumSize(axis);
case PREF_SIZE:
return spring.getPreferredSize(axis);
case MAX_SIZE:
return spring.getMaximumSize(axis);
}
assert false;
return 0;
}
// Padding
/**
* Adjusts the autopadding springs in this group and its children.
* If insert
is true this will insert auto padding
* springs, otherwise this will only adjust the springs that
* comprise auto preferred padding springs.
*
* @param axis the axis of the springs; HORIZONTAL or VERTICAL
* @param leadingPadding List of AutopaddingSprings that occur before
* this Group
* @param trailingPadding any trailing autopadding springs are added
* to this on exit
* @param leading List of ComponentSprings that occur before this Group
* @param trailing any trailing ComponentSpring are added to this
* List
* @param insert Whether or not to insert AutopaddingSprings or just
* adjust any existing AutopaddingSprings.
*/
abstract void insertAutopadding(int axis, List leadingPadding,
List trailingPadding, List leading, List trailing,
boolean insert);
/**
* Removes any AutopaddingSprings.
*/
void removeAutopadding() {
unset();
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = (Spring)springs.get(counter);
if (spring instanceof AutopaddingSpring) {
if (((AutopaddingSpring)spring).getUserCreated()) {
((AutopaddingSpring)spring).reset();
} else {
springs.remove(counter);
}
} else if (spring instanceof Group) {
((Group)spring).removeAutopadding();
}
}
}
void unsetAutopadding() {
// Clear cached pref/min/max.
unset();
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = (Spring)springs.get(counter);
if (spring instanceof AutopaddingSpring) {
((AutopaddingSpring)spring).unset();
} else if (spring instanceof Group) {
((Group)spring).unsetAutopadding();
}
}
}
void calculateAutopadding(int axis) {
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = (Spring)springs.get(counter);
if (spring instanceof AutopaddingSpring) {
// Force size to be reset.
spring.unset();
((AutopaddingSpring)spring).calculatePadding(axis);
} else if (spring instanceof Group) {
((Group)spring).calculateAutopadding(axis);
}
}
// Clear cached pref/min/max.
unset();
}
boolean willHaveZeroSize(boolean treatAutopaddingAsZeroSized) {
for (int i = springs.size() -1; i >= 0; i--) {
Spring spring = (Spring)springs.get(i);
if (!spring.willHaveZeroSize(treatAutopaddingAsZeroSized)) {
return false;
}
}
return true;
}
}
/**
* A Group
that lays out its elements sequentially, one
* after another. This class has no public constructor, use the
* createSequentialGroup
method to create one.
*
* @see #createSequentialGroup()
*/
public class SequentialGroup extends Group {
private Spring baselineSpring;
SequentialGroup() {
}
/**
* Adds the specified Group
to this
* SequentialGroup
*
* @param group the Group to add
* @return this Group
*/
public SequentialGroup add(Group group) {
return (SequentialGroup)addSpring(group);
}
/**
* Adds a Group
to this Group
.
*
* @param group the Group
to add
* @param useAsBaseline whether the specified Group
should
* be used to calculate the baseline for this Group
* @return this Group
*/
public SequentialGroup add(boolean useAsBaseline, Group group) {
add(group);
if (useAsBaseline) {
baselineSpring = group;
}
return this;
}
/**
* Adds the specified Component. If the Component's min/max
* are different from its pref than the component will be resizable.
*
* @param component the Component to add
* @return this SequentialGroup
*/
public SequentialGroup add(Component component) {
return add(component, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE);
}
/**
* Adds a Component
to this Group
.
*
* @param useAsBaseline whether the specified Component
should
* be used to calculate the baseline for this Group
* @param component the Component
to add
* @return this Group
*/
public SequentialGroup add(boolean useAsBaseline, Component component) {
add(component);
if (useAsBaseline) {
baselineSpring = getSpring(springs.size() - 1);
}
return this;
}
/**
* Adds the specified Component
. Min, pref and max
* can be absolute values, or they can be one of
* DEFAULT_SIZE
or PREFERRED_SIZE
. For
* example, the following:
*
* add(component, PREFERRED_SIZE, PREFERRED_SIZE, 1000); ** Forces a max of 1000, with the min and preferred equalling that * of the preferred size of
component
.
*
* @param component the Component to add
* @param min the minimum size
* @param pref the preferred size
* @param max the maximum size
* @throws IllegalArgumentException if min, pref or max are
* not positive and not one of PREFERRED_SIZE or DEFAULT_SIZE
* @return this SequentialGroup
*/
public SequentialGroup add(Component component, int min, int pref,
int max) {
return (SequentialGroup)addSpring(new ComponentSpring(
component, min, pref, max));
}
/**
* Adds a Component
to this Group
* with the specified size.
*
* @param useAsBaseline whether the specified Component
should
* be used to calculate the baseline for this Group
* @param component the Component
to add
* @param min the minimum size or one of DEFAULT_SIZE
or
* PREFERRED_SIZE
* @param pref the preferred size or one of DEFAULT_SIZE
or
* PREFERRED_SIZE
* @param max the maximum size or one of DEFAULT_SIZE
or
* PREFERRED_SIZE
* @return this Group
*/
public SequentialGroup add(boolean useAsBaseline,
Component component, int min, int pref, int max) {
add(component, min, pref, max);
if (useAsBaseline) {
baselineSpring = getSpring(springs.size() - 1);
}
return this;
}
/**
* Adds a rigid gap.
*
* @param pref the size of the gap
* @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
* or the following is not meant min <= pref <= max
* @return this SequentialGroup
*/
public SequentialGroup add(int pref) {
return add(pref, pref, pref);
}
/**
* Adds a gap with the specified size.
*
* @param min the minimum size of the gap, or PREFERRED_SIZE
* @param pref the preferred size of the gap
* @param max the maximum size of the gap, or PREFERRED_SIZE
* @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
* or the following is not meant min <= pref <= max
* @return this SequentialGroup
*/
public SequentialGroup add(int min, int pref, int max) {
return (SequentialGroup)addSpring(new GapSpring(min, pref, max));
}
/**
* Adds an element representing the preferred gap between the two
* components.
*
* @param comp1 the first component
* @param comp2 the second component
* @param type the type of gap; one of the constants defined by
* LayoutStyle
* @return this SequentialGroup
* @throws IllegalArgumentException if type
is not a
* valid LayoutStyle constant
* @see LayoutStyle
*/
public SequentialGroup addPreferredGap(JComponent comp1,
JComponent comp2,
int type) {
return addPreferredGap(comp1, comp2, type, false);
}
/**
* Adds an element representing the preferred gap between the two
* components.
*
* @param comp1 the first component
* @param comp2 the second component
* @param type the type of gap; one of the constants defined by
* LayoutStyle
* @param canGrow true if the gap can grow if more
* space is available
* @return this SequentialGroup
* @throws IllegalArgumentException if type
is not a
* valid LayoutStyle constant
* @see LayoutStyle
*/
public SequentialGroup addPreferredGap(JComponent comp1,
JComponent comp2,
int type, boolean canGrow) {
if (type != LayoutStyle.RELATED &&
type != LayoutStyle.UNRELATED &&
type != LayoutStyle.INDENT) {
throw new IllegalArgumentException("Invalid type argument");
}
if (comp1 == null || comp2 == null) {
throw new IllegalArgumentException(
"Components must be non-null");
}
return (SequentialGroup)addSpring(new PaddingSpring(
comp1, comp2, type, canGrow));
}
/**
* Adds an element representing the preferred gap between the
* nearest components. That is, during layout the neighboring
* components are found, and the min, pref and max of this
* element is set based on the preferred gap between the
* components. If no neighboring components are found the
* min, pref and max are set to 0.
*
* @param type the type of gap; one of the LayoutStyle constants
* @return this SequentialGroup
* @throws IllegalArgumentException if type is not one of
* LayoutStyle.RELATED
or
* LayoutStyle.UNRELATED
* @see LayoutStyle
*/
public SequentialGroup addPreferredGap(int type) {
return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE);
}
/**
* Adds an element for the preferred gap between the
* nearest components. That is, during layout the neighboring
* components are found, and the min of this
* element is set based on the preferred gap between the
* components. If no neighboring components are found the
* min is set to 0. This method allows you to specify the
* preferred and maximum size by way of the pref
* and max
arguments. These can either be a
* value >= 0, in which case the preferred or max is the max
* of the argument and the preferred gap, of DEFAULT_VALUE in
* which case the value is the same as the preferred gap.
*
* @param type the type of gap; one of LayoutStyle.RELATED or
* LayoutStyle.UNRELATED
* @param pref the preferred size; one of DEFAULT_SIZE or a value > 0
* @param max the maximum size; one of DEFAULT_SIZE, PREFERRED_SIZE
* or a value > 0
* @return this SequentialGroup
* @throws IllegalArgumentException if type is not one of
* LayoutStyle.RELATED
or
* LayoutStyle.UNRELATED
or pref/max is
* != DEFAULT_SIZE and < 0, or pref > max
* @see LayoutStyle
*/
public SequentialGroup addPreferredGap(int type, int pref,
int max) {
if (type != LayoutStyle.RELATED && type != LayoutStyle.UNRELATED) {
throw new IllegalArgumentException(
"Padding type must be one of Padding.RELATED or Padding.UNRELATED");
}
if ((pref < 0 && pref != DEFAULT_SIZE && pref != PREFERRED_SIZE) ||
(max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)||
(pref >= 0 && max >= 0 && pref > max)) {
throw new IllegalArgumentException(
"Pref and max must be either DEFAULT_SIZE, " +
"PREFERRED_SIZE, or >= 0 and pref <= max");
}
hasPreferredPaddingSprings = true;
return (SequentialGroup)addSpring(new AutopaddingSpring(
type, pref, max));
}
/**
* Adds an element representing the preferred gap between one edge
* of the container and the next/previous Component. This will have
* no effect if the next/previous element is not a Component and does
* not touch one edge of the parent container.
*
* @return this SequentialGroup
.
*/
public SequentialGroup addContainerGap() {
return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE);
}
/**
* Adds an element representing the preferred gap between one edge
* of the container and the next/previous Component. This will have
* no effect if the next/previous element is not a Component and does
* not touch one edge of the parent container.
*
* @param pref the preferred size; one of DEFAULT_SIZE or a value > 0
* @param max the maximum size; one of DEFAULT_SIZE, PREFERRED_SIZE
* or a value > 0.
* @throws IllegalArgumentException if pref/max is
* != DEFAULT_SIZE and < 0, or pref > max
* @return this SequentialGroup
*/
public SequentialGroup addContainerGap(int pref, int max) {
if ((pref < 0 && pref != DEFAULT_SIZE) ||
(max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE) ||
(pref >= 0 && max >= 0 && pref > max)) {
throw new IllegalArgumentException(
"Pref and max must be either DEFAULT_VALUE or >= 0 and pref <= max");
}
hasPreferredPaddingSprings = true;
return (SequentialGroup)addSpring(
new ContainerAutopaddingSpring(pref, max));
}
int operator(int a, int b) {
return constrain(a) + constrain(b);
}
void setValidSize(int axis, int origin, int size) {
int pref = getPreferredSize(axis);
if (size == pref) {
for (int counter = 0, max = springs.size(); counter < max;
counter++) {
Spring spring = getSpring(counter);
int springPref = spring.getPreferredSize(axis);
spring.setSize(axis, origin, springPref);
origin += springPref;
}
} else if (springs.size() == 1) {
Spring spring = getSpring(0);
spring.setSize(axis, origin, Math.min(
Math.max(size, spring.getMinimumSize(axis)),
spring.getMaximumSize(axis)));
} else if (springs.size() > 1) {
// Adjust between min/pref
setValidSizeNotPreferred(axis, origin, size);
}
}
private void setValidSizeNotPreferred(int axis, int origin, int size) {
int delta = size - getPreferredSize(axis);
assert delta != 0;
boolean useMin = (delta < 0);
int springCount = springs.size();
if (useMin) {
delta *= -1;
}
// The following algorithm if used for resizing springs:
// 1. Calculate the resizability of each spring (pref - min or
// max - pref) into a list.
// 2. Sort the list in ascending order
// 3. Iterate through each of the resizable Springs, attempting
// to give them (pref - size) / resizeCount
// 4. For any Springs that can not accomodate that much space
// add the remainder back to the amount to distribute and
// recalculate how must space the remaining springs will get.
// 5. Set the size of the springs.
// First pass, sort the resizable springs into resizable
List resizable = buildResizableList(axis, useMin);
int resizableCount = resizable.size();
if (resizableCount > 0) {
// How much we would like to give each Spring.
int sDelta = delta / resizableCount;
// Remaining space.
int slop = delta - sDelta * resizableCount;
int[] sizes = new int[springCount];
int sign = useMin ? -1 : 1;
// Second pass, accumulate the resulting deltas (relative to
// preferred) into sizes.
for (int counter = 0; counter < resizableCount; counter++) {
SpringDelta springDelta = (SpringDelta)resizable.
get(counter);
if ((counter + 1) == resizableCount) {
sDelta += slop;
}
springDelta.delta = Math.min(sDelta, springDelta.delta);
delta -= springDelta.delta;
if (springDelta.delta != sDelta && counter + 1 <
resizableCount) {
// Spring didn't take all the space, reset how much
// each spring will get.
sDelta = delta / (resizableCount - counter - 1);
slop = delta - sDelta * (resizableCount - counter - 1);
}
sizes[springDelta.index] = sign * springDelta.delta;
}
// And finally set the size of each spring
for (int counter = 0; counter < springCount; counter++) {
Spring spring = getSpring(counter);
int sSize = spring.getPreferredSize(axis) + sizes[counter];
spring.setSize(axis, origin, sSize);
origin += sSize;
}
} else {
// Nothing resizable, use the min or max of each of the
// springs.
for (int counter = 0; counter < springCount; counter++) {
Spring spring = getSpring(counter);
int sSize;
if (useMin) {
sSize = spring.getMinimumSize(axis);
} else {
sSize = spring.getMaximumSize(axis);
}
spring.setSize(axis, origin, sSize);
origin += sSize;
}
}
}
/**
* Returns the sorted list of SpringDelta's for the current set of
* Springs.
*/
private List buildResizableList(int axis, boolean useMin) {
// First pass, figure out what is resizable
int size = springs.size();
List sorted = new ArrayList(size);
for (int counter = 0; counter < size; counter++) {
Spring spring = getSpring(counter);
int sDelta;
if (useMin) {
sDelta = spring.getPreferredSize(axis) -
spring.getMinimumSize(axis);
} else {
sDelta = spring.getMaximumSize(axis) -
spring.getPreferredSize(axis);
}
if (sDelta > 0) {
sorted.add(new SpringDelta(counter, sDelta));
}
}
Collections.sort(sorted);
return sorted;
}
private int indexOfNextNonZeroSpring(int index, boolean treatAutopaddingAsZeroSized) {
while (index < springs.size()) {
Spring spring = (Spring)springs.get(index);
if (!((Spring)spring).willHaveZeroSize(treatAutopaddingAsZeroSized)) {
return index;
}
index++;
}
return index;
}
void insertAutopadding(int axis, List leadingPadding,
List trailingPadding, List leading, List trailing,
boolean insert) {
List newLeadingPadding = new ArrayList(leadingPadding);
List newTrailingPadding = new ArrayList(1);
List newLeading = new ArrayList(leading);
List newTrailing = null;
int counter = 0;
// Warning, this must use springs.size, as it may change during the
// loop.
while (counter < springs.size()) {
Spring spring = getSpring(counter);
if (spring instanceof AutopaddingSpring) {
if (newLeadingPadding.size() == 0) {
AutopaddingSpring padding = (AutopaddingSpring)spring;
padding.setSources(newLeading);
newLeading.clear();
int nextCounter = indexOfNextNonZeroSpring(counter + 1, true);
if (nextCounter == springs.size()) {
// Last spring in the list, add it to trailingPadding.
if (!(padding instanceof ContainerAutopaddingSpring)) {
trailingPadding.add(padding);
}
} else {
newLeadingPadding.clear();
newLeadingPadding.add(padding);
}
counter = nextCounter;
} else {
counter = indexOfNextNonZeroSpring(counter + 1, true);
}
} else {
// Not a padding spring
if (newLeading.size() > 0 && insert) {
// There's leading ComponentSprings, create an
// autopadding spring.
AutopaddingSpring padding = new AutopaddingSpring();
// Force the newly created spring to be considered
// by NOT incrementing counter
springs.add(counter, padding);
continue;
}
if (spring instanceof ComponentSpring) {
// Spring is a Component, make it the target of any
// leading AutopaddingSpring.
ComponentSpring cSpring = (ComponentSpring)spring;
if (!cSpring.isVisible()) {
counter++;
continue;
}
for (int i = 0; i < newLeadingPadding.size(); i++) {
((AutopaddingSpring)newLeadingPadding.get(i)).
addTarget(cSpring, axis);
}
newLeading.clear();
newLeadingPadding.clear();
int nextCounter = indexOfNextNonZeroSpring(counter + 1, false);
if (nextCounter == springs.size()) {
// Last Spring, add it to trailing
trailing.add(cSpring);
} else {
// Not that last Spring, add it to leading
newLeading.add(cSpring);
}
counter = nextCounter;
} else if (spring instanceof Group) {
// Forward call to child Group
if (newTrailing == null) {
newTrailing = new ArrayList(1);
} else {
newTrailing.clear();
}
newTrailingPadding.clear();
((Group)spring).insertAutopadding(axis, newLeadingPadding,
newTrailingPadding, newLeading, newTrailing,
insert);
newLeading.clear();
newLeadingPadding.clear();
int nextCounter = indexOfNextNonZeroSpring(counter + 1,
newTrailing.size() == 0);
if (nextCounter == springs.size()) {
trailing.addAll(newTrailing);
trailingPadding.addAll(newTrailingPadding);
} else {
newLeading.addAll(newTrailing);
newLeadingPadding.addAll(newTrailingPadding);
}
counter = nextCounter;
} else {
// Gap
newLeadingPadding.clear();
newLeading.clear();
counter++;
}
}
}
}
int getBaseline() {
if (baselineSpring != null) {
int baseline = baselineSpring.getBaseline();
if (baseline >= 0) {
int size = 0;
for (int i = 0, max = springs.size(); i < max; i++) {
Spring spring = getSpring(i);
if (spring == baselineSpring) {
return size + baseline;
} else {
size += spring.getPreferredSize(VERTICAL);
}
}
}
}
return -1;
}
int getBaselineResizeBehavior() {
if (isResizable(VERTICAL)) {
if (!baselineSpring.isResizable(VERTICAL)) {
// Spring to use for baseline isn't resizable. In this case
// baseline resize behavior can be determined based on how
// preceeding springs resize.
boolean leadingResizable = false;
for (int i = 0, max = springs.size(); i < max; i++) {
Spring spring = getSpring(i);
if (spring == baselineSpring) {
break;
} else if (spring.isResizable(VERTICAL)) {
leadingResizable = true;
break;
}
}
boolean trailingResizable = false;
for (int i = springs.size() - 1; i >= 0; i--) {
Spring spring = getSpring(i);
if (spring == baselineSpring) {
break;
}
if (spring.isResizable(VERTICAL)) {
trailingResizable = true;
break;
}
}
if (leadingResizable && !trailingResizable) {
return Baseline.BRB_CONSTANT_DESCENT;
} else if (!leadingResizable && trailingResizable) {
return Baseline.BRB_CONSTANT_ASCENT;
}
// If we get here, both leading and trailing springs are
// resizable. Fall through to OTHER.
} else {
int brb = baselineSpring.getBaselineResizeBehavior();
if (brb == Baseline.BRB_CONSTANT_ASCENT) {
for (int i = 0, max = springs.size(); i < max; i++) {
Spring spring = getSpring(i);
if (spring == baselineSpring) {
return Baseline.BRB_CONSTANT_ASCENT;
}
if (spring.isResizable(VERTICAL)) {
return Baseline.BRB_OTHER;
}
}
} else if (brb == Baseline.BRB_CONSTANT_DESCENT) {
for (int i = springs.size() - 1; i >= 0; i--) {
Spring spring = getSpring(i);
if (spring == baselineSpring) {
return Baseline.BRB_CONSTANT_DESCENT;
}
if (spring.isResizable(VERTICAL)) {
return Baseline.BRB_OTHER;
}
}
}
}
return Baseline.BRB_OTHER;
}
// Not resizable, treat as constant_ascent
return Baseline.BRB_CONSTANT_ASCENT;
}
}
/**
* Used in figuring out how much space to give resizable springs.
*/
private static final class SpringDelta implements Comparable {
// Original index.
public final int index;
// Delta, one of pref - min or max - pref.
public int delta;
public SpringDelta(int index, int delta) {
this.index = index;
this.delta = delta;
}
public int compareTo(Object o) {
return delta - ((SpringDelta)o).delta;
}
public String toString() {
return super.toString() + "[index=" + index + ", delta=" +
delta + "]";
}
}
/**
* A Group
that lays out its elements on top of each
* other. If a child element is smaller than the provided space it
* is aligned based on the alignment of the child (if specified) or
* on the alignment of the ParallelGroup.
*
* @see #createParallelGroup()
*/
public class ParallelGroup extends Group {
// How children are layed out.
private final int childAlignment;
// Whether or not we're resizable.
private final boolean resizable;
ParallelGroup(int childAlignment, boolean resizable) {
this.childAlignment = childAlignment;
this.resizable = resizable;
}
/**
* Adds the specified Group
.
*
* @param group the Group to add
* @return this Group
*/
public ParallelGroup add(Group group) {
return (ParallelGroup)addSpring(group);
}
/**
* Adds the specified Component. If the Component's min/max
* are different from its pref than the component will be resizable.
*
* @param component the Component to add
* @return this ParallelGroup
*/
public ParallelGroup add(Component component) {
return add(component, DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE);
}
/**
* Adds the specified Component
. Min, pref and max
* can be absolute values, or they can be one of
* DEFAULT_SIZE
or PREFERRED_SIZE
. For
* example, the following:
* * add(component, PREFERRED_SIZE, PREFERRED_SIZE, 1000); ** Forces a max of 1000, with the min and preferred equalling that * of the preferred size of
component
.
*
* @param component the Component to add
* @param min the minimum size
* @param pref the preferred size
* @param max the maximum size
* @throws IllegalArgumentException if min, pref or max are
* not positive and not one of PREFERRED_SIZE or DEFAULT_SIZE.
* @return this SequentialGroup
*/
public ParallelGroup add(Component component, int min, int pref,
int max) {
return (ParallelGroup)addSpring(new ComponentSpring(
component, min, pref, max));
}
/**
* Adds a rigid gap.
*
* @param pref the size of the gap
* @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
* or the following is not meant min <= pref <= max.
* @return this ParallelGroup
*/
public ParallelGroup add(int pref) {
return add(pref, pref, pref);
}
/**
* Adds a gap with the specified size.
*
* @param min the minimum size of the gap
* @param pref the preferred size of the gap
* @param max the maximum size of the gap
* @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
* or the following is not meant min <= pref <= max.
* @return this ParallelGroup
*/
public ParallelGroup add(int min, int pref, int max) {
return (ParallelGroup)addSpring(new GapSpring(min, pref, max));
}
/**
* Adds the specified Group
as a child of this group.
*
* @param alignment the alignment of the Group.
* @param group the Group to add
* @return this ParallelGroup
* @throws IllegalArgumentException if alignment is not one of
* LEADING
, TRAILING
or
* CENTER
*/
public ParallelGroup add(int alignment, Group group) {
checkChildAlignment(alignment);
group.setAlignment(alignment);
return (ParallelGroup)addSpring(group);
}
/**
* Adds the specified Component. If the Component's min/max
* are different from its pref than the component will be resizable.
*
* @param alignment the alignment for the component
* @param component the Component to add
* @return this Group
* @throws IllegalArgumentException if alignment is not one of
* LEADING
, TRAILING
or
* CENTER
*/
public ParallelGroup add(int alignment, Component component) {
return add(alignment, component, DEFAULT_SIZE, DEFAULT_SIZE,
DEFAULT_SIZE);
}
/**
* Adds the specified Component
. Min, pref and max
* can be absolute values, or they can be one of
* DEFAULT_SIZE
or PREFERRED_SIZE
. For
* example, the following:
* * add(component, PREFERRED_SIZE, PREFERRED_SIZE, 1000); ** Forces a max of 1000, with the min and preferred equalling that * of the preferred size of
component
.
*
* @param alignment the alignment for the component.
* @param component the Component to add
* @param min the minimum size
* @param pref the preferred size
* @param max the maximum size
* @throws IllegalArgumentException if min, pref or max are
* not positive and not one of PREFERRED_SIZE or DEFAULT_SIZE.
* @return this Group
*/
public ParallelGroup add(int alignment, Component component, int min,
int pref, int max) {
checkChildAlignment(alignment);
ComponentSpring spring = new ComponentSpring(component,
min, pref, max);
spring.setAlignment(alignment);
return (ParallelGroup)addSpring(spring);
}
boolean isResizable() {
return resizable;
}
int operator(int a, int b) {
return Math.max(a, b);
}
int calculateMinimumSize(int axis) {
if (!isResizable()) {
return getPreferredSize(axis);
}
return super.calculateMinimumSize(axis);
}
int calculateMaximumSize(int axis) {
if (!isResizable()) {
return getPreferredSize(axis);
}
return super.calculateMaximumSize(axis);
}
void setValidSize(int axis, int origin, int size) {
for (int i = 0, max = springs.size(); i < max; i++) {
setChildSize(getSpring(i), axis, origin, size);
}
}
void setChildSize(Spring spring, int axis, int origin, int size) {
int alignment = spring.getAlignment();
int springSize = Math.min(
Math.max(spring.getMinimumSize(axis), size),
spring.getMaximumSize(axis));
if (alignment == NO_ALIGNMENT) {
alignment = childAlignment;
}
switch (alignment) {
case TRAILING:
spring.setSize(axis, origin + size - springSize,
springSize);
break;
case CENTER:
spring.setSize(axis, origin +
(size - springSize) / 2,springSize);
break;
default: // LEADING, or BASELINE
spring.setSize(axis, origin, springSize);
break;
}
}
void insertAutopadding(int axis, List leadingPadding,
List trailingPadding, List leading, List trailing,
boolean insert) {
for (int counter = 0, max = springs.size(); counter < max; counter++) {
Spring spring = getSpring(counter);
if (spring instanceof ComponentSpring) {
if (((ComponentSpring)spring).isVisible()) {
for (int i = 0; i < leadingPadding.size(); i++) {
((AutopaddingSpring)leadingPadding.get(i)).addTarget(
(ComponentSpring)spring, axis);
}
trailing.add(spring);
}
} else if (spring instanceof Group) {
((Group)spring).insertAutopadding(axis, leadingPadding,
trailingPadding, leading, trailing, insert);
} else if (spring instanceof AutopaddingSpring) {
((AutopaddingSpring)spring).setSources(leading);
trailingPadding.add(spring);
}
}
}
private void checkChildAlignment(int alignment) {
boolean allowsBaseline = (this instanceof BaselineGroup);
if (!allowsBaseline && alignment == BASELINE) {
throw new IllegalArgumentException("Alignment must be one of:" +
"LEADING, TRAILING or CENTER");
}
if (alignment != CENTER && alignment != BASELINE &&
alignment != LEADING && alignment != TRAILING) {
throw new IllegalArgumentException("Alignment must be one of:" +
"LEADING, TRAILING or CENTER");
}
}
}
/**
* An extension of ParallelGroup
that aligns its
* constituent Spring
s along the baseline.
*/
private class BaselineGroup extends ParallelGroup {
// Whether or not all child springs have a baseline
private boolean allSpringsHaveBaseline;
// max(spring.getBaseline()) of all springs aligned along the baseline
// that have a baseline
private int prefAscent;
// max(spring.getPreferredSize().height - spring.getBaseline()) of all
// springs aligned along the baseline that have a baseline
private int prefDescent;
// Whether baselineAnchoredToTop was explicitly set
private boolean baselineAnchorSet;
// Whether the baseline is anchored to the top or the bottom.
// If anchored to the top the baseline is always at prefAscent,
// otherwise the baseline is at (height - prefDescent)
private boolean baselineAnchoredToTop;
// Whether or not the baseline has been calculated.
private boolean calcedBaseline;
BaselineGroup(boolean resizable) {
super(LEADING, resizable);
prefAscent = prefDescent = -1;
calcedBaseline = false;
}
BaselineGroup(boolean resizable, boolean baselineAnchoredToTop) {
this(resizable);
this.baselineAnchoredToTop = baselineAnchoredToTop;
baselineAnchorSet = true;
}
void unset() {
super.unset();
prefAscent = prefDescent = -1;
calcedBaseline = false;
}
void setValidSize(int axis, int origin, int size) {
checkAxis(axis);
if (prefAscent == -1) {
super.setValidSize(axis, origin, size);
} else {
// do baseline layout
baselineLayout(origin, size);
}
}
int calculateSize(int axis, int type) {
checkAxis(axis);
if (!calcedBaseline) {
calculateBaselineAndResizeBehavior();
}
if (type == MIN_SIZE) {
return calculateMinSize();
}
if (type == MAX_SIZE) {
return calculateMaxSize();
}
if (allSpringsHaveBaseline) {
return prefAscent + prefDescent;
}
return Math.max(prefAscent + prefDescent,
super.calculateSize(axis, type));
}
private void calculateBaselineAndResizeBehavior() {
// calculate baseline
prefAscent = 0;
prefDescent = 0;
int baselineSpringCount = 0;
int resizeBehavior = Baseline.BRB_NONE;
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = getSpring(counter);
if (spring.getAlignment() == NO_ALIGNMENT ||
spring.getAlignment() == BASELINE) {
int baseline = spring.getBaseline();
if (baseline >= 0) {
if (spring.isResizable(VERTICAL)) {
int brb = spring.
getBaselineResizeBehavior();
if (resizeBehavior == Baseline.BRB_NONE) {
resizeBehavior = brb;
} else if (brb != resizeBehavior) {
resizeBehavior = Baseline.BRB_CONSTANT_ASCENT;
}
}
prefAscent = Math.max(prefAscent, baseline);
prefDescent = Math.max(prefDescent, spring.
getPreferredSize(VERTICAL) - baseline);
baselineSpringCount++;
}
}
}
if (!baselineAnchorSet) {
if (resizeBehavior == Baseline.BRB_CONSTANT_DESCENT){
this.baselineAnchoredToTop = false;
} else {
this.baselineAnchoredToTop = true;
}
}
allSpringsHaveBaseline = (baselineSpringCount == springs.size());
calcedBaseline = true;
}
private int calculateMaxSize() {
int maxAscent = prefAscent;
int maxDescent = prefDescent;
int nonBaselineMax = 0;
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = getSpring(counter);
int baseline;
int springMax = spring.getMaximumSize(VERTICAL);
if ((spring.getAlignment() == NO_ALIGNMENT ||
spring.getAlignment() == BASELINE) &&
(baseline = spring.getBaseline()) >= 0) {
int springPref = spring.getPreferredSize(VERTICAL);
if (springPref != springMax) {
switch (spring.getBaselineResizeBehavior()) {
case Baseline.BRB_CONSTANT_ASCENT:
if (baselineAnchoredToTop) {
maxDescent = Math.max(maxDescent,
springMax - baseline);
}
break;
case Baseline.BRB_CONSTANT_DESCENT:
if (!baselineAnchoredToTop) {
maxAscent = Math.max(maxAscent,
springMax - springPref + baseline);
}
break;
default: // CENTER_OFFSET and OTHER, not resizable
break;
}
}
} else {
// Not aligned along the baseline, or no baseline.
nonBaselineMax = Math.max(nonBaselineMax, springMax);
}
}
return Math.max(nonBaselineMax, maxAscent + maxDescent);
}
private int calculateMinSize() {
int minAscent = 0;
int minDescent = 0;
int nonBaselineMin = 0;
if (baselineAnchoredToTop) {
minAscent = prefAscent;
} else {
minDescent = prefDescent;
}
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = getSpring(counter);
int springMin = spring.getMinimumSize(VERTICAL);
int baseline;
if ((spring.getAlignment() == NO_ALIGNMENT ||
spring.getAlignment() == BASELINE) &&
(baseline = spring.getBaseline()) >= 0) {
int springPref = spring.getPreferredSize(VERTICAL);
switch (spring.getBaselineResizeBehavior()) {
case Baseline.BRB_CONSTANT_ASCENT:
if (baselineAnchoredToTop) {
minDescent = Math.max(springMin - baseline,
minDescent);
} else {
minAscent = Math.max(baseline, minAscent);
}
break;
case Baseline.BRB_CONSTANT_DESCENT:
if (!baselineAnchoredToTop) {
minAscent = Math.max(
baseline - (springPref - springMin),
minAscent);
} else {
minDescent = Math.max(springPref - baseline,
minDescent);
}
break;
default:
// CENTER_OFFSET and OTHER are !resizable, use
// the preferred size.
minAscent = Math.max(baseline, minAscent);
minDescent = Math.max(springPref - baseline,
minDescent);
break;
}
} else {
// Not aligned along the baseline, or no baseline.
nonBaselineMin = Math.max(nonBaselineMin, springMin);
}
}
return Math.max(nonBaselineMin, minAscent + minDescent);
}
/**
* Lays out springs that have a baseline along the baseline. All
* others are centered.
*/
private void baselineLayout(int origin, int size) {
int ascent;
int descent;
if (baselineAnchoredToTop) {
ascent = prefAscent;
descent = size - ascent;
} else {
ascent = size - prefDescent;
descent = prefDescent;
}
for (int counter = springs.size() - 1; counter >= 0; counter--) {
Spring spring = getSpring(counter);
int alignment = spring.getAlignment();
if (alignment == NO_ALIGNMENT || alignment == BASELINE) {
int baseline = spring.getBaseline();
if (baseline >= 0) {
int springMax = spring.getMaximumSize(VERTICAL);
int springPref = spring.getPreferredSize(VERTICAL);
int height = springPref;
int y;
switch(spring.getBaselineResizeBehavior()) {
case Baseline.BRB_CONSTANT_ASCENT:
y = origin + ascent - baseline;
height = Math.min(descent, springMax -
baseline) + baseline;
break;
case Baseline.BRB_CONSTANT_DESCENT:
height = Math.min(ascent, springMax -
springPref + baseline) +
(springPref - baseline);
y = origin + ascent +
(springPref - baseline) - height;
break;
default: // CENTER_OFFSET & OTHER, not resizable
y = origin + ascent - baseline;
break;
}
spring.setSize(VERTICAL, y, height);
} else {
setChildSize(spring, VERTICAL, origin, size);
}
} else {
setChildSize(spring, VERTICAL, origin, size);
}
}
}
int getBaseline() {
if (springs.size() > 1) {
// Force the baseline to be calculated
getPreferredSize(VERTICAL);
return prefAscent;
} else if (springs.size() == 1) {
return getSpring(0).getBaseline();
}
return -1;
}
int getBaselineResizeBehavior() {
if (springs.size() == 1) {
return getSpring(0).getBaselineResizeBehavior();
}
if (baselineAnchoredToTop) {
return Baseline.BRB_CONSTANT_ASCENT;
}
return Baseline.BRB_CONSTANT_DESCENT;
}
// If the axis is VERTICAL, throws an IllegalStateException
private void checkAxis(int axis) {
if (axis == HORIZONTAL) {
throw new IllegalStateException(
"Baseline must be used along vertical axis");
}
}
}
/**
* A Spring representing one axis of a Component.
* There are three ways to configure this:
* LayoutStyle
for the Windows look and feel.
* This information comes from:
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp
*
* @version $Revision: 1.8 $
*/
class WindowsLayoutStyle extends LayoutStyle {
/**
* Base dialog units along the horizontal axis.
*/
private int baseUnitX;
/**
* Base dialog units along the vertical axis.
*/
private int baseUnitY;
public int getPreferredGap(JComponent source, JComponent target,
int type, int position, Container parent) {
// Invoke super to check arguments.
super.getPreferredGap(source, target, type, position, parent);
if (type == INDENT) {
if (position == SwingConstants.EAST || position == SwingConstants.WEST) {
int gap = getButtonChildIndent(source, position);
if (gap != 0) {
return gap;
}
return 10;
}
// Treat vertical INDENT as RELATED
type = RELATED;
}
if (type == UNRELATED) {
// Between unrelated controls: 7
return getCBRBPadding(source, target, position,
dluToPixels(7, position));
}
else { //type == RELATED
boolean sourceLabel = (source.getUIClassID() == "LabelUI");
boolean targetLabel = (target.getUIClassID() == "LabelUI");
if (((sourceLabel && !targetLabel) ||
(targetLabel && !sourceLabel)) &&
(position == SwingConstants.EAST ||
position == SwingConstants.WEST)) {
// Between text labels and their associated controls (for
// example, text boxes and list boxes): 3
// NOTE: We're not honoring:
// 'Text label beside a button 3 down from the top of
// the button,' but I suspect that is an attempt to
// enforce a baseline layout which will be handled
// separately. In order to enforce this we would need
// this API to return a more complicated type (Insets,
// or something else).
return getCBRBPadding(source, target, position,
dluToPixels(3, position));
}
// Between related controls: 4
return getCBRBPadding(source, target, position,
dluToPixels(4, position));
}
}
public int getContainerGap(JComponent component, int position,
Container parent) {
super.getContainerGap(component, position, parent);
return getCBRBPadding(component, position, dluToPixels(7, position));
}
private int dluToPixels(int dlu, int direction) {
if (baseUnitX == 0) {
calculateBaseUnits();
}
if (direction == SwingConstants.EAST ||
direction == SwingConstants.WEST) {
return dlu * baseUnitX / 4;
}
assert (direction == SwingConstants.NORTH ||
direction == SwingConstants.SOUTH);
return dlu * baseUnitY / 8;
}
private void calculateBaseUnits() {
// This calculation comes from:
// http://support.microsoft.com/default.aspx?scid=kb;EN-US;125681
FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(
UIManager.getFont("Button.font"));
baseUnitX = metrics.stringWidth(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
baseUnitX = (baseUnitX / 26 + 1) / 2;
// The -1 comes from experimentation.
baseUnitY = metrics.getAscent() + metrics.getDescent() - 1;
}
}
./swing-layout-1.0.4/src/java/org/jdesktop/layout/LayoutStyle.java 0000644 0001750 0001750 00000040202 11271565634 024145 0 ustar yn158264 yn158264 /*
* Copyright (C) 2005-2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.layout;
import java.awt.Container;
import java.awt.Insets;
import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JRadioButton;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.UIResource;
/**
* LayoutStyle is used to determine how much space to place between components
* during layout. LayoutStyle can be obtained for two components, or for
* a component relative to an edge of a parent container. The amount of
* space can vary depending upon whether or not the components are
* logically grouped together (RELATED
).
*
* This class is primarily useful for JREs prior to 1.6. In 1.6 API for this
* was added to Swing. When run on a JRE of 1.6 or greater this will call into
* the appropriate methods in Swing.
*
* @version $Revision: 1.11 $
*/
public class LayoutStyle {
private static final boolean USE_CORE_LAYOUT_STYLE;
/**
* Possible argument to getPreferredGap. Used to indicate the two componets
* are grouped together.
*/
public static final int RELATED = 0;
/**
* Possible argument to getPreferredGap. Used to indicate the two componets
* are not grouped together.
*/
public static final int UNRELATED = 1;
/**
* Possible argument to getPreferredGap. Used to indicate the distance
* to indent a component is being requested. To visually indicate
* a set of related components they will often times be horizontally
* indented, the INDENT
constant for this.
* For example, to indent a check box relative to a label use this
* constant to getPreferredGap
.
*/
public static final int INDENT = 3;
private static LayoutStyle layoutStyle;
private static LookAndFeel laf;
static {
boolean useCoreLayoutStyle = false;
try {
Class.forName("javax.swing.LayoutStyle");
useCoreLayoutStyle = true;
} catch (ClassNotFoundException cnfe) {
}
USE_CORE_LAYOUT_STYLE = useCoreLayoutStyle;
}
/**
* Sets the LayoutStyle instance to use for this look and feel.
* You generally don't need to invoke this, instead use the getter which
* will return the LayoutStyle appropriate for the current look and feel.
*
* @param layoutStyle the LayoutStyle to use; a value of null indicates
* the default should be used
*/
public static void setSharedInstance(LayoutStyle layoutStyle) {
UIManager.getLookAndFeelDefaults().put("LayoutStyle.instance",
layoutStyle);
}
/**
* Factory methods for obtaining the current LayoutStyle
* object appropriate for the current look and feel.
*
* @return the current LayoutStyle instance
*/
public static LayoutStyle getSharedInstance() {
Object layoutImpl = UIManager.get("LayoutStyle.instance");
if (layoutImpl != null && (layoutImpl instanceof LayoutStyle)) {
return (LayoutStyle)layoutImpl;
}
LookAndFeel currentLAF = UIManager.getLookAndFeel();
if (layoutStyle == null || currentLAF != laf) {
laf = currentLAF;
String lafID= laf.getID();
if (USE_CORE_LAYOUT_STYLE) {
if ("Aqua" == lafID) {
try {
currentLAF.getClass().getDeclaredMethod("getLayoutStyle", new Class[0]);
layoutStyle = new SwingLayoutStyle();
} catch (NoSuchMethodException nsfex) {
// getLayoutStyle() not overriden => use our own (issue 52)
layoutStyle = new AquaLayoutStyle();
}
} else {
layoutStyle = new SwingLayoutStyle();
}
} else if ("Metal" == lafID) {
layoutStyle = new MetalLayoutStyle();
}
else if ("Windows" == lafID) {
layoutStyle = new WindowsLayoutStyle();
}
else if ("GTK" == lafID) {
layoutStyle = new GnomeLayoutStyle();
}
else if ("Aqua" == lafID) {
layoutStyle = new AquaLayoutStyle();
} else {
layoutStyle = new LayoutStyle();
}
}
return layoutStyle;
}
/**
* Returns the amount of space to use between two components.
* The return value indicates the distance to place
* component2
relative to component1
.
* For example, the following returns the amount of space to place
* between component2
and component1
* when component2
is placed vertically above
* component1
:
*
* int gap = getPreferredGap(component1, component2, * LayoutStyle.RELATED, * SwingConstants.NORTH, parent); ** The
type
parameter indicates the type
* of gap being requested. It can be one of the following values:
* RELATED
* | If the two components will be contained in
* the same parent and are showing similar logically related
* items, use RELATED .
* |
UNRELATED
* | If the two components will be
* contained in the same parent but show logically unrelated items
* use UNRELATED .
* |
INDENT
* | Used to obtain the preferred distance to indent a component
* relative to another. For example, if you want to horizontally
* indent a JCheckBox relative to a JLabel use INDENT .
* This is only useful for the horizontal axis.
* |
* It's important to note that some look and feels may not distinguish
* between RELATED
and UNRELATED
.
*
* The return value is not intended to take into account the
* current size and position of component2
or
* component1
. The return value may take into
* consideration various properties of the components. For
* example, the space may vary based on font size, or the preferred
* size of the component.
*
* @param component1 the JComponent
* component2
is being placed relative to
* @param component2 the JComponent
being placed
* @param type how the two components are being placed
* @param position the position component2
is being placed
* relative to component1
; one of
* SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
* @param parent the parent of component2
; this may differ
* from the actual parent and may be null
* @return the amount of space to place between the two components
* @throws IllegalArgumentException if position
is not
* one of SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
; type
not one
* of INDENT
, RELATED
* or UNRELATED
; or component1
or
* component2
is null
*/
public int getPreferredGap(JComponent component1, JComponent component2,
int type, int position, Container parent) {
if (position != SwingConstants.NORTH &&
position != SwingConstants.SOUTH &&
position != SwingConstants.WEST &&
position != SwingConstants.EAST) {
throw new IllegalArgumentException("Invalid position");
}
if (component1 == null || component2== null) {
throw new IllegalArgumentException("Components must be non-null");
}
if (type == RELATED) {
return 6;
} else if (type == UNRELATED) {
return 12;
} else if (type == INDENT) {
if (position == SwingConstants.EAST || position == SwingConstants.WEST) {
int gap = getButtonChildIndent(component1, position);
if (gap != 0) {
return gap;
}
return 6;
}
return 6;
}
throw new IllegalArgumentException("Invalid type");
}
/**
* Returns the amount of space to position a component inside its
* parent.
*
* @param component the Component
being positioned
* @param position the position component
is being placed
* relative to its parent; one of
* SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
* @param parent the parent of component
; this may differ
* from the actual parent and may be null
* @return the amount of space to place between the component and specified
* edge
* @throws IllegalArgumentException if position
is not
* one of SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
;
* or component
is null
*/
public int getContainerGap(JComponent component, int position,
Container parent) {
if (position != SwingConstants.NORTH &&
position != SwingConstants.SOUTH &&
position != SwingConstants.WEST &&
position != SwingConstants.EAST) {
throw new IllegalArgumentException("Invalid position");
}
if (component == null) {
throw new IllegalArgumentException("Component must be non-null");
}
return 12;
}
/**
* Returns true if component
should be treated as a dialog.
*/
boolean isDialog(JComponent component) {
// PENDING: tag the content pane to make this easier to check for
String name = component.getName();
return (name != null && name.endsWith(".contentPane"));
}
/**
* For some look and feels check boxs and radio buttons have an empty
* border around them. Look and feel guidelines generally don't include
* this space. Use this method to subtract this space from the specified
* components.
*
* @param source First component
* @param target Second component
* @param position Position doing layout along.
* @param offset Ideal offset, not including border/margin
* @return offset - border/margin around the component.
*/
int getCBRBPadding(JComponent source, JComponent target, int position,
int offset) {
offset -= getCBRBPadding(source, position);
if (offset > 0) {
offset -= getCBRBPadding(target, flipDirection(position));
}
if (offset < 0) {
return 0;
}
return offset;
}
/**
* For some look and feels check boxs and radio buttons have an empty
* border around them. Look and feel guidelines generally don't include
* this space. Use this method to subtract this space from the specified
* components.
*
* @param source Component
* @param position Position doing layout along.
* @param offset Ideal offset, not including border/margin
* @return offset - border/margin around the component.
*/
int getCBRBPadding(JComponent source, int position, int offset) {
offset -= getCBRBPadding(source, position);
return Math.max(offset, 0);
}
int flipDirection(int position) {
switch(position) {
case SwingConstants.NORTH:
return SwingConstants.SOUTH;
case SwingConstants.SOUTH:
return SwingConstants.NORTH;
case SwingConstants.EAST:
return SwingConstants.WEST;
case SwingConstants.WEST:
return SwingConstants.EAST;
}
assert false;
return 0;
}
private int getCBRBPadding(JComponent c, int position) {
if (c.getUIClassID() == "CheckBoxUI" ||
c.getUIClassID() == "RadioButtonUI") {
Border border = c.getBorder();
if (border instanceof UIResource) {
return getInset(c, position);
}
}
return 0;
}
private int getInset(JComponent c, int position) {
Insets insets = c.getInsets();
switch(position) {
case SwingConstants.NORTH:
return insets.top;
case SwingConstants.SOUTH:
return insets.bottom;
case SwingConstants.EAST:
return insets.right;
case SwingConstants.WEST:
return insets.left;
}
assert false;
return 0;
}
private boolean isLeftAligned(AbstractButton button, int position) {
if (position == SwingConstants.WEST) {
boolean ltr = button.getComponentOrientation().isLeftToRight();
int hAlign = button.getHorizontalAlignment();
return ((ltr && (hAlign == SwingConstants.LEFT ||
hAlign == SwingConstants.LEADING)) ||
(!ltr && (hAlign == SwingConstants.TRAILING)));
}
return false;
}
private boolean isRightAligned(AbstractButton button, int position) {
if (position == SwingConstants.EAST) {
boolean ltr = button.getComponentOrientation().isLeftToRight();
int hAlign = button.getHorizontalAlignment();
return ((ltr && (hAlign == SwingConstants.RIGHT ||
hAlign == SwingConstants.TRAILING)) ||
(!ltr && (hAlign == SwingConstants.LEADING)));
}
return false;
}
private Icon getIcon(AbstractButton button) {
Icon icon = button.getIcon();
if (icon != null) {
return icon;
}
String key = null;
if (button instanceof JCheckBox) {
key = "CheckBox.icon";
} else if (button instanceof JRadioButton) {
key = "RadioButton.icon";
}
if (key != null) {
Object oIcon = UIManager.get(key);
if (oIcon instanceof Icon) {
return (Icon)oIcon;
}
}
return null;
}
/**
* Returns the amount to indent the specified component if it's
* a JCheckBox or JRadioButton. If the component is not a JCheckBox or
* JRadioButton, 0 will be returned.
*/
int getButtonChildIndent(JComponent c, int position) {
if ((c instanceof JRadioButton) || (c instanceof JCheckBox)) {
AbstractButton button = (AbstractButton)c;
Insets insets = c.getInsets();
Icon icon = getIcon(button);
int gap = button.getIconTextGap();
if (isLeftAligned(button, position)) {
return insets.left + icon.getIconWidth() + gap;
} else if (isRightAligned(button, position)) {
return insets.right + icon.getIconWidth() + gap;
}
}
return 0;
}
}
./swing-layout-1.0.4/src/java/org/jdesktop/layout/MetalLayoutStyle.java 0000644 0001750 0001750 00000016550 10441546106 025130 0 ustar yn158264 yn158264 /*
* Copyright (C) 2005-2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.layout;
import java.awt.Container;
import javax.swing.ButtonModel;
import javax.swing.DefaultButtonModel;
import javax.swing.JComponent;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.MetalTheme;
import java.lang.reflect.*;
/**
* An implementation of LayoutStyle
for the java look and feel.
* This information comes from the
*
* The Java Look and Feel Design Guidelines.
*
* @version $Revision: 1.7 $
*/
class MetalLayoutStyle extends LayoutStyle {
/**
* Whether or not we're using ocean, the default metal theme in 1.5.
*/
private boolean isOcean;
public MetalLayoutStyle() {
isOcean = false;
try {
Method method = MetalLookAndFeel.class.
getMethod("getCurrentTheme", (Class[])null);
isOcean = ((MetalTheme)method.invoke(null, (Object[])null)).
getName() == "Ocean";
} catch (NoSuchMethodException nsme) {
} catch (IllegalAccessException iae) {
} catch (IllegalArgumentException iae2) {
} catch (InvocationTargetException ite) {
}
}
// NOTE: The JLF makes reference to a number of guidelines in terms of
// 6 pixels - 1 pixel. The rationale is because steel buttons have
// a heavy border followed by a light border, and so that if you pad
// by 6 pixels it'll look like 7. Using 5 pixels than produces an effect
// of 6 pixels. With Ocean things are different, the only component
// that you want this behavior to happen with is checkboxs.
public int getPreferredGap(JComponent source, JComponent target,
int type, int position, Container parent) {
// Invoke super to check arguments.
super.getPreferredGap(source, target, type, position, parent);
if (type == INDENT) {
if (position == SwingConstants.EAST || position == SwingConstants.WEST) {
int gap = getButtonChildIndent(source, position);
if (gap != 0) {
return gap;
}
return 12;
}
// Treat vertical INDENT as RELATED
type = RELATED;
}
String sourceCID = source.getUIClassID();
String targetCID = target.getUIClassID();
int offset;
if (type == RELATED) {
if (sourceCID == "ToggleButtonUI" &&
targetCID == "ToggleButtonUI") {
ButtonModel sourceModel = ((JToggleButton)source).getModel();
ButtonModel targetModel = ((JToggleButton)target).getModel();
if ((sourceModel instanceof DefaultButtonModel) &&
(targetModel instanceof DefaultButtonModel) &&
(((DefaultButtonModel)sourceModel).getGroup() ==
((DefaultButtonModel)targetModel).getGroup()) &&
((DefaultButtonModel)sourceModel).getGroup() != null) {
// When toggle buttons are exclusive (that is, they form a
// radio button set), separate them with 2 pixels. This
// rule applies whether the toggle buttons appear in a
// toolbar or elsewhere in the interface.
// Note: this number does not appear to include any borders
// and so is not adjusted by the border of the toggle
// button
return 2;
}
// When toggle buttons are independent (like checkboxes)
// and used outside a toolbar, separate them with 5
// pixels.
if (isOcean) {
return 6;
}
return 5;
}
offset = 6;
}
else {
offset = 12;
}
if ((position == SwingConstants.EAST ||
position == SwingConstants.WEST) &&
((sourceCID == "LabelUI" && targetCID != "LabelUI") ||
(sourceCID != "LabelUI" && targetCID == "LabelUI"))) {
// Insert 12 pixels between the trailing edge of a
// label and any associated components. Insert 12
// pixels between the trailing edge of a label and the
// component it describes when labels are
// right-aligned. When labels are left-aligned, insert
// 12 pixels between the trailing edge of the longest
// label and its associated component
return getCBRBPadding(source, target, position, offset + 6);
}
return getCBRBPadding(source, target, position, offset);
}
int getCBRBPadding(JComponent source, JComponent target, int position,
int offset) {
offset = super.getCBRBPadding(source, target, position, offset);
if (offset > 0) {
int buttonAdjustment = getButtonAdjustment(source, position);
if (buttonAdjustment == 0) {
buttonAdjustment = getButtonAdjustment(target,
flipDirection(position));
}
offset -= buttonAdjustment;
}
if (offset < 0) {
return 0;
}
return offset;
}
private int getButtonAdjustment(JComponent source, int edge) {
String uid = source.getUIClassID();
if (uid == "ButtonUI" || uid == "ToggleButtonUI") {
if (!isOcean && (edge == SwingConstants.EAST ||
edge == SwingConstants.SOUTH)) {
return 1;
}
}
else if (edge == SwingConstants.SOUTH) {
if (uid == "RadioButtonUI" || (!isOcean && uid == "CheckBoxUI")) {
return 1;
}
}
return 0;
}
public int getContainerGap(JComponent component, int position,
Container parent) {
super.getContainerGap(component, position, parent);
// Here's the rules we should be honoring:
//
// Include 11 pixels between the bottom and right
// borders of a dialog box and its command
// buttons. (To the eye, the 11-pixel spacing appears
// to be 12 pixels because the white borders on the
// lower and right edges of the button components are
// not visually significant.)
// NOTE: this last text was designed with Steel in mind, not Ocean.
//
// Insert 12 pixels between the edges of the panel and the
// titled border. Insert 11 pixels between the top of the
// title and the component above the titled border. Insert 12
// pixels between the bottom of the title and the top of the
// first label in the panel. Insert 11 pixels between
// component groups and between the bottom of the last
// component and the lower border.
return getCBRBPadding(component, position, 12 -
getButtonAdjustment(component, position));
}
}
./swing-layout-1.0.4/src/java/org/jdesktop/layout/AquaLayoutStyle.java 0000644 0001750 0001750 00000117377 10701140642 024760 0 ustar yn158264 yn158264 /*
* Copyright (C) 2005-2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.layout;
import javax.swing.border.EmptyBorder;
import org.jdesktop.layout.*;
import java.awt.*;
import java.lang.reflect.*;
import javax.swing.*;
import java.util.*;
/**
* An implementation of LayoutStyle
for Mac OS X Tiger.
*
* The information used for this layout style comes from: * http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/ * * @author Werner Randelshofer * @version $Revision: 1.4 $ */ class AquaLayoutStyle extends LayoutStyle { private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); /** Mini size style. */ private final static int MINI = 0; /** Small size style. */ private final static int SMALL = 1; /** Regular size style. */ private final static int REGULAR = 2; /** * The containerGapDefinitions array defines the preferred insets (child gaps) * of a parent container towards one of its child components. * * Note: As of now, we do not yet specify the preferred gap from a child * to its parent. Therefore we may not be able to treat all special cases. * * This array is used to initialize the containerGaps HashMap. * * The array has the following structure, which is supposed to be a * a compromise between legibility and code size. * containerGapDefinitions[0..n] = preferred insets for some parent UI's * containerGapDefinitions[][0..m-3] = name of parent UI, * optionally followed by a full stop and * a style name * containerGapDefinitions[][m-2] = mini insets * containerGapDefinitions[][m-1] = small insets * containerGapDefinitions[][m] = regular insets */ private final static Object[][] containerGapDefinitions = { // Format: // { list of parent UI's, // mini insets, small insets, regular insets } { "TabbedPaneUI", new Insets(6,10,10,10), new Insets(6,10,10,12), new Insets(12,20,20,20) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_27 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_26 // note for small and mini size: leave 8 to 10 pixels on top // note for regular size: leave only 12 pixel at top if tabbed pane UI { "RootPaneUI", new Insets(8,10,10,10), new Insets(8,10,10,12), new Insets(14,20,20,20) }, // These child gaps are used for all other components { "default", new Insets(8,10,10,10), new Insets(8,10,10,12), new Insets(14,20,20,20) }, }; /** * The relatedGapDefinitions table defines the preferred gaps * of one party of two related components. * * The effective preferred gap is the maximum of the preferred gaps of * both parties. * * This array is used to initialize the relatedGaps HashMap. * * The array has the following structure, which is supposed to be a * a compromise between legibility and code size. * containerGapDefinitions[0..n] = preferred gaps for a party of a two related UI's * containerGapDefinitions[][0..m-3] = name of UI * optionally followed by a full stop and * a style name * containerGapDefinitions[][m-2] = mini insets * containerGapDefinitions[][m-1] = small insets * containerGapDefinitions[][m] = regular insets */ private final static Object[][] relatedGapDefinitions = { // Format: // { list of UI's, // mini insets, small insets, regular insets } // Push Button: // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF104 { "ButtonUI", "ButtonUI.push", "ButtonUI.text", "ToggleButtonUI.push", "ToggleButtonUI.text", new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12) }, // Metal Button // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF187 { "ButtonUI.metal", "ToggleButtonUI.metal", new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(12,12,12,12) }, // Bevel Button (Rounded and Square) // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112 { "ButtonUI.bevel", "ButtonUI.toggle", "ButtonUI.square", "ToggleButtonUI", "ToggleButtonUI.bevel", "ToggleButtonUI.square", "ToggleButtonUI.toggle", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, // Bevel Button (Rounded and Square) // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112 { "ButtonUI.bevel.largeIcon", "ToggleButtonUI.bevel.largeIcon", new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(8,8,8,8) }, // Icon Button // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF189 { "ButtonUI.icon", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, { "ButtonUI.icon.largeIcon", new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(8,8,8,8) }, // Round Button // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF191 { "ButtonUI.round", "ToggleButtonUI.round", new Insets(12,12,12,12), new Insets(12,12,12,12), new Insets(12,12,12,12) }, // Help Button // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF193 { "ButtonUI.help", new Insets(12,12,12,12), new Insets(12,12,12,12), new Insets(12,12,12,12) }, // Segmented Control // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF196 { "ButtonUI.toggleCenter", "ToggleButtonUI.toggleCenter", new Insets(8,0,8,0), new Insets(10,0,10,0), new Insets(12,0,12,0) }, { "ButtonUI.toggleEast", "ToggleButtonUI.toggleEast", new Insets(8,0,8,8), new Insets(10,0,10,10), new Insets(12,0,12,12) }, { "ButtonUI.toggleWest", "ToggleButtonUI.toggleWest", new Insets(8,8,8,0), new Insets(10,10,10,0), new Insets(12,12,12,0) }, { "ButtonUI.toolBarTab", "ToggleButtonUI.toolBarTab", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, // Color Well Button // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF213 { "ButtonUI.colorWell", "ToggleButtonUI.colorWell", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198 // FIXME - The following values are given in the AHIG. // In reality, the values further below seem to be more appropriate. // Which ones are right? //{ "CheckBoxUI", new Insets(7, 5, 7, 5), new Insets(8, 6, 8, 6), new Insets(8, 8, 8, 8) }, { "CheckBoxUI", new Insets(6, 5, 6, 5), new Insets(7, 6, 7, 6), new Insets(7, 6, 7, 6) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198 { "ComboBoxUI.editable", new Insets(8, 5, 8, 5), new Insets(10, 6, 10, 6), new Insets(12, 8, 12, 8) }, { "ComboBoxUI.uneditable", new Insets(6, 5, 6, 5), new Insets(8, 6, 8, 6), new Insets(10, 8, 10, 8) }, // There is no spacing given for labels. // This comes from playing with IB. // We use the values here, which is the minimum of the spacing of all // other components. { "LabelUI", new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8) }, // ? spacing not given { "ListUI", new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6) }, // ? spacing not given { "PanelUI", new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_5.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF106 // ? spacing not given { "ProgressBarUI", new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-BIAHBFAD { "RadioButtonUI", new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6) }, //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF114 // ? spacing not given. We use the same as for text fields. { "ScrollPaneUI", new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10) }, //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_8.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF214 // ? spacing not given //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_2.html#//apple_ref/doc/uid/20000957-TP30000360-CHDEACGD { "SeparatorUI", new Insets(8, 8, 8, 8), new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF115 { "SliderUI.horizontal", new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12) }, { "SliderUI.vertical", new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12) }, //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF204 { "SpinnerUI", new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-CHDDBIJE // ? spacing not given { "SplitPaneUI", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF105 // ? spacing not given { "TabbedPaneUI", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, { "TableUI", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, // ? spacing not given { "TextAreaUI", "EditorPaneUI", "TextPaneUI", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF225 { "TextFieldUI", "FormattedTextFieldUI", "PasswordFieldUI", new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10) }, // ? spacing not given { "TreeUI", new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) }, }; private final static Object[][] unrelatedGapDefinitions = { // UI, mini, small, regular { "ButtonUI.help", new Insets(24,24,24,24), new Insets(24,24,24,24), new Insets(24,24,24,24) }, { "default", new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12), new Insets(14, 14, 14, 14) }, }; /** * The indentGapDefinitions table defines the preferred indentation * for components that are indented after the specified component. * * This array is used to initialize the indentGaps HashMap. * * The array has the following structure, which is supposed to be a * a compromise between legibility and code size. * indentGapDefinitions[0..n] = preferred gaps for a party of a two related UI's * indentGapDefinitions[][0..m-3] = name of UI * optionally followed by a full stop and * a style name * indentGapDefinitions[][m-2] = mini insets * indentGapDefinitions[][m-1] = small insets * indentGapDefinitions[][m] = regular insets */ private final static Object[][] indentGapDefinitions = { // UI, mini, small, regular // The Aqua L&F does not scale button images of check boxes and radio // buttons. Therefore we use to the same horizontal indents for all sizes. { "CheckBoxUI", "RadioButtonUI", new Insets(16, 24, 16, 24), new Insets(20, 24, 20, 24), new Insets(24, 24, 24, 24) }, { "default", new Insets(16, 16, 16, 16), new Insets(20, 20, 20, 20), new Insets(24, 24, 24, 24) }, }; /** * The visualMarginDefinition table defines the visually perceived * margin of the components. * * This array is used to initialize the visualMargins HashMap. * * The array has the following structure, which is supposed to be a * a compromise between legibility and code size. * visualMarginDefinitions[0..n] = preferred gaps for a party of a two related UI's * visualMarginDefinitions[][0..m-1] = name of UI * optionally followed by a full stop and * a style name * containerGapDefinitions[][m] = visual margins */ private final static Object[][] visualMarginDefinitions = { // UI, regular { "ButtonUI", "ButtonUI.text", "ToggleButtonUI", "ToggleButtonUI.text", new Insets(5, 3, 3, 3) }, { "ButtonUI.icon", "ToggleButtonUI.icon", new Insets(5, 2, 3, 2) }, { "ButtonUI.toolbar", "ToggleButtonUI.toolbar", new Insets(0, 0, 0, 0) }, { "CheckBoxUI", new Insets(4, 4, 3, 3) }, { "ComboBoxUI", new Insets(2, 3, 4, 3) }, { "DesktopPaneUI", new Insets(0, 0, 0, 0) }, { "EditorPaneUI", "TextAreaUI", "TextPaneUI", new Insets(0, 0, 0, 0) }, { "FormattedTextFieldUI", "PasswordFieldUI", "TextFieldUI", new Insets(0, 0, 0, 0) }, { "LabelUI", new Insets(0, 0, 0, 0) }, { "ListUI", new Insets(0, 0, 0, 0) }, { "PanelUI", new Insets(0, 0, 0, 0) }, { "ProgressBarUI", "ProgressBarUI.horizontal", new Insets(0, 2, 4, 2) }, { "ProgressBarUI.vertical", new Insets(2, 0, 2, 4) }, { "RadioButtonUI", new Insets(4, 4, 3, 3) }, { "ScrollBarUI", new Insets(0, 0, 0, 0) }, { "ScrollPaneUI", new Insets(0, 0, 0, 0) }, { "SpinnerUI", new Insets(0, 0, 0, 0) }, { "SeparatorUI", new Insets(0, 0, 0, 0) }, { "SplitPaneUI", new Insets(0, 0, 0, 0) }, { "SliderUI", "SliderUI.horizontal", new Insets(3, 6, 3, 6) }, { "SliderUI.vertical", new Insets(6, 3, 6, 3) }, { "TabbedPaneUI", "TabbedPaneUI.top", new Insets(5, 7, 10, 7) }, { "TabbedPaneUI.bottom", new Insets(4, 7, 5, 7) }, { "TabbedPaneUI.left", new Insets(4, 6, 10, 7) }, { "TabbedPaneUI.right", new Insets(4, 7, 10, 6) }, { "TableUI", new Insets(0, 0, 0, 0) }, { "TreeUI", new Insets(0, 0, 0, 0) }, { "default", new Insets(0, 0, 0, 0) }, }; /** * The relatedGaps map defines the preferred gaps * of one party of two related components. */ private final static Map RELATED_GAPS = createInsetsMap(relatedGapDefinitions); /** * The unrelatedGaps map defines the preferred gaps * of one party of two unrelated components. */ private final static Map UNRELATED_GAPS = createInsetsMap(unrelatedGapDefinitions); /** * The containerGaps map defines the preferred insets (child gaps) * of a parent component towards one of its children. */ private final static Map CONTAINER_GAPS = createInsetsMap(containerGapDefinitions); /** * The indentGaps map defines the preferred indentation * for components that are indented after the specified component. */ private final static Map INDENT_GAPS = createInsetsMap(indentGapDefinitions); /** * The visualMargins map defines the preferred indentation * for components that are indented after the specified component. */ private final static Map VISUAL_MARGINS = createInsetsMap(visualMarginDefinitions); /** * Creates a map for the specified definitions array. *
* The key for the map is the name of the UI, for example, ButtonUI, with
* a value of ComponentInsets. Each ComponentInsets may have sub styles.
*/
// private static Map
* It's important to note that some look and feels may not distinguish
* between
* The return value is not intended to take into account the
* current size and position of
* In addition to determining the baseline, this class also allows for
* determining how the baseline changes as the size of the component changes.
* The method getBaselineResizeBehavior can be used for this. This will return
* one of BRB_OTHER, BRB_CONSTANT_ASCENT, BRB_CONSTANT_DESCENT or
* BRB_CENTER_OFFSET. The following algorithm is used in determining the
* baseline resize behavior.
*
* This class is primarily useful for JREs prior to 1.6. In 1.6 API for this
* was added directly to Component. When run on 1.6 or newer, this class calls
* into the appropriate Component methods.
*
* @version $Revision: 1.13 $
*/
public class Baseline {
static final int BRB_NONE = 0;
/**
* Baseline resize behavior constant. Indicates as the size of the component
* changes the baseline remains a fixed distance from the top of the
* component.
*/
public static final int BRB_CONSTANT_ASCENT = 1;
/**
* Baseline resize behavior constant. Indicates as the size of the component
* changes the baseline remains a fixed distance from the bottom of the
* component.
*/
public static final int BRB_CONSTANT_DESCENT = 2;
/**
* Baseline resize behavior constant. Indicates as the size of the component
* changes the baseline remains a fixed distance from the center of the
* component.
*/
public static final int BRB_CENTER_OFFSET = 3;
/**
* Baseline resize behavior constant. Indicates as the size of the component
* changes the baseline can not be determined using one of the other
* constants.
*/
public static final int BRB_OTHER = 4;
//
// Used by button and label baseline code, cached to avoid excessive
// garbage.
//
private static final Rectangle viewRect = new Rectangle();
private static final Rectangle textRect = new Rectangle();
private static final Rectangle iconRect = new Rectangle();
//
// These come from TitleBorder. NOTE that these are NOT final in
// TitledBorder
//
private static final int EDGE_SPACING = 2;
private static final int TEXT_SPACING = 2;
private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
// Prototype label for calculating baseline of tables.
private static JLabel TABLE_LABEL;
// Prototype label for calculating baseline of lists.
private static JLabel LIST_LABEL;
// Prototype label for calculating baseline of trees.
private static JLabel TREE_LABEL;
// Corresponds to com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel
private static Class CLASSIC_WINDOWS;
// Whether or not we've tried to load WindowsClassicLookAndFeel.
private static boolean checkedForClassic;
// Corresponds to com.sun.java.swing.plaf.windows.WindowsLookAndFeel
private static Class WINDOWS_CLASS;
// Whether we've tried to load WindowsLookAndFeel
private static boolean checkedForWindows;
// Whether or not we are running in a sandbox. This is used to determine
// how we should decide if we're using ocean.
private static boolean inSandbox;
// If in the sandbox, this is set after we've determine if using ocean.
private static boolean checkedForOcean;
// Whether or not using ocean. This is only used if inSandbox.
private static boolean usingOcean;
// Map
* A custom look and feel that wants to provide component2
relative to component1
.
* For example, the following returns the amount of space to place
* between component2
and component1
* when component2
is placed vertically above
* component1
:
*
* int gap = getPreferredGap(component1, component2,
* LayoutStyle.RELATED,
* SwingConstants.NORTH, parent);
*
* The type
parameter indicates the type
* of gap being requested. It can be one of the following values:
*
*
* RELATED
* If the two components will be contained in
* the same parent and are showing similar logically related
* items, use RELATED
.
* UNRELATED
* If the two components will be
* contained in the same parent but show logically unrelated items
* use UNRELATED
.
* INDENT
* Used to obtain the preferred distance to indent a component
* relative to another. For example, if you want to horizontally
* indent a JCheckBox relative to a JLabel use INDENT
.
* This is only useful for the horizontal axis.
* RELATED
and UNRELATED
.
* component2
or
* component1
. The return value may take into
* consideration various properties of the components. For
* example, the space may vary based on font size, or the preferred
* size of the component.
*
* @param component1 the JComponent
* component2
is being placed relative to
* @param component2 the JComponent
being placed
* @param type how the two components are being placed
* @param position the position component2
is being placed
* relative to component1
; one of
* SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
* @param parent the parent of component2
; this may differ
* from the actual parent and may be null
* @return the amount of space to place between the two components
* @throws IllegalArgumentException if position
is not
* one of SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
; type
not one
* of INDENT
, RELATED
* or UNRELATED
; or component1
or
* component2
is null
*/
public int getPreferredGap(JComponent component1, JComponent component2,
int type, int position, Container parent) {
// Check args
super.getPreferredGap(component1, component2, type, position, parent);
int result;
// Compute gap
if (type == INDENT) {
// Compute gap
if (position == SwingConstants.EAST || position == SwingConstants.WEST) {
int gap = getButtonChildIndent(component1, position);
if (gap != 0) {
return gap;
}
}
int sizeStyle = getSizeStyle(component1);
Insets gap1 = getPreferredGap(component1, type, sizeStyle);
switch (position) {
case SwingConstants.NORTH :
result = gap1.bottom;
break;
case SwingConstants.SOUTH :
result = gap1.top;
break;
case SwingConstants.EAST :
result = gap1.left;
break;
case SwingConstants.WEST :
default :
result = gap1.right;
break;
}
int raw = result;
// Compensate for visual margin
Insets visualMargin2 = getVisualMargin(component2);
switch (position) {
case SwingConstants.NORTH :
result -= visualMargin2.bottom;
break;
case SwingConstants.SOUTH :
result -= visualMargin2.top;
break;
case SwingConstants.EAST :
result -= visualMargin2.left;
break;
case SwingConstants.WEST :
result -= visualMargin2.right;
default :
break;
}
} else {
// Compute gap
int sizeStyle = Math.min(getSizeStyle(component1),
getSizeStyle(component2));
Insets gap1 = getPreferredGap(component1, type, sizeStyle);
Insets gap2 = getPreferredGap(component2, type, sizeStyle);
switch (position) {
case SwingConstants.NORTH :
result = Math.max(gap1.top, gap2.bottom);
break;
case SwingConstants.SOUTH :
result = Math.max(gap1.bottom, gap2.top);
break;
case SwingConstants.EAST :
result = Math.max(gap1.right, gap2.left);
break;
case SwingConstants.WEST :
default :
result = Math.max(gap1.left, gap2.right);
break;
}
// Compensate for visual margin
Insets visualMargin1 = getVisualMargin(component1);
Insets visualMargin2 = getVisualMargin(component2);
switch (position) {
case SwingConstants.NORTH :
result -= visualMargin1.top + visualMargin2.bottom;
break;
case SwingConstants.SOUTH :
result -= visualMargin1.bottom + visualMargin2.top;
break;
case SwingConstants.EAST :
result -= visualMargin1.right + visualMargin2.left;
break;
case SwingConstants.WEST :
result -= visualMargin1.left + visualMargin2.right;
default :
break;
}
}
// Aqua does not support negative gaps, because all its components are
// opaque
return Math.max(0, result);
}
private Insets getPreferredGap(JComponent component, int type, int sizeStyle) {
Map gapMap;
switch (type) {
case INDENT :
gapMap = INDENT_GAPS;
break;
case RELATED :
gapMap = RELATED_GAPS;
break;
case UNRELATED :
default :
gapMap = UNRELATED_GAPS;
break;
}
String uid = component.getUIClassID();
String style = null;
// == is ok here as Strings from Swing get interned, if for some reason
// need .equals then must deal with null.
if (uid == "ButtonUI" || uid =="ToggleButtonUI") {
style = (String) component.getClientProperty("JButton.buttonType");
} else if (uid =="ProgressBarUI") {
style = (((JProgressBar) component).getOrientation() ==
JProgressBar.HORIZONTAL) ? "horizontal" : "vertical";
} else if (uid == "SliderUI") {
style = (((JSlider) component).getOrientation()
== JProgressBar.HORIZONTAL) ? "horizontal" : "vertical";
} else if (uid == "TabbedPaneUI") {
switch (((JTabbedPane) component).getTabPlacement()) {
case JTabbedPane.TOP :
style = "top";
break;
case JTabbedPane.LEFT :
style = "left";
break;
case JTabbedPane.BOTTOM :
style = "bottom";
break;
case JTabbedPane.RIGHT :
style = "right";
break;
}
} else if (uid == "ComboBoxUI") {
style = ((JComboBox) component).isEditable() ? "editable" : "uneditable";
}
return getInsets(gapMap, uid, style, sizeStyle);
}
/**
* Returns the amount of space to position a component inside its
* parent.
*
* @param component the Component
being positioned
* @param position the position component
is being placed
* relative to its parent; one of
* SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
* @param parent the parent of component
; this may differ
* from the actual parent and may be null
* @return the amount of space to place between the component and specified
* edge
* @throws IllegalArgumentException if position
is not
* one of SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
;
* or component
is null
*/
public int getContainerGap(JComponent component, int position,
Container parent) {
int result;
int sizeStyle = Math.min(getSizeStyle(component), getSizeStyle(parent));
// Compute gap
Insets gap = getContainerGap(parent, sizeStyle);
switch (position) {
case SwingConstants.NORTH :
result = gap.top;
break;
case SwingConstants.SOUTH :
result = gap.bottom;
break;
case SwingConstants.EAST :
result = gap.right;
break;
case SwingConstants.WEST :
default :
result = gap.left;
break;
}
// Compensate for visual margin
Insets visualMargin = getVisualMargin(component);
switch (position) {
case SwingConstants.NORTH :
result -= visualMargin.top;
break;
case SwingConstants.SOUTH :
result -= visualMargin.bottom;
// Radio buttons in Quaqua are 1 pixel too high, in order
// to align their baselines with other components, when no
// baseline aware layout manager is used.
if (component instanceof JRadioButton) {
result--;
}
break;
case SwingConstants.EAST :
result -= visualMargin.left;
break;
case SwingConstants.WEST :
result -= visualMargin.right;
default :
break;
}
// Aqua does not support negative gaps, because all its components are
// opaque
return Math.max(0, result);
}
private Insets getContainerGap(Container container, int sizeStyle) {
String uid;
if (container instanceof JComponent) {
uid = ((JComponent) container).getUIClassID();
} else if (container instanceof Dialog) {
uid = "Dialog";
} else if (container instanceof Frame) {
uid = "Frame";
} else if (container instanceof java.applet.Applet) {
uid = "Applet";
} else if (container instanceof Panel) {
uid = "Panel";
} else {
uid = "default";
}
// FIXME insert style code here for JInternalFrame with palette style
return getInsets(CONTAINER_GAPS, uid, null, sizeStyle);
}
private Insets getInsets(Map gapMap, String uid, String style,
int sizeStyle) {
if (uid == null) {
uid = "default";
}
ComponentInsets componentInsets = (ComponentInsets)gapMap.get(uid);
if (componentInsets == null) {
componentInsets = (ComponentInsets)gapMap.get("default");
if (componentInsets == null) {
return EMPTY_INSETS;
}
} else if (style != null) {
ComponentInsets subInsets = componentInsets.getSubinsets(style);
if (subInsets != null) {
componentInsets = subInsets;
}
}
return componentInsets.getInsets(sizeStyle);
}
private Insets getVisualMargin(JComponent component) {
String uid = component.getUIClassID();
String style = null;
if (uid == "ButtonUI" || uid == "ToggleButtonUI") {
style = (String) component.getClientProperty("JButton.buttonType");
} else if (uid == "ProgressBarUI") {
style = (((JProgressBar) component).getOrientation()
== JProgressBar.HORIZONTAL) ? "horizontal" :
"vertical";
} else if (uid == "SliderUI") {
style = (((JSlider) component).getOrientation() ==
JProgressBar.HORIZONTAL) ? "horizontal"
: "vertical";
} else if (uid == "TabbedPaneUI") {
switch (((JTabbedPane) component).getTabPlacement()) {
case JTabbedPane.TOP :
style = "top";
break;
case JTabbedPane.LEFT :
style = "left";
break;
case JTabbedPane.BOTTOM :
style = "bottom";
break;
case JTabbedPane.RIGHT :
style = "right";
break;
}
}
Insets gap = getInsets(VISUAL_MARGINS, uid, style, 0);
// Take into account different positions of the button icon
if (uid == "RadioButtonUI" || uid == "CheckBoxUI") {
switch (((AbstractButton) component).getHorizontalTextPosition()) {
case SwingConstants.RIGHT :
gap = new Insets(gap.top, gap.right, gap.bottom, gap.left);
break;
case SwingConstants.CENTER :
gap = new Insets(gap.top, gap.right, gap.bottom, gap.right);
break;
/*
case SwingConstants.LEFT :
break;
*/
default:
gap = new Insets(gap.top, gap.left, gap.bottom, gap.right);
}
if (component.getBorder() instanceof EmptyBorder) {
gap.left -= 2;
gap.right -= 2;
gap.top -= 2;
gap.bottom -= 2;
}
}
return gap;
}
/**
* Returns the size style of a specified component.
*
* @return REGULAR, SMALL or MINI.
*/
private int getSizeStyle(Component c) {
// Aqua components have a different style depending on the
// font size used.
// 13 Point = Regular
// 11 Point = Small
// 9 Point = Mini
if (c == null) {
return REGULAR;
}
Font font = c.getFont();
if (font == null) {
return REGULAR;
}
int fontSize = font.getSize();
return (fontSize >= 13) ? REGULAR : ((fontSize > 9) ? SMALL : MINI);
}
/**
* ComponentInsets is used to manage the Insets for a specific Component
* type. Each ComponentInsets may also have children (sub) ComponentInsets.
* Subinsets are used to represent different styles a component may have.
* For example, a Button may not a set of insets, as well as insets when
* it has a style of metal.
*/
private static class ComponentInsets {
// MapgetBaseline
uses the
* following algorithm to determine the baseline:
*
*
* getBaseline(JComponent,int,int)
* method, invoke it.
* UIManager
property of the name
* Baseline.instance
, forward the call to that Baseline.
*
*
* JTextField
.
*/
private static int getSingleLineTextBaseline(JTextComponent textComponent,
int h) {
View rootView = textComponent.getUI().getRootView(textComponent);
if (rootView.getViewCount() > 0) {
Insets insets = textComponent.getInsets();
int height = h - insets.top - insets.bottom;
int y = insets.top;
View fieldView = rootView.getView(0);
int vspan = (int)fieldView.getPreferredSpan(View.Y_AXIS);
if (height != vspan) {
int slop = height - vspan;
y += slop / 2;
}
FontMetrics fm = textComponent.getFontMetrics(
textComponent.getFont());
y += fm.getAscent();
return y;
}
return -1;
}
/**
* Returns the baseline for buttons.
*/
private static int getButtonBaseline(AbstractButton button, int height) {
FontMetrics fm = button.getFontMetrics(button.getFont());
resetRects(button, height);
String text = button.getText();
if (text != null && text.startsWith("")) {
return -1;
}
// NOTE: that we use "a" here to make sure we get a valid value, if
// we were to pass in an empty string or null we would not get
// back the right thing.
SwingUtilities.layoutCompoundLabel(
button, fm, "a", button.getIcon(),
button.getVerticalAlignment(), button.getHorizontalAlignment(),
button.getVerticalTextPosition(),
button.getHorizontalTextPosition(),
viewRect, iconRect, textRect,
text == null ? 0 : button.getIconTextGap());
if (isAqua()) {
return textRect.y + fm.getAscent() + 1;
}
return textRect.y + fm.getAscent();
}
private static void resetRects(JComponent c, int height) {
Insets insets = c.getInsets();
viewRect.x = insets.left;
viewRect.y = insets.top;
viewRect.width = c.getWidth() - (insets.right + viewRect.x);
viewRect.height = height - (insets.bottom + viewRect.y);
textRect.x = textRect.y = textRect.width = textRect.height = 0;
iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;
}
private static boolean isOceanTheme() {
if (!inSandbox) {
try {
java.lang.reflect.Field field = MetalLookAndFeel.class.getDeclaredField("currentTheme");
field.setAccessible(true);
Object theme = field.get(null);
return "javax.swing.plaf.metal.OceanTheme".equals(theme.getClass().getName());
} catch (Exception ex) {
// We're in a sandbox and can't access the field
inSandbox = true;
}
}
if (!checkedForOcean) {
checkedForOcean = true;
checkForOcean();
}
return usingOcean;
}
private static void checkForOcean() {
String version = System.getProperty("java.specification.version");
int firstDot = version.indexOf('.');
String majorString;
String minorString;
if (firstDot != -1) {
majorString = version.substring(0, firstDot);
int secondDot = version.indexOf('.', firstDot + 1);
if (secondDot == -1) {
minorString = version.substring(firstDot + 1);
} else {
minorString = version.substring(firstDot + 1, secondDot);
}
} else {
majorString = version;
minorString = null;
}
try {
int majorVersion = Integer.parseInt(majorString);
int minorVersion = (minorString != null) ? Integer.parseInt(minorString) : 0;
usingOcean = (majorVersion > 1 || minorVersion > 4);
} catch (NumberFormatException nfe) {
}
}
private static boolean isWindows() {
return isWindows(UIManager.getLookAndFeel());
}
private static boolean isWindows(LookAndFeel laf) {
if (laf.getID() == "Windows") {
return true;
}
if (!checkedForWindows) {
try {
WINDOWS_CLASS = Class.forName(
"com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (ClassNotFoundException e) {
}
checkedForWindows = true;
}
return (WINDOWS_CLASS != null && WINDOWS_CLASS.isInstance(laf));
}
private static boolean isMetal() {
return isMetal(UIManager.getLookAndFeel());
}
private static boolean isMetal(LookAndFeel laf) {
return (laf.getID() == "Metal" || laf instanceof MetalLookAndFeel);
}
private static boolean isGTK() {
return UIManager.getLookAndFeel().getID() == "GTK";
}
private static boolean isAqua() {
return UIManager.getLookAndFeel().getID() == "Aqua";
}
private static boolean isXP() {
if (!checkedForClassic) {
try {
CLASSIC_WINDOWS = Class.forName(
"com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel");
} catch (ClassNotFoundException e) {
}
checkedForClassic = true;
}
if (CLASSIC_WINDOWS != null && CLASSIC_WINDOWS.
isInstance(UIManager.getLookAndFeel())) {
return false;
}
Toolkit toolkit = Toolkit.getDefaultToolkit();
Boolean themeActive = (Boolean)toolkit.getDesktopProperty(
"win.xpstyle.themeActive");
if (themeActive == null) {
themeActive = Boolean.FALSE;
}
return themeActive.booleanValue();
}
/**
* Creates an instance of Baseline. You typically don't create a
* Baseline. The constructor is provided by look and feels that wish
* to provide baseline support.
* Baseline
* support should put the instance in the defaults returned
* from getDefaults
. If you want to override the
* baseline suport for a look and feel place the instance in the defaults
* returned from UIManager.getLookAndFeelDefaults(). Tthis will ensure
* that if the look and feel changes the appropriate baseline can be used.
*/
protected Baseline() {
}
/**
* Returns the baseline for the specified component, or -1 if the
* baseline can not be determined. The baseline is measured from
* the top of the component.
*
* @param component JComponent to calculate baseline for
* @param width Width of the component to determine baseline for.
* @param height Height of the component to determine baseline for.
* @return baseline for the specified component
*/
public int getComponentBaseline(JComponent component, int width,
int height) {
return -1;
}
}
./swing-layout-1.0.4/releaseNotes.txt 0000644 0001750 0001750 00000010370 11271602230 016522 0 ustar yn158264 yn158264 Release notes for swing-layout (http://swing-layout.dev.java.net)
More information on each of the fixs can be found in the bug reports
at http://swing-layout.dev.java.net/servlets/ProjectIssues
Special Thanks to our External Contributors
-------------------------------------------
Werner Randelshofer
1.0.4 Release (2009/10/27)
--------------------------
44: getBaseline methods incorrectly cached
52: LayoutStyle should not delegate to core on Apple's JDK 6
1.0.3 Release (2007/10/4)
--------------------------
46: The contents of the VISUAL_MARGINS structure in AquaLayoutStyle can be
inadvertently modified.
47: Preferred gap between uneditable ComboBoxes is wrong. (Aqua look&feel)
Changed packaging of published ZIP file with sources to include the root folder
of the whole project.
1.0.2 Release (2007/2/5)
--------------------------
25: Visibility handling
37: Changing visibility of components may result in 0 sized gap
38: Support baseline alignment of vertical groups
43: Incorrect parameter passed to super() call
45: Preferred gaps are not calculated correctly for invisible components
1.0.1 Release (2006/6/7)
--------------------------
The following bugs have been fixed:
30: Adding then removing a component can result in an exception
31: Add a setLayoutStyle method to GroupLayout
32: Baseline should use instanceof
33: BASELINE and CENTER constants have the same value
40: AccessControlException when using GroupLayout in an applet
42: ComponentOrientation not honored
WARNING: The fix for issue 33 (BASELINE and CENTER constants have the
same value) has introduced an incompatibility. Prior to fixing 33 both
constants BASELINE and CENTER had the same value. Because java copies
the values for integer constants into the byte codes it means if you
do not recompile with 1.0.1 you may encounter problems. The easy way
to fix the problem is recompile with 1.0.1 and all will be well.
1.0 Release (2005/12/07)
--------------------------
In anticipation of NetBeans 5.0 swing-layout has reached 1.0! The API
will not incompatability change.
The following bugs have been fixed:
29: replace only works after you've done an initial layout
0.9.5 Release (2005/12/02)
--------------------------
The following bugs have been fixed:
22: LayoutStyle should call into Swing's LayoutStyle when runnin
23: Have better defaults for look and feels not supported by LayoutStyle
24: src.zip should include other files
28: GroupLayout doesn't cleanup after removing a component
0.9 Release (2005/11/03)
--------------------------
This is primarily a bug fix release.
The following bugs have been fixed:
18: dump should be renamed to toString
19: Baseline for sliders on OS X is wrong
20: There is no LayoutStyle for OS X
21: GroupLayout should allow for negative gaps
0.7.1 Release (2005/10/03)
--------------------------
This is primarily a bug fix release.
The following bugs have been fixed:
16: Padding incorrectly calculated in certain situations
17: Components that have their sized linked do not resize correctly
0.7 Release (2005/08/30)
--------------------------
This is primarily a bug fix release, although it does contain a bit of
new API. The biggest addition is baseline support for OS X.
The following bugs have been fixed:
4: Need ability to plug in custom instance of Baseline
15: Add baseline support for OS X
0.6.1 Release (2005/08/17)
--------------------------
This is primarily a bug fix release.
The following bugs have been fixed:
1: Sample code in class description not working
2: Error message if component not in vertical and horizontal gr
6: Autopadding not properly computed
7: Odd resizing behavior
8: GroupLayout should work with Container and Components
9: Having to specify JComponent to LayoutStyle results in lots
10: Child indent should take into consideration icons for checkb
11: noop intructions.
12: ContainerAutopadding incorrectly calculated for non-JCompone
13: Indent padding for checkboxs/radiobuttons incorrect
14: ContainerAutopadding incorrectly calculated in certain cases
And the following Enhancements have been fixed:
3: Need ability to provide baseline for custom components
5: Add ability to get preferred gap for indenting components
./swing-layout-1.0.4/COPYING 0000644 0001750 0001750 00000065564 10441546106 014411 0 ustar yn158264 yn158264 Copyright (c) 2005-2006 Sun Microsystems, Inc., 4150 Network Circle, Santa
Clara, California 95054, U.S.A. All rights reserved. Use is subject
to license terms below. Sun, Sun Microsystems and the Sun logo are
trademarks or registered trademarks of Sun Microsystems, Inc. in the
U.S. and other countries.
Notice: This product is covered by U.S. export control laws and may be
subject to the export or import laws in other countries. These laws may
restrict the fields of use for this software and may require you to
secure government authorization.
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.