0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.jdesktop.layout;
0043:
0044: import java.awt.Component;
0045: import java.awt.Container;
0046: import java.awt.Dimension;
0047: import java.awt.Insets;
0048: import java.awt.LayoutManager2;
0049: import java.awt.Toolkit;
0050: import java.util.*;
0051:
0052: /**
0053: * GroupLayout is a LayoutManager that hierarchically groups components to
0054: * achieve common, and not so common, layouts. Grouping is done by instances
0055: * of the Group class. GroupLayout supports two types of groups:
0056: * <table>
0057: * <tr><td valign=top>Sequential:<td>A sequential group positions its child
0058: * elements sequentially, one after another.
0059: * <tr><td valign=top>Parallel:<td>A parallel group positions its child
0060: * elements in the same space on top of each other. Parallel groups
0061: * can also align the child elements along their baseline.
0062: * </table>
0063: * Each Group can contain any number of child groups, Components or gaps.
0064: * GroupLayout treats each axis independently. That is, there is a group
0065: * representing the horizontal axis, and a separate group representing the
0066: * vertical axis. The horizontal group is responsible for setting the x
0067: * and width of its contents, where as the vertical group is responsible for
0068: * setting the y and height of its contents.
0069: * <p>
0070: * The following code builds a simple layout consisting of two labels in
0071: * one column, followed by two textfields in the next column:
0072: * <pre>
0073: * JComponent panel = ...;
0074: * GroupLayout layout = new GroupLayout(panel);
0075: * panel.setLayout(layout);
0076: * layout.setAutocreateGaps(true);
0077: * layout.setAutocreateContainerGaps(true);
0078: * GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
0079: * hGroup.add(layout.createParallelGroup().add(label1).add(label2)).
0080: * add(layout.createParallelGroup().add(tf1).add(tf2));
0081: * layout.setHorizontalGroup(hGroup);
0082: * GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
0083: * vGroup.add(layout.createParallelGroup(GroupLayout.BASELINE).add(label1).add(tf1)).
0084: * add(layout.createParallelGroup(GroupLayout.BASELINE).add(label2).add(tf2));
0085: * layout.setVerticalGroup(vGroup);
0086: * </pre>
0087: * <p>
0088: * This layout consists of the following:
0089: * <ul><li>The horizontal axis consists of a sequential group containing two
0090: * parallel groups. The first parallel group consists of the labels,
0091: * with the second parallel group consisting of the text fields.
0092: * <li>The vertical axis similarly consists of a sequential group
0093: * containing two parallel groups. The parallel groups align their
0094: * contents along the baseline. The first parallel group consists
0095: * of the first label and text field, and the second group consists
0096: * of the second label and text field.
0097: * </ul>
0098: * There are a couple of things to notice in this code:
0099: * <ul>
0100: * <li>You need not explicitly add the components to the container, this
0101: * is indirectly done by using one of the <code>add</code> methods.
0102: * <li>The various <code>add</code> methods of <code>Groups</code> return
0103: * themselves. This allows for easy chaining of invocations. For
0104: * example, <code>group.add(label1).add(label2);</code> is equivalent to
0105: * <code>group.add(label1);group.add(label2);</code>.
0106: * <li>There are no public constructors for the Groups, instead
0107: * use the create methods of <code>GroupLayout</code>.
0108: * </ul>
0109: * GroupLayout offer the ability to automatically insert the appropriate gap
0110: * between components. This can be turned on using the
0111: * <code>setAutocreateGaps()</code> method. Similarly you can use
0112: * the <code>setAutocreateContainerGaps()</code> method to insert gaps
0113: * between the components and the container.
0114: *
0115: * @version $Revision$
0116: * @author Tomas Pavek
0117: * @author Jan Stola
0118: * @author Scott Violet
0119: */
0120: public class GroupLayout implements LayoutManager2 {
0121: // Used in size calculations
0122: private static final int MIN_SIZE = 0;
0123: private static final int PREF_SIZE = 1;
0124: private static final int MAX_SIZE = 2;
0125:
0126: private static final int UNSET = Integer.MIN_VALUE;
0127:
0128: /**
0129: * Possible argument when linking sizes of components. Specifies the
0130: * the two component should share the same size along the horizontal
0131: * axis.
0132: *
0133: * @see #linkSize(java.awt.Component[],int)
0134: */
0135: public static final int HORIZONTAL = 1;
0136:
0137: /**
0138: * Possible argument when linking sizes of components. Specifies the
0139: * the two component should share the same size along the vertical
0140: * axis.
0141: *
0142: * @see #linkSize(java.awt.Component[],int)
0143: */
0144: public static final int VERTICAL = 2;
0145:
0146: private static final int NO_ALIGNMENT = 0;
0147: /**
0148: * Possible alignment type. Indicates the elements should be
0149: * aligned to the origin. For the horizontal axis with a left to
0150: * right orientation this means aligned to the left.
0151: *
0152: * @see #createParallelGroup(int)
0153: */
0154: public static final int LEADING = 1;
0155: /**
0156: * Possible alignment type. Indicates the elements should be
0157: * aligned to the end. For the horizontal axis with a left to
0158: * right orientation this means aligned to the right.
0159: *
0160: * @see #createParallelGroup(int)
0161: */
0162: public static final int TRAILING = 2;
0163: /**
0164: * Possible alignment type. Indicates the elements should centered in
0165: * the spaced provided.
0166: *
0167: * @see #createParallelGroup(int)
0168: */
0169: public static final int CENTER = 3;
0170: /**
0171: * Possible alignment type. Indicates the elements should aligned along
0172: * their baseline.
0173: *
0174: * @see #createParallelGroup(int)
0175: */
0176: public static final int BASELINE = 3;
0177:
0178: /**
0179: * Possible value for the add methods that takes a Component.
0180: * Indicates the size from the component should be used.
0181: */
0182: public static final int DEFAULT_SIZE = -1;
0183: /**
0184: * Possible value for the add methods that takes a Component.
0185: * Indicates the preferred size should be used.
0186: */
0187: public static final int PREFERRED_SIZE = -2;
0188:
0189: // Whether or not we automatically try and create the preferred
0190: // padding between components.
0191: private boolean autocreatePadding;
0192:
0193: // Whether or not we automatically try and create the preferred
0194: // padding between containers
0195: private boolean autocreateContainerPadding;
0196:
0197: /**
0198: * Group responsible for layout along the horizontal axis. This is NOT
0199: * the user specified group, use getHorizontalGroup to dig that out.
0200: */
0201: private Group horizontalGroup;
0202: /**
0203: * Group responsible for layout along the vertical axis. This is NOT
0204: * the user specified group, use getVerticalGroup to dig that out.
0205: */
0206: private Group verticalGroup;
0207:
0208: // Maps from Component to ComponentInfo. This is used for tracking
0209: // information specific to a Component.
0210: private Map componentInfos;
0211:
0212: // Container we're doing layout for.
0213: private Container host;
0214:
0215: // Used by areParallelSiblings, cached to avoid excessive garbage.
0216: private List parallelList;
0217:
0218: // Indicates Springs have been added since last change.
0219: private boolean springsAdded;
0220:
0221: // Whether or not any preferred padding (or container padding) springs exist
0222: private boolean hasPreferredPaddingSprings;
0223:
0224: private static void checkSize(int min, int pref, int max,
0225: boolean isComponentSpring) {
0226: checkResizeType(min, isComponentSpring);
0227: if (!isComponentSpring && pref < 0) {
0228: throw new IllegalArgumentException("Pref must be >= 0");
0229: }
0230: checkResizeType(max, isComponentSpring);
0231: checkLessThan(min, pref);
0232: checkLessThan(min, max);
0233: checkLessThan(pref, max);
0234: }
0235:
0236: private static void checkResizeType(int type,
0237: boolean isComponentSpring) {
0238: if (type < 0
0239: && ((isComponentSpring && type != DEFAULT_SIZE && type != PREFERRED_SIZE) || (!isComponentSpring && type != PREFERRED_SIZE))) {
0240: throw new IllegalArgumentException("Invalid size");
0241: }
0242: }
0243:
0244: private static void checkLessThan(int min, int max) {
0245: if (min >= 0 && max >= 0 && min > max) {
0246: throw new IllegalArgumentException(
0247: "Following is not met: min<=pref<=max");
0248: }
0249: }
0250:
0251: // Makes sure the alignment is one of the known values.
0252: private static void checkAlignment(int alignment,
0253: boolean allowsBaseline) {
0254: if (alignment == LEADING || alignment == TRAILING
0255: || alignment == CENTER) {
0256: return;
0257: }
0258: if (allowsBaseline && alignment != BASELINE) {
0259: throw new IllegalArgumentException(
0260: "Alignment must be one of:"
0261: + "LEADING, TRAILING, CENTER or BASELINE");
0262: }
0263: throw new IllegalArgumentException("Alignment must be one of:"
0264: + "LEADING, TRAILING or CENTER");
0265: }
0266:
0267: /**
0268: * Creates a GroupLayout for the specified JComponent.
0269: *
0270: * @param host the Container to layout
0271: * @throws IllegalArgumentException if host is null
0272: */
0273: public GroupLayout(Container host) {
0274: if (host == null) {
0275: throw new IllegalArgumentException(
0276: "Container must be non-null");
0277: }
0278: this .host = host;
0279: setHorizontalGroup(createParallelGroup(LEADING, true));
0280: setVerticalGroup(createParallelGroup(LEADING, true));
0281: componentInfos = new HashMap();
0282: autocreatePadding = false;
0283: parallelList = new ArrayList();
0284: }
0285:
0286: /**
0287: * Returns a textual description of this GroupLayout. The return value
0288: * is intended for debugging purposes only.
0289: *
0290: * @return textual description of this GroupLayout
0291: **/
0292: public String toString() {
0293: StringBuffer buffer = new StringBuffer();
0294: buffer.append("HORIZONTAL\n");
0295: dump(buffer, horizontalGroup, " ", HORIZONTAL);
0296: buffer.append("\nVERTICAL\n");
0297: dump(buffer, verticalGroup, " ", VERTICAL);
0298: return buffer.toString();
0299: }
0300:
0301: private void dump(StringBuffer buffer, Spring spring,
0302: String indent, int axis) {
0303: String origin = "";
0304: String padding = "";
0305: if (spring instanceof ComponentSpring) {
0306: ComponentSpring cSpring = (ComponentSpring) spring;
0307: origin = Integer.toString(cSpring.getOrigin()) + " ";
0308: String name = cSpring.getComponent().getName();
0309: if (name != null) {
0310: origin = "name=" + name + ", ";
0311: }
0312: }
0313: if (spring instanceof AutopaddingSpring) {
0314: AutopaddingSpring paddingSpring = (AutopaddingSpring) spring;
0315: padding = ", userCreated=" + paddingSpring.getUserCreated()
0316: + ", matches="
0317: + paddingSpring.getMatchDescription();
0318: }
0319: buffer.append(indent + spring.getClass().getName() + " "
0320: + Integer.toHexString(spring.hashCode()) + " " + origin
0321: + ", size=" + spring.getSize() + ", alignment="
0322: + spring.getAlignment() + " prefs=["
0323: + spring.getMinimumSize(axis) + " "
0324: + spring.getPreferredSize(axis) + " "
0325: + spring.getMaximumSize(axis) + padding + "]\n");
0326: if (spring instanceof Group) {
0327: List springs = ((Group) spring).springs;
0328: indent += " ";
0329: for (int counter = 0; counter < springs.size(); counter++) {
0330: dump(buffer, (Spring) springs.get(counter), indent,
0331: axis);
0332: }
0333: }
0334: }
0335:
0336: /**
0337: * Sets whether or not a gap between components
0338: * should automatically be created. For example, if this is true
0339: * and you add two components to a <code>SequentialGroup</code> a
0340: * gap between the two will automatically be created. The default
0341: * is false.
0342: *
0343: * @param autocreatePadding whether or not to automatically created a gap
0344: * between components and the container
0345: */
0346: public void setAutocreateGaps(boolean autocreatePadding) {
0347: this .autocreatePadding = autocreatePadding;
0348: }
0349:
0350: /**
0351: * Returns true if gaps between components are automatically be created.
0352: *
0353: * @return true if gaps between components should automatically be created
0354: */
0355: public boolean getAutocreateGaps() {
0356: return autocreatePadding;
0357: }
0358:
0359: /**
0360: * Sets whether or not gaps between the container and the first/last
0361: * components should automatically be created. The default
0362: * is false.
0363: *
0364: * @param autocreatePadding whether or not to automatically create
0365: * gaps between the container and first/last components.
0366: */
0367: public void setAutocreateContainerGaps(boolean autocreatePadding) {
0368: if (autocreatePadding != autocreateContainerPadding) {
0369: autocreateContainerPadding = autocreatePadding;
0370: horizontalGroup = createTopLevelGroup(getHorizontalGroup());
0371: verticalGroup = createTopLevelGroup(getVerticalGroup());
0372: }
0373: }
0374:
0375: /**
0376: * Returns whether or not gaps between the container and the
0377: * first/last components should automatically be created. The default
0378: * is false.
0379: *
0380: * @return whether or not the gaps between the container and the
0381: * first/last components should automatically be created
0382: */
0383: public boolean getAutocreateContainerGaps() {
0384: return autocreateContainerPadding;
0385: }
0386:
0387: /**
0388: * Sets the <code>Group</code> that is responsible for
0389: * layout along the horizontal axis.
0390: *
0391: * @param group <code>Group</code> responsible for layout along
0392: * the horizontal axis
0393: * @throws IllegalArgumentException if group is null
0394: */
0395: public void setHorizontalGroup(Group group) {
0396: if (group == null) {
0397: throw new IllegalArgumentException("Group must be non-null");
0398: }
0399: horizontalGroup = createTopLevelGroup(group);
0400: }
0401:
0402: /**
0403: * Returns the <code>Group</code> that is responsible for
0404: * layout along the horizontal axis.
0405: *
0406: * @return <code>ParallelGroup</code> responsible for layout along
0407: * the horizontal axis.
0408: */
0409: public Group getHorizontalGroup() {
0410: int index = 0;
0411: if (horizontalGroup.springs.size() > 1) {
0412: index = 1;
0413: }
0414: return (Group) horizontalGroup.springs.get(index);
0415: }
0416:
0417: /**
0418: * Sets the <code>Group</code> that is responsible for
0419: * layout along the vertical axis.
0420: *
0421: * @param group <code>Group</code> responsible for layout along
0422: * the vertical axis.
0423: * @throws IllegalArgumentException if group is null.
0424: */
0425: public void setVerticalGroup(Group group) {
0426: if (group == null) {
0427: throw new IllegalArgumentException("Group must be non-null");
0428: }
0429: verticalGroup = createTopLevelGroup(group);
0430: }
0431:
0432: /**
0433: * Returns the <code>ParallelGroup</code> that is responsible for
0434: * layout along the vertical axis.
0435: *
0436: * @return <code>ParallelGroup</code> responsible for layout along
0437: * the vertical axis.
0438: */
0439: public Group getVerticalGroup() {
0440: int index = 0;
0441: if (verticalGroup.springs.size() > 1) {
0442: index = 1;
0443: }
0444: return (Group) verticalGroup.springs.get(index);
0445: }
0446:
0447: /**
0448: * Wraps the user specified group in a sequential group. If
0449: * container gaps should be generate the necessary springs are
0450: * added.
0451: */
0452: private Group createTopLevelGroup(Group specifiedGroup) {
0453: SequentialGroup group = createSequentialGroup();
0454: if (getAutocreateContainerGaps()) {
0455: group.addSpring(new ContainerAutopaddingSpring());
0456: group.add(specifiedGroup);
0457: group.addSpring(new ContainerAutopaddingSpring());
0458: } else {
0459: group.add(specifiedGroup);
0460: }
0461: return group;
0462: }
0463:
0464: /**
0465: * Creates and returns a <code>SequentialGroup</code>.
0466: *
0467: * @return a new <code>SequentialGroup</code>
0468: */
0469: public SequentialGroup createSequentialGroup() {
0470: return new SequentialGroup();
0471: }
0472:
0473: /**
0474: * Creates and returns a <code>ParallelGroup</code> with a
0475: * <code>LEADING</code> alignment. This is a cover method for the more
0476: * general <code>createParallelGroup(int)</code> method.
0477: *
0478: * @return a new ParallelGroup
0479: * @see #createParallelGroup(int)
0480: */
0481: public ParallelGroup createParallelGroup() {
0482: return createParallelGroup(LEADING);
0483: }
0484:
0485: /**
0486: * Creates and returns an <code>ParallelGroup</code>. The alignment
0487: * specifies how children elements should be positioned when the
0488: * the parallel group is given more space than necessary. For example,
0489: * if a ParallelGroup with an alignment of TRAILING is given 100 pixels
0490: * and a child only needs 50 pixels, the child will be positioned at the
0491: * position 50.
0492: *
0493: * @param alignment alignment for the elements of the Group, one
0494: * of <code>LEADING</code>, <code>TRAILING</code>,
0495: * <code>CENTER</code> or <code>BASELINE</code>.
0496: * @throws IllegalArgumentException if alignment is not one of
0497: * <code>LEADING</code>, <code>TRAILING</code>,
0498: * <code>CENTER</code> or <code>BASELINE</code>
0499: * @return a new <code>ParallelGroup</code>
0500: */
0501: public ParallelGroup createParallelGroup(int alignment) {
0502: return createParallelGroup(alignment, true);
0503: }
0504:
0505: /**
0506: * Creates and returns an <code>ParallelGroup</code>. The alignment
0507: * specifies how children elements should be positioned when the
0508: * the parallel group is given more space than necessary. For example,
0509: * if a ParallelGroup with an alignment of TRAILING is given 100 pixels
0510: * and a child only needs 50 pixels, the child will be positioned at the
0511: * position 50.
0512: *
0513: * @param alignment alignment for the elements of the Group, one
0514: * of <code>LEADING</code>, <code>TRAILING</code>,
0515: * <code>CENTER</code> or <code>BASELINE</code>.
0516: * @param resizable whether or not the group is resizable. If the group
0517: * is not resizable the min/max size will be the same as the
0518: * preferred.
0519: * @throws IllegalArgumentException if alignment is not one of
0520: * <code>LEADING</code>, <code>TRAILING</code>,
0521: * <code>CENTER</code> or <code>BASELINE</code>
0522: * @return a new <code>ParallelGroup</code>
0523: */
0524: public ParallelGroup createParallelGroup(int alignment,
0525: boolean resizable) {
0526: if (alignment == BASELINE) {
0527: return new BaselineGroup(resizable);
0528: }
0529: return new ParallelGroup(alignment, resizable);
0530: }
0531:
0532: /**
0533: * Forces the set of components to have the same size.
0534: * This can be used multiple times to force
0535: * any number of components to share the same size.
0536: * <p>
0537: * Linked Components are not be resizable.
0538: *
0539: * @param components Components to force to have same size.
0540: * @throws IllegalArgumentException if <code>components</code> is
0541: * null, or contains null.
0542: */
0543: public void linkSize(Component[] components) {
0544: linkSize(components, HORIZONTAL | VERTICAL);
0545: }
0546:
0547: /**
0548: * Forces the set of components to have the same size.
0549: * This can be used multiple times to force
0550: * any number of components to share the same size.
0551: * <p>
0552: * Linked Components are not be resizable.
0553: *
0554: * @param components Components to force to have same size.
0555: * @param axis Axis to bind size, one of HORIZONTAL, VERTICAL or
0556: * HORIZONTAL | VERTICAL
0557: * @throws IllegalArgumentException if <code>components</code> is
0558: * null, or contains null.
0559: * @throws IllegalArgumentException if <code>axis</code> does not
0560: * contain <code>HORIZONTAL</code> or <code>VERTICAL</code>
0561: */
0562: public void linkSize(Component[] components, int axis) {
0563: if (components == null) {
0564: throw new IllegalArgumentException(
0565: "Components must be non-null");
0566: }
0567: boolean horizontal = ((axis & HORIZONTAL) == HORIZONTAL);
0568: boolean vertical = ((axis & VERTICAL) == VERTICAL);
0569: if (!vertical && !horizontal) {
0570: throw new IllegalArgumentException(
0571: "Axis must contain HORIZONTAL or VERTICAL");
0572: }
0573: for (int counter = components.length - 1; counter >= 0; counter--) {
0574: Component c = components[counter];
0575: if (components[counter] == null) {
0576: throw new IllegalArgumentException(
0577: "Components must be non-null");
0578: }
0579: // Force the component to be added
0580: getComponentInfo(c);
0581: }
0582: if (horizontal) {
0583: linkSize0(components, HORIZONTAL);
0584: }
0585: if (vertical) {
0586: linkSize0(components, VERTICAL);
0587: }
0588: }
0589:
0590: private void linkSize0(Component[] components, int axis) {
0591: ComponentInfo master = getComponentInfo(
0592: components[components.length - 1])
0593: .getMasterComponentInfo(axis);
0594: for (int counter = components.length - 2; counter >= 0; counter--) {
0595: master
0596: .addChild(getComponentInfo(components[counter]),
0597: axis);
0598: }
0599: }
0600:
0601: /**
0602: * Removes an existing component replacing it with the specified component.
0603: *
0604: * @param existingComponent the Component that should be removed and
0605: * replaced with newComponent
0606: * @param newComponent the Component to put in existingComponents place
0607: * @throws IllegalArgumentException is either of the Components are null or
0608: * if existingComponent is not being managed by this layout manager
0609: */
0610: public void replace(Component existingComponent,
0611: Component newComponent) {
0612: if (existingComponent == null || newComponent == null) {
0613: throw new IllegalArgumentException(
0614: "Components must be non-null");
0615: }
0616: ComponentInfo info = (ComponentInfo) componentInfos
0617: .remove(existingComponent);
0618: if (info == null) {
0619: throw new IllegalArgumentException(
0620: "Component must already exist");
0621: }
0622: host.remove(existingComponent);
0623: host.add(newComponent);
0624: info.setComponent(newComponent);
0625: componentInfos.put(newComponent, info);
0626: invalidateLayout(host);
0627: }
0628:
0629: //
0630: // LayoutManager
0631: //
0632: /**
0633: * Notification that a <code>Component</code> has been added to
0634: * the parent container. Developers should not invoke this method
0635: * directly, instead you should use one of the <code>Group</code>
0636: * methods to add a <code>Component</code>.
0637: *
0638: * @param name the string to be associated with the component
0639: * @param component the <code>Component</code> to be added
0640: */
0641: public void addLayoutComponent(String name, Component component) {
0642: }
0643:
0644: /**
0645: * Notification that a <code>Component</code> has been removed from
0646: * the parent container. You should not invoke this method
0647: * directly, instead invoke <code>remove</code> on the parent
0648: * <code>Container</code>.
0649: *
0650: * @param comp the component to be removed
0651: * @see java.awt.Component#remove
0652: */
0653: public void removeLayoutComponent(Component comp) {
0654: }
0655:
0656: /**
0657: * Returns the preferred size for the specified container.
0658: *
0659: * @param parent the container to return size for
0660: * @throws IllegalArgumentException if <code>parent</code> is not
0661: * the same <code>Container</code> that this was created with
0662: * @throws IllegalStateException if any of the components added to
0663: * this layout are not in both a horizontal and vertical group
0664: * @see java.awt.Container#getPreferredSize
0665: */
0666: public Dimension preferredLayoutSize(Container parent) {
0667: checkParent(parent);
0668: prepare(PREF_SIZE);
0669: return adjustSize(horizontalGroup.getPreferredSize(HORIZONTAL),
0670: verticalGroup.getPreferredSize(VERTICAL));
0671: }
0672:
0673: /**
0674: * Returns the minimum size for the specified container.
0675: *
0676: * @param parent the container to return size for
0677: * @throws IllegalArgumentException if <code>parent</code> is not
0678: * the same <code>Container</code> that this was created with
0679: * @throws IllegalStateException if any of the components added to
0680: * this layout are not in both a horizontal and vertical group
0681: * @see java.awt.Container#getMinimumSize
0682: */
0683: public Dimension minimumLayoutSize(Container parent) {
0684: checkParent(parent);
0685: prepare(MIN_SIZE);
0686: return adjustSize(horizontalGroup.getMinimumSize(HORIZONTAL),
0687: verticalGroup.getMinimumSize(VERTICAL));
0688: }
0689:
0690: /**
0691: * Lays out the specified container.
0692: *
0693: * @param parent the container to be laid out
0694: * @throws IllegalStateException if any of the components added to
0695: * this layout are not in both a horizontal and vertical group
0696: */
0697: public void layoutContainer(Container parent) {
0698: prepare();
0699: Insets insets = parent.getInsets();
0700:
0701: if (getAutocreateGaps() || getAutocreateContainerGaps()
0702: || hasPreferredPaddingSprings) {
0703: resetAutopadding(horizontalGroup, HORIZONTAL, -1, 0, parent
0704: .getWidth()
0705: - insets.left - insets.right);
0706: resetAutopadding(verticalGroup, VERTICAL, -1, 0, parent
0707: .getHeight()
0708: - insets.top - insets.bottom);
0709: }
0710: horizontalGroup.setSize(HORIZONTAL, 0, parent.getWidth()
0711: - insets.left - insets.right);
0712: verticalGroup.setSize(VERTICAL, 0, parent.getHeight()
0713: - insets.top - insets.bottom);
0714:
0715: Iterator componentInfo = componentInfos.values().iterator();
0716: while (componentInfo.hasNext()) {
0717: ComponentInfo info = (ComponentInfo) componentInfo.next();
0718: Component c = info.getComponent();
0719: info.setBounds(insets);
0720: }
0721: }
0722:
0723: //
0724: // LayoutManager2
0725: //
0726: /**
0727: * Notification that a <code>Component</code> has been added to
0728: * the parent container. You should not invoke this method
0729: * directly, instead you should use one of the <code>Group</code>
0730: * methods to add a <code>Component</code>.
0731: *
0732: * @param component The component added
0733: * @param constraints Description of where to place the component.
0734: */
0735: public void addLayoutComponent(Component component,
0736: Object constraints) {
0737: }
0738:
0739: /**
0740: * Returns the maximum size for the specified container.
0741: *
0742: * @param parent the container to return size for
0743: * @throws IllegalArgumentException if <code>parent</code> is not
0744: * the same <code>Container</code> that this was created with
0745: * @throws IllegalStateException if any of the components added to
0746: * this layout are not in both a horizontal and vertical group
0747: * @see java.awt.Container#getMaximumSize
0748: */
0749: public Dimension maximumLayoutSize(Container parent) {
0750: checkParent(parent);
0751: prepare(MAX_SIZE);
0752: return adjustSize(horizontalGroup.getMaximumSize(HORIZONTAL),
0753: verticalGroup.getMaximumSize(VERTICAL));
0754: }
0755:
0756: /**
0757: * Returns the alignment along the x axis. This specifies how
0758: * the component would like to be aligned relative to other
0759: * components. The value should be a number between 0 and 1
0760: * where 0 represents alignment along the origin, 1 is aligned
0761: * the furthest away from the origin, 0.5 is centered, etc.
0762: *
0763: * @param parent Container hosting this LayoutManager
0764: * @throws IllegalArgumentException if <code>parent</code> is not
0765: * the same <code>Container</code> that this was created with
0766: * @return alignment
0767: */
0768: public float getLayoutAlignmentX(Container parent) {
0769: checkParent(parent);
0770: return .5f;
0771: }
0772:
0773: /**
0774: * Returns the alignment along the y axis. This specifies how
0775: * the component would like to be aligned relative to other
0776: * components. The value should be a number between 0 and 1
0777: * where 0 represents alignment along the origin, 1 is aligned
0778: * the furthest away from the origin, 0.5 is centered, etc.
0779: *
0780: * @param parent Container hosting this LayoutManager
0781: * @throws IllegalArgumentException if <code>parent</code> is not
0782: * the same <code>Container</code> that this was created with
0783: * @return alignment
0784: */
0785: public float getLayoutAlignmentY(Container parent) {
0786: checkParent(parent);
0787: return .5f;
0788: }
0789:
0790: /**
0791: * Invalidates the layout, indicating that if the layout manager
0792: * has cached information it should be discarded.
0793: *
0794: * @param parent Container hosting this LayoutManager
0795: * @throws IllegalArgumentException if <code>parent</code> is not
0796: * the same <code>Container</code> that this was created with
0797: */
0798: public void invalidateLayout(Container parent) {
0799: checkParent(parent);
0800: // invalidateLayout is called from Container.invalidate, which
0801: // does NOT grab the treelock. All other methods do. To make sure
0802: // there aren't any possible threading problems we grab the tree lock
0803: // here.
0804: synchronized (parent.getTreeLock()) {
0805: horizontalGroup.setSize(HORIZONTAL, UNSET, UNSET);
0806: verticalGroup.setSize(VERTICAL, UNSET, UNSET);
0807: for (Iterator cis = componentInfos.values().iterator(); cis
0808: .hasNext();) {
0809: ComponentInfo ci = (ComponentInfo) cis.next();
0810: ci.clear();
0811: }
0812: }
0813: }
0814:
0815: private void resetAutopadding(Group group, int axis, int sizeType,
0816: int origin, int size) {
0817: group.resetAutopadding();
0818: switch (sizeType) {
0819: case MIN_SIZE:
0820: size = group.getMinimumSize(axis);
0821: break;
0822: case PREF_SIZE:
0823: size = group.getPreferredSize(axis);
0824: break;
0825: case MAX_SIZE:
0826: size = group.getMaximumSize(axis);
0827: break;
0828: }
0829: group.setSize(axis, origin, size);
0830: group.calculateAutopadding(axis);
0831: }
0832:
0833: private void prepare(int sizeType) {
0834: prepare();
0835: if (getAutocreateGaps() || getAutocreateContainerGaps()
0836: || hasPreferredPaddingSprings) {
0837: resetAutopadding(horizontalGroup, HORIZONTAL, sizeType, 0,
0838: 0);
0839: resetAutopadding(verticalGroup, VERTICAL, sizeType, 0, 0);
0840: }
0841:
0842: }
0843:
0844: private void prepare() {
0845: if (springsAdded) {
0846: registerComponents(horizontalGroup, HORIZONTAL);
0847: registerComponents(verticalGroup, VERTICAL);
0848: checkComponents();
0849: horizontalGroup.removeAutopadding();
0850: verticalGroup.removeAutopadding();
0851: if (getAutocreateGaps()) {
0852: adjustAutopadding(true);
0853: } else if (hasPreferredPaddingSprings
0854: || getAutocreateContainerGaps()) {
0855: adjustAutopadding(false);
0856: }
0857: springsAdded = false;
0858: }
0859: }
0860:
0861: private void checkComponents() {
0862: Iterator infos = componentInfos.values().iterator();
0863: while (infos.hasNext()) {
0864: ComponentInfo info = (ComponentInfo) infos.next();
0865: if (info.horizontalSpring == null) {
0866: throw new IllegalStateException(info.component
0867: + " is not attached to a horizontal group");
0868: }
0869: if (info.verticalSpring == null) {
0870: throw new IllegalStateException(info.component
0871: + " is not attached to a vertical group");
0872: }
0873: }
0874: }
0875:
0876: private void registerComponents(Group group, int axis) {
0877: List springs = group.springs;
0878: for (int counter = springs.size() - 1; counter >= 0; counter--) {
0879: Spring spring = (Spring) springs.get(counter);
0880: if (spring instanceof ComponentSpring) {
0881: ((ComponentSpring) spring).installIfNecessary(axis);
0882: } else if (spring instanceof Group) {
0883: registerComponents((Group) spring, axis);
0884: }
0885: }
0886: }
0887:
0888: private Dimension adjustSize(int width, int height) {
0889: Insets insets = host.getInsets();
0890: return new Dimension(width + insets.left + insets.right, height
0891: + insets.top + insets.bottom);
0892: }
0893:
0894: private void checkParent(Container parent) {
0895: if (parent != host) {
0896: throw new IllegalArgumentException(
0897: "GroupLayout can only be used with one Container at a time");
0898: }
0899: }
0900:
0901: /**
0902: * Returns the <code>ComponentInfo</code> for the specified Component.
0903: */
0904: private ComponentInfo getComponentInfo(Component component) {
0905: ComponentInfo info = (ComponentInfo) componentInfos
0906: .get(component);
0907: if (info == null) {
0908: componentInfos.put(component, new ComponentInfo(component));
0909: host.add(component);
0910: }
0911: return info;
0912: }
0913:
0914: /**
0915: * Adjusts the autopadding springs for the horizontal and vertical
0916: * groups. If <code>insert</code> is true this will insert auto padding
0917: * springs, otherwise this will only adjust the springs that
0918: * comprise auto preferred padding springs.
0919: */
0920: private void adjustAutopadding(boolean insert) {
0921: horizontalGroup.insertAutopadding(HORIZONTAL, new ArrayList(1),
0922: new ArrayList(1), new ArrayList(1), new ArrayList(1),
0923: insert);
0924: verticalGroup.insertAutopadding(VERTICAL, new ArrayList(1),
0925: new ArrayList(1), new ArrayList(1), new ArrayList(1),
0926: insert);
0927: }
0928:
0929: /**
0930: * Returns true if the two Components have a common ParallelGroup ancestor
0931: * along the particular axis.
0932: */
0933: private boolean areParallelSiblings(Component source,
0934: Component target, int axis) {
0935: ComponentInfo sourceInfo = getComponentInfo(source);
0936: ComponentInfo targetInfo = getComponentInfo(target);
0937: Spring sourceSpring;
0938: Spring targetSpring;
0939: if (axis == HORIZONTAL) {
0940: sourceSpring = sourceInfo.horizontalSpring;
0941: targetSpring = targetInfo.horizontalSpring;
0942: } else {
0943: sourceSpring = sourceInfo.verticalSpring;
0944: targetSpring = targetInfo.verticalSpring;
0945: }
0946: List sourcePath = parallelList;
0947: sourcePath.clear();
0948: Spring spring = sourceSpring.getParent();
0949: while (spring != null) {
0950: sourcePath.add(spring);
0951: spring = spring.getParent();
0952: }
0953: spring = targetSpring.getParent();
0954: while (spring != null) {
0955: if (sourcePath.contains(spring)) {
0956: while (spring != null) {
0957: if (spring instanceof ParallelGroup) {
0958: return true;
0959: }
0960: spring = spring.getParent();
0961: }
0962: return false;
0963: }
0964: spring = spring.getParent();
0965: }
0966: return false;
0967: }
0968:
0969: /**
0970: * Spring consists of a range: min, pref and max a value some where in
0971: * the middle of that and a location. Subclasses must override
0972: * methods to get the min/max/pref and will likely want to override
0973: * the <code>setSize</code> method. Spring automatically caches the
0974: * min/max/pref. If the min/pref/max has internally changes, or needs
0975: * to be updated you must invoked clear.
0976: */
0977: abstract class Spring {
0978: private int size;
0979: private int min;
0980: private int max;
0981: private int pref;
0982: private Spring parent;
0983:
0984: private int alignment;
0985:
0986: Spring() {
0987: min = pref = max = UNSET;
0988: }
0989:
0990: abstract int getMinimumSize0(int axis);
0991:
0992: abstract int getPreferredSize0(int axis);
0993:
0994: abstract int getMaximumSize0(int axis);
0995:
0996: /**
0997: * Sets the parent of this Spring.
0998: */
0999: void setParent(Spring parent) {
1000: this .parent = parent;
1001: }
1002:
1003: /**
1004: * Returns the parent of this spring.
1005: */
1006: Spring getParent() {
1007: return parent;
1008: }
1009:
1010: // This is here purely as a conveniance for ParallelGroup to avoid
1011: // having to track alignment separately.
1012: void setAlignment(int alignment) {
1013: checkAlignment(alignment, false);
1014: this .alignment = alignment;
1015: }
1016:
1017: int getAlignment() {
1018: return alignment;
1019: }
1020:
1021: /**
1022: * Returns the minimum size.
1023: */
1024: final int getMinimumSize(int axis) {
1025: if (min == UNSET) {
1026: min = constrain(getMinimumSize0(axis));
1027: }
1028: return min;
1029: }
1030:
1031: /**
1032: * Returns the preferred size.
1033: */
1034: final int getPreferredSize(int axis) {
1035: if (pref == UNSET) {
1036: pref = constrain(getPreferredSize0(axis));
1037: }
1038: return pref;
1039: }
1040:
1041: /**
1042: * Returns the maximum size.
1043: */
1044: final int getMaximumSize(int axis) {
1045: if (max == UNSET) {
1046: max = constrain(getMaximumSize0(axis));
1047: }
1048: return max;
1049: }
1050:
1051: /**
1052: * Resets the cached min/max/pref.
1053: */
1054: void clear() {
1055: size = min = pref = max = UNSET;
1056: }
1057:
1058: /**
1059: * Sets the value and location of the spring. Subclasses
1060: * will want to invoke super, then do any additional sizing.
1061: *
1062: * @param axis HORIZONTAL or VERTICAL
1063: * @param origin of this Spring
1064: * @param size of the Spring. If size is UNSET, this invokes
1065: * clear.
1066: */
1067: void setSize(int axis, int origin, int size) {
1068: this .size = size;
1069: if (size == UNSET) {
1070: clear();
1071: }
1072: }
1073:
1074: /**
1075: * Returns the current size.
1076: */
1077: int getSize() {
1078: return size;
1079: }
1080:
1081: int constrain(int value) {
1082: return Math.min(value, Short.MAX_VALUE);
1083: }
1084: }
1085:
1086: /**
1087: * Group provides for commonality between the two types of operations
1088: * supported by <code>GroupLayout</code>: laying out components one
1089: * after another (<code>SequentialGroup</code>) or layout on top
1090: * of each other (<code>ParallelGroup</code>). Use one of
1091: * <code>createSequentialGroup</code> or
1092: * <code>createParallelGroup</code> to create one.
1093: */
1094: public abstract class Group extends Spring {
1095: // private int origin;
1096: // private int size;
1097: List springs;
1098:
1099: Group() {
1100: springs = new ArrayList();
1101: }
1102:
1103: int indexOf(Spring spring) {
1104: return springs.indexOf(spring);
1105: }
1106:
1107: /**
1108: * Adds the Spring to the list of <code>Spring</code>s and returns
1109: * the receiver.
1110: */
1111: Group addSpring(Spring spring, int index) {
1112: springs.add(spring);
1113: spring.setParent(this );
1114: if (!(spring instanceof AutopaddingSpring)) {
1115: springsAdded = true;
1116: }
1117: return this ;
1118: }
1119:
1120: /**
1121: * Adds the Spring to the list of <code>Spring</code>s and returns
1122: * the receiver.
1123: */
1124: Group addSpring(Spring spring) {
1125: addSpring(spring, springs.size());
1126: return this ;
1127: }
1128:
1129: //
1130: // Spring methods
1131: //
1132:
1133: void setParent(Spring parent) {
1134: super .setParent(parent);
1135: for (int counter = springs.size() - 1; counter >= 0; counter--) {
1136: ((Spring) springs.get(counter)).setParent(this );
1137: }
1138: }
1139:
1140: void setSize(int axis, int origin, int size) {
1141: super .setSize(axis, origin, size);
1142: if (size == UNSET) {
1143: for (int counter = springs.size() - 1; counter >= 0; counter--) {
1144: getSpring(counter).setSize(axis, origin, size);
1145: }
1146: } else {
1147: setSize0(axis, origin, size);
1148: }
1149: }
1150:
1151: /**
1152: * This is invoked from <code>setSize</code> if passed a value
1153: * other than UNSET.
1154: */
1155: abstract void setSize0(int axis, int origin, int size);
1156:
1157: int getMinimumSize0(int axis) {
1158: return calculateSize(axis, MIN_SIZE);
1159: }
1160:
1161: int getPreferredSize0(int axis) {
1162: return calculateSize(axis, PREF_SIZE);
1163: }
1164:
1165: int getMaximumSize0(int axis) {
1166: return calculateSize(axis, MAX_SIZE);
1167: }
1168:
1169: /**
1170: * Used to compute how the two values representing two springs
1171: * will be combined. For example, a group that layed things out
1172: * one after the next would return <code>a + b</code>.
1173: */
1174: abstract int operator(int a, int b);
1175:
1176: /**
1177: * Calculates the specified size. This is called from
1178: * one of the <code>getMinimumSize0</code>,
1179: * <code>getPreferredSize0</code> or
1180: * <code>getMaximumSize0</code> methods. This will invoke
1181: * to <code>operator</code> to combine the values.
1182: */
1183: int calculateSize(int axis, int type) {
1184: int count = springs.size();
1185: if (count == 0) {
1186: return 0;
1187: }
1188: if (count == 1) {
1189: return getSize(getSpring(0), axis, type);
1190: }
1191: int size = constrain(operator(getSize(getSpring(0), axis,
1192: type), getSize(getSpring(1), axis, type)));
1193: for (int counter = 2; counter < count; counter++) {
1194: size = constrain(operator(size, getSize(
1195: getSpring(counter), axis, type)));
1196: }
1197: return size;
1198: }
1199:
1200: Spring getSpring(int index) {
1201: return (Spring) springs.get(index);
1202: }
1203:
1204: int getSize(Spring spring, int axis, int type) {
1205: switch (type) {
1206: case MIN_SIZE:
1207: return spring.getMinimumSize(axis);
1208: case PREF_SIZE:
1209: return spring.getPreferredSize(axis);
1210: case MAX_SIZE:
1211: return spring.getMaximumSize(axis);
1212: }
1213: return 0;
1214: }
1215:
1216: // Padding
1217: /**
1218: * Adjusts the autopadding springs in this group and its children.
1219: * If <code>insert</code> is true this will insert auto padding
1220: * springs, otherwise this will only adjust the springs that
1221: * comprise auto preferred padding springs.
1222: *
1223: * @param axis the axis of the springs; HORIZONTAL or VERTICAL
1224: * @param leadingPadding List of AutopaddingSprings that occur before
1225: * this Group
1226: * @param trailingPadding any trailing autopadding springs are added
1227: * to this on exit
1228: * @param leading List of ComponentSprings that occur before this Group
1229: * @param trailing any trailing ComponentSpring are added to this
1230: * List
1231: * @param insert Whether or not to insert AutopaddingSprings or just
1232: * adjust any existing AutopaddingSprings.
1233: */
1234: abstract void insertAutopadding(int axis, List leadingPadding,
1235: List trailingPadding, List leading, List trailing,
1236: boolean insert);
1237:
1238: /**
1239: * Removes any AutopaddingSprings.
1240: */
1241: void removeAutopadding() {
1242: for (int counter = springs.size() - 1; counter >= 0; counter--) {
1243: Spring spring = (Spring) springs.get(counter);
1244: if (spring instanceof AutopaddingSpring) {
1245: if (((AutopaddingSpring) spring).getUserCreated()) {
1246: ((AutopaddingSpring) spring).reset();
1247: } else {
1248: springs.remove(counter);
1249: }
1250: } else if (spring instanceof Group) {
1251: ((Group) spring).removeAutopadding();
1252: }
1253: }
1254: }
1255:
1256: void resetAutopadding() {
1257: // Clear cached pref/min/max.
1258: clear();
1259: for (int counter = springs.size() - 1; counter >= 0; counter--) {
1260: Spring spring = (Spring) springs.get(counter);
1261: if (spring instanceof AutopaddingSpring) {
1262: ((AutopaddingSpring) spring).clear();
1263: } else if (spring instanceof Group) {
1264: ((Group) spring).resetAutopadding();
1265: }
1266: }
1267: }
1268:
1269: void calculateAutopadding(int axis) {
1270: for (int counter = springs.size() - 1; counter >= 0; counter--) {
1271: Spring spring = (Spring) springs.get(counter);
1272: if (spring instanceof AutopaddingSpring) {
1273: // Force size to be reset.
1274: spring.clear();
1275: ((AutopaddingSpring) spring).calculatePadding(axis);
1276: } else if (spring instanceof Group) {
1277: ((Group) spring).calculateAutopadding(axis);
1278: }
1279: }
1280: // Clear cached pref/min/max.
1281: clear();
1282: }
1283: }
1284:
1285: /**
1286: * A <code>Group</code> that lays out its elements sequentially, one
1287: * after another. This class has no public constructor, use the
1288: * <code>createSequentialGroup</code> method to create one.
1289: *
1290: * @see #createSequentialGroup()
1291: */
1292: public class SequentialGroup extends Group {
1293: SequentialGroup() {
1294: }
1295:
1296: /**
1297: * Adds the specified <code>Group</code> to this
1298: * <code>SequentialGroup</code>
1299: *
1300: * @param group the Group to add
1301: * @return this Group
1302: */
1303: public SequentialGroup add(Group group) {
1304: return (SequentialGroup) addSpring(group);
1305: }
1306:
1307: /**
1308: * Adds the specified Component. If the Component's min/max
1309: * are different from its pref than the component will be resizable.
1310: *
1311: * @param component the Component to add
1312: * @return this <code>SequentialGroup</code>
1313: */
1314: public SequentialGroup add(Component component) {
1315: return add(component, DEFAULT_SIZE, DEFAULT_SIZE,
1316: DEFAULT_SIZE);
1317: }
1318:
1319: /**
1320: * Adds the specified <code>Component</code>. Min, pref and max
1321: * can be absolute values, or they can be one of
1322: * <code>DEFAULT_SIZE</code> or <code>PREFERRED_SIZE</code>. For
1323: * example, the following:
1324: * <pre>
1325: * add(component, PREFERRED_SIZE, PREFERRED_SIZE, 1000);
1326: * </pre>
1327: * Forces a max of 1000, with the min and preferred equalling that
1328: * of the preferred size of <code>component</code>.
1329: *
1330: * @param component the Component to add
1331: * @param min the minimum size
1332: * @param pref the preferred size
1333: * @param max the maximum size
1334: * @throws IllegalArgumentException if min, pref or max are
1335: * not positive and not one of PREFERRED_SIZE or DEFAULT_SIZE
1336: * @return this <code>SequentialGroup</code>
1337: */
1338: public SequentialGroup add(Component component, int min,
1339: int pref, int max) {
1340: return (SequentialGroup) addSpring(new ComponentSpring(
1341: component, min, pref, max));
1342: }
1343:
1344: /**
1345: * Adds a rigid gap.
1346: *
1347: * @param pref the size of the gap
1348: * @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
1349: * or the following is not meant min <= pref <= max
1350: * @return this <code>SequentialGroup</code>
1351: */
1352: public SequentialGroup add(int pref) {
1353: return add(pref, pref, pref);
1354: }
1355:
1356: /**
1357: * Adds a gap with the specified size.
1358: *
1359: * @param min the minimum size of the gap, or PREFERRED_SIZE
1360: * @param pref the preferred size of the gap
1361: * @param max the maximum size of the gap, or PREFERRED_SIZE
1362: * @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
1363: * or the following is not meant min <= pref <= max
1364: * @return this <code>SequentialGroup</code>
1365: */
1366: public SequentialGroup add(int min, int pref, int max) {
1367: return (SequentialGroup) addSpring(new GapSpring(min, pref,
1368: max));
1369: }
1370:
1371: /**
1372: * Adds an element representing the preferred gap between the two
1373: * components.
1374: *
1375: * @param comp1 the first component
1376: * @param comp2 the second component
1377: * @param type the type of gap; one of the constants defined by
1378: * LayoutStyle
1379: * @return this <code>SequentialGroup</code>
1380: * @throws IllegalArgumentException if <code>type</code> is not a
1381: * valid LayoutStyle constant
1382: * @see LayoutStyle
1383: */
1384: public SequentialGroup addPreferredGap(Component comp1,
1385: Component comp2, int type) {
1386: return addPreferredGap(comp1, comp2, type, false);
1387: }
1388:
1389: /**
1390: * Adds an element representing the preferred gap between the two
1391: * components.
1392: *
1393: * @param comp1 the first component
1394: * @param comp2 the second component
1395: * @param type the type of gap; one of the constants defined by
1396: * LayoutStyle
1397: * @param canGrow true if the gap can grow if more
1398: * space is available
1399: * @return this <code>SequentialGroup</code>
1400: * @throws IllegalArgumentException if <code>type</code> is not a
1401: * valid LayoutStyle constant
1402: * @see LayoutStyle
1403: */
1404: public SequentialGroup addPreferredGap(Component comp1,
1405: Component comp2, int type, boolean canGrow) {
1406: if (type != LayoutStyle.RELATED
1407: && type != LayoutStyle.UNRELATED
1408: && type != LayoutStyle.INDENT) {
1409: throw new IllegalArgumentException(
1410: "Invalid type argument");
1411: }
1412: return (SequentialGroup) addSpring(new PaddingSpring(comp1,
1413: comp2, type, canGrow));
1414: }
1415:
1416: /**
1417: * Adds an element representing the preferred gap between the
1418: * nearest components. That is, during layout the neighboring
1419: * components are found, and the min, pref and max of this
1420: * element is set based on the preferred gap between the
1421: * components. If no neighboring components are found the
1422: * min, pref and max are set to 0.
1423: *
1424: * @param type the type of gap; one of the LayoutStyle constants
1425: * @return this SequentialGroup
1426: * @throws IllegalArgumentException if type is not one of
1427: * <code>LayoutStyle.RELATED</code> or
1428: * <code>LayoutStyle.UNRELATED</code>
1429: * @see LayoutStyle
1430: */
1431: public SequentialGroup addPreferredGap(int type) {
1432: return addPreferredGap(type, DEFAULT_SIZE, DEFAULT_SIZE);
1433: }
1434:
1435: /**
1436: * Adds an element for the preferred gap between the
1437: * nearest components. That is, during layout the neighboring
1438: * components are found, and the min of this
1439: * element is set based on the preferred gap between the
1440: * components. If no neighboring components are found the
1441: * min is set to 0. This method allows you to specify the
1442: * preferred and maximum size by way of the <code>pref</code>
1443: * and <code>max</code> arguments. These can either be a
1444: * value >= 0, in which case the preferred or max is the max
1445: * of the argument and the preferred gap, of DEFAULT_VALUE in
1446: * which case the value is the same as the preferred gap.
1447: *
1448: * @param type the type of gap; one of LayoutStyle.RELATED or
1449: * LayoutStyle.UNRELATED
1450: * @param pref the preferred size; one of DEFAULT_SIZE or a value > 0
1451: * @param max the maximum size; one of DEFAULT_SIZE, PREFERRED_SIZE
1452: * or a value > 0
1453: * @return this SequentialGroup
1454: * @throws IllegalArgumentException if type is not one of
1455: * <code>LayoutStyle.RELATED</code> or
1456: * <code>LayoutStyle.UNRELATED</code> or pref/max is
1457: * != DEFAULT_SIZE and < 0, or pref > max
1458: * @see LayoutStyle
1459: */
1460: public SequentialGroup addPreferredGap(int type, int pref,
1461: int max) {
1462: if (type != LayoutStyle.RELATED
1463: && type != LayoutStyle.UNRELATED) {
1464: throw new IllegalArgumentException(
1465: "Padding type must be one of Padding.RELATED or Padding.UNRELATED");
1466: }
1467: if ((pref < 0 && pref != DEFAULT_SIZE)
1468: || (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)
1469: || (pref >= 0 && max >= 0 && pref > max)) {
1470: throw new IllegalArgumentException(
1471: "Pref and max must be either DEFAULT_VALUE or >= 0 and pref <= max");
1472: }
1473: hasPreferredPaddingSprings = true;
1474: return (SequentialGroup) addSpring(new AutopaddingSpring(
1475: type, pref, max));
1476: }
1477:
1478: /**
1479: * Adds an element representing the preferred gap between one edge
1480: * of the container and the next/previous Component. This will have
1481: * no effect if the next/previous element is not a Component and does
1482: * not touch one edge of the parent container.
1483: *
1484: * @return this <code>SequentialGroup</code>.
1485: */
1486: public SequentialGroup addContainerGap() {
1487: return addContainerGap(DEFAULT_SIZE, DEFAULT_SIZE);
1488: }
1489:
1490: /**
1491: * Adds an element representing the preferred gap between one edge
1492: * of the container and the next/previous Component. This will have
1493: * no effect if the next/previous element is not a Component and does
1494: * not touch one edge of the parent container.
1495: *
1496: * @param pref the preferred size; one of DEFAULT_SIZE or a value > 0
1497: * @param max the maximum size; one of DEFAULT_SIZE, PREFERRED_SIZE
1498: * or a value > 0.
1499: * @throws IllegalArgumentException if pref/max is
1500: * != DEFAULT_SIZE and < 0, or pref > max
1501: * @return this <code>SequentialGroup</code>
1502: */
1503: public SequentialGroup addContainerGap(int pref, int max) {
1504: if ((pref < 0 && pref != DEFAULT_SIZE)
1505: || (max < 0 && max != DEFAULT_SIZE && max != PREFERRED_SIZE)
1506: || (pref >= 0 && max >= 0 && pref > max)) {
1507: throw new IllegalArgumentException(
1508: "Pref and max must be either DEFAULT_VALUE or >= 0 and pref <= max");
1509: }
1510: hasPreferredPaddingSprings = true;
1511: return (SequentialGroup) addSpring(new ContainerAutopaddingSpring(
1512: pref, max));
1513: }
1514:
1515: int operator(int a, int b) {
1516: return constrain(a) + constrain(b);
1517: }
1518:
1519: void setSize0(int axis, int origin, int size) {
1520: int pref = getPreferredSize(axis);
1521: if ((size - pref) == 0) {
1522: for (int counter = 0, max = springs.size(); counter < max; counter++) {
1523: Spring spring = getSpring(counter);
1524: int springPref = spring.getPreferredSize(axis);
1525: spring.setSize(axis, origin, springPref);
1526: origin += springPref;
1527: }
1528: } else if (springs.size() == 1) {
1529: Spring spring = getSpring(0);
1530: spring.setSize(axis, origin, Math.min(size, spring
1531: .getMaximumSize(axis)));
1532: } else if (springs.size() > 1) {
1533: // Adjust between min/pref
1534: resize(axis, origin, size);
1535: }
1536: }
1537:
1538: private void resize(int axis, int origin, int size) {
1539: int delta = size - getPreferredSize(axis);
1540: boolean useMin = (delta < 0);
1541: int springCount = springs.size();
1542: if (useMin) {
1543: delta *= -1;
1544: }
1545:
1546: // First pass, sort the resizable springs into resizable
1547: List resizable = buildResizableList(axis, useMin);
1548: int resizableCount = resizable.size();
1549:
1550: if (resizableCount > 0) {
1551: int sDelta = delta / resizableCount;
1552: int slop = delta - sDelta * resizableCount;
1553: int[] sizes = new int[springCount];
1554: int sign = useMin ? -1 : 1;
1555: // Second pass, accumulate the resulting deltas (relative to
1556: // preferred) into sizes.
1557: for (int counter = 0; counter < resizableCount; counter++) {
1558: SpringDelta springDelta = (SpringDelta) resizable
1559: .get(counter);
1560: if ((counter + 1) == resizableCount) {
1561: sDelta += slop;
1562: }
1563: springDelta.delta = Math.min(sDelta,
1564: springDelta.delta);
1565: delta -= springDelta.delta;
1566: if (springDelta.delta != sDelta
1567: && counter + 1 < resizableCount) {
1568: // Spring didn't take all the space, reset how much
1569: // each spring will get.
1570: sDelta = delta / (resizableCount - counter - 1);
1571: slop = delta - sDelta
1572: * (resizableCount - counter - 1);
1573: }
1574: Spring spring = getSpring(springDelta.index);
1575: sizes[springDelta.index] = sign * springDelta.delta;
1576: }
1577:
1578: // And finally set the size of each spring
1579: for (int counter = 0; counter < springCount; counter++) {
1580: Spring spring = getSpring(counter);
1581: int sSize = spring.getPreferredSize(axis)
1582: + sizes[counter];
1583: spring.setSize(axis, origin, sSize);
1584: origin += sSize;
1585: }
1586: } else {
1587: // Nothing resizable, use the min or max of each of the
1588: // springs.
1589: for (int counter = 0; counter < springCount; counter++) {
1590: Spring spring = getSpring(counter);
1591: int sSize;
1592: if (useMin) {
1593: sSize = spring.getMinimumSize(axis);
1594: } else {
1595: sSize = spring.getMaximumSize(axis);
1596: }
1597: spring.setSize(axis, origin, sSize);
1598: origin += sSize;
1599: }
1600: }
1601: }
1602:
1603: /**
1604: * Returns the sorted list of SpringDelta's for the current set of
1605: * Springs.
1606: */
1607: private List buildResizableList(int axis, boolean useMin) {
1608: // First pass, figure out what is resizable
1609: int size = springs.size();
1610: List sorted = new ArrayList(size);
1611: for (int counter = 0; counter < size; counter++) {
1612: Spring spring = getSpring(counter);
1613: int sDelta;
1614: if (useMin) {
1615: sDelta = spring.getPreferredSize(axis)
1616: - spring.getMinimumSize(axis);
1617: } else {
1618: sDelta = spring.getMaximumSize(axis)
1619: - spring.getPreferredSize(axis);
1620: }
1621: if (sDelta > 0) {
1622: sorted.add(new SpringDelta(counter, sDelta));
1623: }
1624: }
1625: Collections.sort(sorted);
1626: return sorted;
1627: }
1628:
1629: /**
1630: * Returns an AutopaddingSpring, or null, at the specified index.
1631: * If the Spring at index is an AutopaddingSpring and user created
1632: * it will be returned. Otherwise if insert is true this will
1633: * create and AutopaddingSpring and insert it. If insert is false
1634: * null will be returned.
1635: */
1636: private AutopaddingSpring getNextAutopadding(int index,
1637: boolean insert) {
1638: Spring spring = getSpring(index);
1639: if (spring instanceof AutopaddingSpring
1640: && ((AutopaddingSpring) spring).getUserCreated()) {
1641: return (AutopaddingSpring) spring;
1642: }
1643: if (insert) {
1644: AutopaddingSpring autoSpring = new AutopaddingSpring();
1645: springs.add(index, autoSpring);
1646: return autoSpring;
1647: }
1648: return null;
1649: }
1650:
1651: void insertAutopadding(int axis, List leadingPadding,
1652: List trailingPadding, List leading, List trailing,
1653: boolean insert) {
1654: List newLeadingPadding = new ArrayList(leadingPadding);
1655: List newTrailingPadding = new ArrayList(1);
1656: List newLeading = new ArrayList(leading);
1657: List newTrailing = null;
1658: for (int counter = 0; counter < springs.size(); counter++) {
1659: Spring spring = getSpring(counter);
1660: if (spring instanceof AutopaddingSpring) {
1661: AutopaddingSpring padding = (AutopaddingSpring) spring;
1662: padding.setSources(newLeading);
1663: newLeading.clear();
1664: if (counter + 1 == springs.size()) {
1665: if (!(padding instanceof ContainerAutopaddingSpring)) {
1666: trailingPadding.add(padding);
1667: }
1668: } else {
1669: newLeadingPadding.clear();
1670: newLeadingPadding.add(padding);
1671: }
1672: } else {
1673: if (newLeading.size() > 0 && insert) {
1674: AutopaddingSpring padding = new AutopaddingSpring();
1675: // Force this to be revisted by decrementing counter
1676: // and breaking
1677: springs.add(counter--, padding);
1678: continue;
1679: }
1680: if (spring instanceof ComponentSpring) {
1681: ComponentSpring cSpring = (ComponentSpring) spring;
1682: for (int i = 0; i < newLeadingPadding.size(); i++) {
1683: ((AutopaddingSpring) newLeadingPadding
1684: .get(i)).add(cSpring, axis);
1685: }
1686: newLeading.clear();
1687: newLeadingPadding.clear();
1688: if (counter + 1 == springs.size()) {
1689: trailing.add(cSpring);
1690: } else {
1691: newLeading.add(cSpring);
1692: }
1693: } else if (spring instanceof Group) {
1694: if (newTrailing == null) {
1695: newTrailing = new ArrayList(1);
1696: } else {
1697: newTrailing.clear();
1698: }
1699: newTrailingPadding.clear();
1700: ((Group) spring).insertAutopadding(axis,
1701: newLeadingPadding, newTrailingPadding,
1702: newLeading, newTrailing, insert);
1703: newLeading.clear();
1704: newLeadingPadding.clear();
1705: if (counter + 1 == springs.size()) {
1706: trailing.addAll(newTrailing);
1707: trailingPadding.addAll(newTrailingPadding);
1708: } else {
1709: newLeading.addAll(newTrailing);
1710: newLeadingPadding
1711: .addAll(newTrailingPadding);
1712: }
1713: } else {
1714: newLeadingPadding.clear();
1715: newLeading.clear();
1716: }
1717: }
1718: }
1719: }
1720: }
1721:
1722: /**
1723: * Used in figuring out how much space to give resizable springs.
1724: */
1725: private static class SpringDelta implements Comparable {
1726: // Original index.
1727: public int index;
1728: // Delta, one of pref - min or max - pref.
1729: public int delta;
1730:
1731: public SpringDelta(int index, int delta) {
1732: this .index = index;
1733: this .delta = delta;
1734: }
1735:
1736: public int compareTo(Object o) {
1737: return delta - ((SpringDelta) o).delta;
1738: }
1739:
1740: public String toString() {
1741: return super .toString() + "[index=" + index + ", delta="
1742: + delta + "]";
1743: }
1744: }
1745:
1746: /**
1747: * A <code>Group</code> that lays out its elements on top of each
1748: * other. If a child element is smaller than the provided space it
1749: * is aligned based on the alignment of the child (if specified) or
1750: * on the alignment of the ParallelGroup.
1751: *
1752: * @see #createParallelGroup()
1753: */
1754: public class ParallelGroup extends Group {
1755: // How children are layed out.
1756: private int childAlignment;
1757: // Whether or not we're resizable.
1758: private boolean resizable;
1759:
1760: ParallelGroup(int childAlignment, boolean resizable) {
1761: checkAlignment(childAlignment, true);
1762: this .childAlignment = childAlignment;
1763: this .resizable = resizable;
1764: }
1765:
1766: /**
1767: * Adds the specified <code>Group</code>.
1768: *
1769: * @param group the Group to add
1770: * @return this Group
1771: */
1772: public ParallelGroup add(Group group) {
1773: return (ParallelGroup) addSpring(group);
1774: }
1775:
1776: /**
1777: * Adds the specified Component. If the Component's min/max
1778: * are different from its pref than the component will be resizable.
1779: *
1780: * @param component the Component to add
1781: * @return this <code>ParallelGroup</code>
1782: */
1783: public ParallelGroup add(Component component) {
1784: return add(component, DEFAULT_SIZE, DEFAULT_SIZE,
1785: DEFAULT_SIZE);
1786: }
1787:
1788: /**
1789: * Adds the specified <code>Component</code>. Min, pref and max
1790: * can be absolute values, or they can be one of
1791: * <code>DEFAULT_SIZE</code> or <code>PREFERRED_SIZE</code>. For
1792: * example, the following:
1793: * <pre>
1794: * add(component, PREFERRED_SIZE, PREFERRED_SIZE, 1000);
1795: * </pre>
1796: * Forces a max of 1000, with the min and preferred equalling that
1797: * of the preferred size of <code>component</code>.
1798: *
1799: * @param component the Component to add
1800: * @param min the minimum size
1801: * @param pref the preferred size
1802: * @param max the maximum size
1803: * @throws IllegalArgumentException if min, pref or max are
1804: * not positive and not one of PREFERRED_SIZE or DEFAULT_SIZE.
1805: * @return this <code>SequentialGroup</code>
1806: */
1807: public ParallelGroup add(Component component, int min,
1808: int pref, int max) {
1809: return (ParallelGroup) addSpring(new ComponentSpring(
1810: component, min, pref, max));
1811: }
1812:
1813: /**
1814: * Adds a rigid gap.
1815: *
1816: * @param pref the size of the gap
1817: * @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
1818: * or the following is not meant min <= pref <= max.
1819: * @return this <code>ParallelGroup</code>
1820: */
1821: public ParallelGroup add(int pref) {
1822: return add(pref, pref, pref);
1823: }
1824:
1825: /**
1826: * Adds a gap with the specified size.
1827: *
1828: * @param min the minimum size of the gap
1829: * @param pref the preferred size of the gap
1830: * @param max the maximum size of the gap
1831: * @throws IllegalArgumentException if min < 0 or pref < 0 or max < 0
1832: * or the following is not meant min <= pref <= max.
1833: * @return this <code>ParallelGroup</code>
1834: */
1835: public ParallelGroup add(int min, int pref, int max) {
1836: return (ParallelGroup) addSpring(new GapSpring(min, pref,
1837: max));
1838: }
1839:
1840: /**
1841: * Adds the specified <code>Group</code> as a child of this group.
1842: *
1843: * @param alignment the alignment of the Group.
1844: * @param group the Group to add
1845: * @return this <code>ParallelGroup</code>
1846: * @throws IllegalArgumentException if alignment is not one of
1847: * <code>LEADING</code>, <code>TRAILING</code> or
1848: * <code>CENTER</code>
1849: */
1850: public ParallelGroup add(int alignment, Group group) {
1851: group.setAlignment(alignment);
1852: return (ParallelGroup) addSpring(group);
1853: }
1854:
1855: /**
1856: * Adds the specified Component. If the Component's min/max
1857: * are different from its pref than the component will be resizable.
1858: *
1859: * @param alignment the alignment for the component
1860: * @param component the Component to add
1861: * @return this <code>Group</code>
1862: * @throws IllegalArgumentException if alignment is not one of
1863: * <code>LEADING</code>, <code>TRAILING</code> or
1864: * <code>CENTER</code>
1865: */
1866: public ParallelGroup add(int alignment, Component component) {
1867: return add(alignment, component, DEFAULT_SIZE,
1868: DEFAULT_SIZE, DEFAULT_SIZE);
1869: }
1870:
1871: /**
1872: * Adds the specified <code>Component</code>. Min, pref and max
1873: * can be absolute values, or they can be one of
1874: * <code>DEFAULT_SIZE</code> or <code>PREFERRED_SIZE</code>. For
1875: * example, the following:
1876: * <pre>
1877: * add(component, PREFERRED_SIZE, PREFERRED_SIZE, 1000);
1878: * </pre>
1879: * Forces a max of 1000, with the min and preferred equalling that
1880: * of the preferred size of <code>component</code>.
1881: *
1882: * @param alignment the alignment for the component.
1883: * @param component the Component to add
1884: * @param min the minimum size
1885: * @param pref the preferred size
1886: * @param max the maximum size
1887: * @throws IllegalArgumentException if min, pref or max are
1888: * not positive and not one of PREFERRED_SIZE or DEFAULT_SIZE.
1889: * @return this <code>Group</code>
1890: */
1891: public ParallelGroup add(int alignment, Component component,
1892: int min, int pref, int max) {
1893: ComponentSpring spring = new ComponentSpring(component,
1894: min, pref, max);
1895: spring.setAlignment(alignment);
1896: return (ParallelGroup) addSpring(spring);
1897: }
1898:
1899: boolean isResizable() {
1900: return resizable;
1901: }
1902:
1903: int operator(int a, int b) {
1904: return Math.max(a, b);
1905: }
1906:
1907: int getMinimumSize0(int axis) {
1908: if (!isResizable()) {
1909: return getPreferredSize(axis);
1910: }
1911: return super .getMinimumSize0(axis);
1912: }
1913:
1914: int getMaximumSize0(int axis) {
1915: if (!isResizable()) {
1916: return getPreferredSize(axis);
1917: }
1918: return super .getMaximumSize0(axis);
1919: }
1920:
1921: void setSize0(int axis, int origin, int size) {
1922: int alignment = childAlignment;
1923: if (alignment == BASELINE) {
1924: // Not having the axis in the constructor forces us
1925: // to do this.
1926: alignment = LEADING;
1927: }
1928: for (int counter = 0, max = springs.size(); counter < max; counter++) {
1929: Spring spring = getSpring(counter);
1930: int sAlignment = spring.getAlignment();
1931: int springSize = Math.min(size, spring
1932: .getMaximumSize(axis));
1933: if (sAlignment == NO_ALIGNMENT) {
1934: sAlignment = alignment;
1935: }
1936: switch (sAlignment) {
1937: case TRAILING:
1938: spring.setSize(axis, origin + size - springSize,
1939: springSize);
1940: break;
1941: case CENTER:
1942: spring.setSize(axis, origin + (size - springSize)
1943: / 2, springSize);
1944: break;
1945: default: // LEADING or NO_ALIGNMENT
1946: spring.setSize(axis, origin, springSize);
1947: break;
1948: }
1949: }
1950: }
1951:
1952: void insertAutopadding(int axis, List leadingPadding,
1953: List trailingPadding, List leading, List trailing,
1954: boolean insert) {
1955: for (int counter = 0; counter < springs.size(); counter++) {
1956: Spring spring = getSpring(counter);
1957: if (spring instanceof ComponentSpring) {
1958: for (int i = 0; i < leadingPadding.size(); i++) {
1959: ((AutopaddingSpring) leadingPadding.get(i))
1960: .add((ComponentSpring) spring, axis);
1961: }
1962: trailing.add(spring);
1963: } else if (spring instanceof Group) {
1964: ((Group) spring).insertAutopadding(axis,
1965: leadingPadding, trailingPadding, leading,
1966: trailing, insert);
1967: } else if (spring instanceof AutopaddingSpring) {
1968: trailingPadding.add(spring);
1969: }
1970: }
1971: }
1972: }
1973:
1974: /**
1975: * An extension of <code>ParallelGroup</code> that aligns its
1976: * constituent <code>Spring</code>s along the baseline.
1977: */
1978: private class BaselineGroup extends ParallelGroup {
1979: //
1980: // This class aligns all components that have a baseline along
1981: // the baseline. Any components that do not have a baseline are
1982: // centered. Addititionally components that have a baseline
1983: // will NOT be resized!
1984: //
1985: private boolean allSpringsHaveBaseline;
1986: private int prefAscent;
1987: private int prefDescent;
1988:
1989: BaselineGroup(boolean resizable) {
1990: super (LEADING, resizable);
1991: prefAscent = prefDescent = -1;
1992: }
1993:
1994: void setSize(int axis, int origin, int size) {
1995: if (size == UNSET) {
1996: prefAscent = prefDescent = -1;
1997: }
1998: super .setSize(axis, origin, size);
1999: }
2000:
2001: void setSize0(int axis, int origin, int size) {
2002: if (axis == HORIZONTAL || prefAscent == -1) {
2003: super .setSize0(axis, origin, size);
2004: } else {
2005: // do baseline layout
2006: baselineLayout(origin, size);
2007: }
2008: }
2009:
2010: int calculateSize(int axis, int type) {
2011: if (springs.size() < 2 || axis != VERTICAL) {
2012: return super .calculateSize(axis, type);
2013: }
2014: if (prefAscent == -1) {
2015: calculateBaseline();
2016: }
2017: if (allSpringsHaveBaseline) {
2018: return prefAscent + prefDescent;
2019: }
2020: return Math.max(prefAscent + prefDescent, super
2021: .calculateSize(axis, type));
2022: }
2023:
2024: /**
2025: * Calculates the baseline of children Springs that have a baseline.
2026: * The results are stored in prefAscent & prefDescent. If all
2027: * child springs have a baseline <code>allSpringsHaveBaseline</code>
2028: * is set to true.
2029: */
2030: private void calculateBaseline() {
2031: // calculate baseline
2032: prefAscent = 0;
2033: prefDescent = 0;
2034: allSpringsHaveBaseline = true;
2035: for (int counter = springs.size() - 1; counter >= 0; counter--) {
2036: Spring spring = getSpring(counter);
2037: int baseline = -1;
2038: if (spring instanceof ComponentSpring) {
2039: baseline = ((ComponentSpring) spring).getBaseline();
2040: if (baseline >= 0) {
2041: prefAscent = Math.max(prefAscent, baseline);
2042: prefDescent = Math.max(prefDescent, spring
2043: .getPreferredSize(VERTICAL)
2044: - baseline);
2045: }
2046: }
2047: if (baseline < 0) {
2048: allSpringsHaveBaseline = false;
2049: }
2050: }
2051: }
2052:
2053: /**
2054: * Lays out springs that have a baseline along the baseline. All
2055: * others are centered.
2056: */
2057: private void baselineLayout(int origin, int size) {
2058: for (int counter = 0, max = springs.size(); counter < max; counter++) {
2059: Spring spring = getSpring(counter);
2060: int baseline = -1;
2061: if (spring instanceof ComponentSpring) {
2062: baseline = ((ComponentSpring) spring).getBaseline();
2063: if (baseline >= 0) {
2064: spring.setSize(VERTICAL, origin + prefAscent
2065: - baseline, spring
2066: .getPreferredSize(VERTICAL));
2067: }
2068: }
2069: if (baseline < 0) {
2070: // PENDING: do we want to offer up alternative alignments
2071: // here?
2072: int sSize = Math.min(spring
2073: .getMaximumSize(VERTICAL), size);
2074: spring.setSize(VERTICAL, origin + (size - sSize)
2075: / 2, sSize);
2076: }
2077: }
2078: }
2079: }
2080:
2081: /**
2082: * A Spring representing one axis of a Component.
2083: * There are three ways to configure this:
2084: * <ul>
2085: * <li>Use the pref/min/max from the component
2086: * <li>Use the pref from the component and fix the min to 0 or max
2087: * to a big number.
2088: * <li>Force the min/max/pref to be a certain value.
2089: * If the Component's size is to be linked to another components than
2090: * the min/max/pref all come from the ComponentInfo.
2091: */
2092: class ComponentSpring extends Spring {
2093: private Component component;
2094: private int origin;
2095:
2096: private int min;
2097: private int pref;
2098: private int max;
2099:
2100: // Baseline for the component.
2101: private int baseline = -1;
2102:
2103: // Whether or not the size has been requested yet.
2104: private boolean installed;
2105:
2106: private ComponentSpring(Component component, int min, int pref,
2107: int max) {
2108: this .component = component;
2109:
2110: checkSize(min, pref, max, true);
2111:
2112: this .min = min;
2113: this .max = max;
2114: this .pref = pref;
2115:
2116: getComponentInfo(component);
2117: }
2118:
2119: int getMinimumSize0(int axis) {
2120: if (isLinked(axis)) {
2121: return getLinkSize(axis, MIN_SIZE);
2122: }
2123: return getMinimumSize1(axis);
2124: }
2125:
2126: int getMinimumSize1(int axis) {
2127: if (min >= 0) {
2128: return min;
2129: }
2130: if (min == PREFERRED_SIZE) {
2131: return getPreferredSize1(axis);
2132: }
2133: return getSizeAlongAxis(axis, component.getMinimumSize());
2134: }
2135:
2136: int getPreferredSize0(int axis) {
2137: if (isLinked(axis)) {
2138: return getLinkSize(axis, PREF_SIZE);
2139: }
2140: return Math.max(getMinimumSize(axis),
2141: getPreferredSize1(axis));
2142: }
2143:
2144: int getPreferredSize1(int axis) {
2145: if (pref >= 0) {
2146: return pref;
2147: }
2148: return getSizeAlongAxis(axis, component.getPreferredSize());
2149: }
2150:
2151: int getMaximumSize0(int axis) {
2152: if (isLinked(axis)) {
2153: return getLinkSize(axis, MAX_SIZE);
2154: }
2155: return Math
2156: .max(getMinimumSize(axis), getMaximumSize1(axis));
2157: }
2158:
2159: int getMaximumSize1(int axis) {
2160: if (max >= 0) {
2161: return max;
2162: }
2163: if (max == PREFERRED_SIZE) {
2164: return getPreferredSize1(axis);
2165: }
2166: return getSizeAlongAxis(axis, component.getMaximumSize());
2167: }
2168:
2169: private int getSizeAlongAxis(int axis, Dimension size) {
2170: return (axis == HORIZONTAL) ? size.width : size.height;
2171: }
2172:
2173: private int getLinkSize(int axis, int type) {
2174: ComponentInfo ci = getComponentInfo(component);
2175: return ci.getSize(axis, type);
2176: }
2177:
2178: void setSize(int axis, int origin, int size) {
2179: super .setSize(axis, origin, size);
2180: this .origin = origin;
2181: if (size == UNSET) {
2182: baseline = -1;
2183: }
2184: }
2185:
2186: int getOrigin() {
2187: return origin;
2188: }
2189:
2190: void setComponent(Component component) {
2191: this .component = component;
2192: }
2193:
2194: Component getComponent() {
2195: return component;
2196: }
2197:
2198: int getBaseline() {
2199: if (baseline == -1 && (component instanceof Component)) {
2200: Spring horizontalSpring = getComponentInfo(component).horizontalSpring;
2201: int width;
2202: if (horizontalSpring != null) {
2203: width = horizontalSpring.getSize();
2204: } else {
2205: width = component.getPreferredSize().width;
2206: }
2207: baseline = Baseline.getBaseline((Component) component,
2208: width, getPreferredSize(VERTICAL));
2209: }
2210: return baseline;
2211: }
2212:
2213: private boolean isLinked(int axis) {
2214: return getComponentInfo(component).isLinked(axis);
2215: }
2216:
2217: void installIfNecessary(int axis) {
2218: if (!installed) {
2219: installed = true;
2220: if (axis == HORIZONTAL) {
2221: getComponentInfo(component).horizontalSpring = this ;
2222: } else {
2223: getComponentInfo(component).verticalSpring = this ;
2224: }
2225: }
2226: }
2227: }
2228:
2229: /**
2230: * Spring representing the preferred distance between two components.
2231: */
2232: class PaddingSpring extends Spring {
2233: private Component source;
2234: private Component target;
2235: private int type;
2236: private boolean canGrow;
2237:
2238: PaddingSpring(Component source, Component target, int type,
2239: boolean canGrow) {
2240: this .source = source;
2241: this .target = target;
2242: this .type = type;
2243: this .canGrow = canGrow;
2244: }
2245:
2246: int getMinimumSize0(int axis) {
2247: return getPadding(axis);
2248: }
2249:
2250: int getPreferredSize0(int axis) {
2251: return getPadding(axis);
2252: }
2253:
2254: int getMaximumSize0(int axis) {
2255: if (canGrow) {
2256: return Short.MAX_VALUE;
2257: }
2258: return getPadding(axis);
2259: }
2260:
2261: private int getPadding(int axis) {
2262: int position;
2263: if (axis == HORIZONTAL) {
2264: position = 3;//SwingConstants.EAST;
2265: } else {
2266: position = 5;//SwingConstants.SOUTH;
2267: }
2268: return LayoutStyle.getSharedInstance().getPreferredGap(
2269: source, target, type, position, host);
2270: }
2271: }
2272:
2273: /**
2274: * Spring represented a certain amount of space.
2275: */
2276: class GapSpring extends Spring {
2277: private int min;
2278: private int pref;
2279: private int max;
2280:
2281: GapSpring(int min, int pref, int max) {
2282: checkSize(min, pref, max, false);
2283: this .min = min;
2284: this .pref = pref;
2285: this .max = max;
2286: }
2287:
2288: int getMinimumSize0(int axis) {
2289: if (min == PREFERRED_SIZE) {
2290: return getPreferredSize(axis);
2291: }
2292: return min;
2293: }
2294:
2295: int getPreferredSize0(int axis) {
2296: return pref;
2297: }
2298:
2299: int getMaximumSize0(int axis) {
2300: if (max == PREFERRED_SIZE) {
2301: return getPreferredSize(axis);
2302: }
2303: return max;
2304: }
2305: }
2306:
2307: /**
2308: * Spring reprensenting the distance between any number of sources and
2309: * targets. The targets and sources are computed during layout. An
2310: * instance of this can either be dynamically created when
2311: * autocreatePadding is true, or explicitly created by the developer.
2312: */
2313: private class AutopaddingSpring extends Spring {
2314: List sources;
2315: ComponentSpring source;
2316: private List matches;
2317: int size;
2318: int lastSize;
2319: private int pref;
2320: private int max;
2321: private int type;
2322: private boolean userCreated;
2323:
2324: private AutopaddingSpring() {
2325: this .pref = PREFERRED_SIZE;
2326: this .max = PREFERRED_SIZE;
2327: this .type = LayoutStyle.RELATED;
2328: }
2329:
2330: AutopaddingSpring(int pref, int max) {
2331: this .pref = pref;
2332: this .max = max;
2333: }
2334:
2335: AutopaddingSpring(int type, int pref, int max) {
2336: this .type = type;
2337: this .pref = pref;
2338: this .max = max;
2339: this .userCreated = true;
2340: }
2341:
2342: public void setSource(ComponentSpring source) {
2343: this .source = source;
2344: }
2345:
2346: public void setSources(List sources) {
2347: this .sources = new ArrayList(sources.size());
2348: this .sources.addAll(sources);
2349: }
2350:
2351: public void setUserCreated(boolean userCreated) {
2352: this .userCreated = userCreated;
2353: }
2354:
2355: public boolean getUserCreated() {
2356: return userCreated;
2357: }
2358:
2359: void clear() {
2360: lastSize = getSize();
2361: super .clear();
2362: size = 0;
2363: }
2364:
2365: public void reset() {
2366: size = 0;
2367: sources = null;
2368: source = null;
2369: matches = null;
2370: }
2371:
2372: public void calculatePadding(int axis) {
2373: size = 0;
2374: int maxPadding = 0;
2375: if (matches != null) {
2376: LayoutStyle p = LayoutStyle.getSharedInstance();
2377: // PENDING: rtl
2378: int position = (axis == HORIZONTAL) ? 3 : 5;
2379: for (int i = matches.size() - 1; i >= 0; i--) {
2380: AutopaddingMatch match = (AutopaddingMatch) matches
2381: .get(i);
2382: maxPadding = Math.max(maxPadding, calculatePadding(
2383: p, position, match.source, match.target));
2384: }
2385: }
2386: if (lastSize != UNSET) {
2387: size += Math.min(maxPadding, lastSize);
2388: }
2389: }
2390:
2391: private int calculatePadding(LayoutStyle p, int position,
2392: ComponentSpring source, ComponentSpring target) {
2393: int delta = target.getOrigin()
2394: - (source.getOrigin() + source.getSize());
2395: if (delta >= 0) {
2396: int padding;
2397: if ((source.getComponent() instanceof Component)
2398: && (target.getComponent() instanceof Component)) {
2399: padding = p.getPreferredGap((Component) source
2400: .getComponent(), (Component) target
2401: .getComponent(), type, position, host);
2402: } else {
2403: padding = 10;
2404: }
2405: if (padding > delta) {
2406: size = Math.max(size, padding - delta);
2407: }
2408: return padding;
2409: }
2410: return 0;
2411: }
2412:
2413: public void add(ComponentSpring spring, int axis) {
2414: int oAxis = (axis == HORIZONTAL) ? VERTICAL : HORIZONTAL;
2415: if (source != null) {
2416: if (areParallelSiblings(source.getComponent(), spring
2417: .getComponent(), oAxis)) {
2418: addMatch(source, spring);
2419: }
2420: } else {
2421: Component component = spring.getComponent();
2422: for (int counter = sources.size() - 1; counter >= 0; counter--) {
2423: ComponentSpring source = (ComponentSpring) sources
2424: .get(counter);
2425: if (areParallelSiblings(source.getComponent(),
2426: component, oAxis)) {
2427: addMatch(source, spring);
2428: }
2429: }
2430: }
2431: }
2432:
2433: private void addMatch(ComponentSpring source,
2434: ComponentSpring target) {
2435: if (matches == null) {
2436: matches = new ArrayList(1);
2437: }
2438: matches.add(new AutopaddingMatch(source, target));
2439: }
2440:
2441: int getMinimumSize0(int axis) {
2442: return size;
2443: }
2444:
2445: int getPreferredSize0(int axis) {
2446: if (pref == PREFERRED_SIZE || pref == DEFAULT_SIZE) {
2447: return size;
2448: }
2449: return Math.max(size, pref);
2450: }
2451:
2452: int getMaximumSize0(int axis) {
2453: if (max >= 0) {
2454: return Math.max(getPreferredSize(axis), max);
2455: }
2456: return size;
2457: }
2458:
2459: String getMatchDescription() {
2460: return (matches == null) ? "" : matches.toString();
2461: }
2462:
2463: public String toString() {
2464: return super .toString() + getMatchDescription();
2465: }
2466: }
2467:
2468: /**
2469: * Represents two springs that should have autopadding inserted between
2470: * them.
2471: */
2472: private static class AutopaddingMatch {
2473: public ComponentSpring source;
2474: public ComponentSpring target;
2475:
2476: AutopaddingMatch(ComponentSpring source, ComponentSpring target) {
2477: this .source = source;
2478: this .target = target;
2479: }
2480:
2481: private String toString(ComponentSpring spring) {
2482: return spring.getComponent().getName();
2483: }
2484:
2485: public String toString() {
2486: return "[" + toString(source) + "-" + toString(target)
2487: + "]";
2488: }
2489: }
2490:
2491: /**
2492: * An extension of AutopaddingSpring used for container level padding.
2493: */
2494: private class ContainerAutopaddingSpring extends AutopaddingSpring {
2495: private List targets;
2496:
2497: ContainerAutopaddingSpring() {
2498: super ();
2499: setUserCreated(true);
2500: }
2501:
2502: ContainerAutopaddingSpring(int pref, int max) {
2503: super (pref, max);
2504: setUserCreated(true);
2505: }
2506:
2507: public void add(ComponentSpring spring, int axis) {
2508: if (targets == null) {
2509: targets = new ArrayList(1);
2510: }
2511: targets.add(spring);
2512: }
2513:
2514: public void calculatePadding(int axis) {
2515: LayoutStyle p = LayoutStyle.getSharedInstance();
2516: int maxPadding = 0;
2517: size = 0;
2518: if (targets != null) {
2519: // Indicates leading
2520: // PENDING: rtl
2521: int position = (axis == HORIZONTAL) ? 7 : 1;
2522: for (int i = targets.size() - 1; i >= 0; i--) {
2523: ComponentSpring targetSpring = (ComponentSpring) targets
2524: .get(i);
2525: int padding = 10;
2526: if (targetSpring.getComponent() instanceof Component) {
2527: padding = p
2528: .getContainerGap(
2529: (Component) targetSpring
2530: .getComponent(),
2531: position, host);
2532: maxPadding = Math.max(padding, maxPadding);
2533: padding -= targetSpring.getOrigin();
2534: } else {
2535: maxPadding = Math.max(padding, maxPadding);
2536: }
2537: size = Math.max(size, padding);
2538: }
2539: } else {
2540: // Trailing
2541: // PENDING: rtl
2542: int position = (axis == HORIZONTAL) ? 3 : 5;
2543: if (sources != null) {
2544: for (int i = sources.size() - 1; i >= 0; i--) {
2545: ComponentSpring sourceSpring = (ComponentSpring) sources
2546: .get(i);
2547: maxPadding = Math.max(maxPadding, updateSize(p,
2548: sourceSpring, position));
2549: }
2550: } else if (source != null) {
2551: maxPadding = updateSize(p, source, position);
2552: }
2553: }
2554: if (lastSize != UNSET) {
2555: size += Math.min(maxPadding, lastSize);
2556: }
2557: }
2558:
2559: private int updateSize(LayoutStyle p,
2560: ComponentSpring sourceSpring, int position) {
2561: int padding = 10;
2562: if (sourceSpring.getComponent() instanceof Component) {
2563: padding = p.getContainerGap((Component) sourceSpring
2564: .getComponent(), position, host);
2565: }
2566: int delta = Math
2567: .max(0, getParent().getSize()
2568: - sourceSpring.getSize()
2569: - sourceSpring.getOrigin());
2570: size = Math.max(size, padding - delta);
2571: return padding;
2572: }
2573:
2574: String getMatchDescription() {
2575: if (targets != null) {
2576: return "leading: " + targets.toString();
2577: }
2578: if (sources != null) {
2579: return "trailing: " + sources.toString();
2580: }
2581: return "--";
2582: }
2583: }
2584:
2585: /**
2586: * Tracks the horizontal/vertical Springs for a Component.
2587: * This class is also used to handle Springs that have their sizes
2588: * linked.
2589: */
2590: private static class ComponentInfo {
2591: // Component being layed out
2592: private Component component;
2593:
2594: ComponentSpring horizontalSpring;
2595: ComponentSpring verticalSpring;
2596:
2597: // If there is a chain of components that share the
2598: // same size this will be the master CI. If this is the
2599: // masterCI, masterCI == this.
2600: private ComponentInfo horizontalMaster;
2601: private ComponentInfo verticalMaster;
2602: // If we're the master, this is the dependant ComponentInfos
2603: private List horizontalDependants;
2604: private List verticalDependants;
2605: // If we're the masterCI this gives the min/pref/max size of all
2606: // children. This is indexed by one of the size operators. Horizontal
2607: // first, then vertical.
2608: private int[] horizontalSizes;
2609: private int[] verticalSizes;
2610:
2611: ComponentInfo(Component component) {
2612: this .component = component;
2613: clear();
2614: }
2615:
2616: public void setBounds(Insets insets) {
2617: int x = 0;
2618: int y = 0;
2619: int w = 0;
2620: int h = 0;
2621:
2622: if (horizontalSpring != null) {
2623: x = horizontalSpring.getOrigin();
2624: w = horizontalSpring.getSize();
2625: }
2626: if (verticalSpring != null) {
2627: y = verticalSpring.getOrigin();
2628: h = verticalSpring.getSize();
2629: }
2630: /* TODO fix up
2631: * There is problem, that right (also bottom sometimes) is not
2632: * well aligned. The magic constant 2 ensures that componets are 2px from
2633: * bottom and right. The side effect is, that user can not put component to full screen size.
2634: */
2635: Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
2636: if (x + insets.left + w + insets.right + 2 > d.width) {
2637: w = d.width - (insets.left + insets.right + x + 2);
2638: }
2639: if (y + insets.top + h + insets.bottom + 2 > d.height) {
2640: h = d.height - (insets.top + insets.bottom + y + 2);
2641: }
2642: //end fix up
2643: component.setBounds(x + insets.left, y + insets.top, w, h);
2644: }
2645:
2646: public void setComponent(Component component) {
2647: this .component = component;
2648: if (horizontalSpring != null) {
2649: horizontalSpring.setComponent(component);
2650: }
2651: if (verticalSpring != null) {
2652: verticalSpring.setComponent(component);
2653: }
2654: }
2655:
2656: public Component getComponent() {
2657: return component;
2658: }
2659:
2660: /**
2661: * Returns true if this component has its size linked to
2662: * other components.
2663: */
2664: public boolean isLinked(int axis) {
2665: if (axis == HORIZONTAL) {
2666: return horizontalMaster != null;
2667: }
2668: return (verticalMaster != null);
2669: }
2670:
2671: public ComponentInfo getMasterComponentInfo(int axis) {
2672: if (axis == HORIZONTAL) {
2673: if (horizontalMaster == null) {
2674: horizontalMaster = this ;
2675: horizontalDependants = new ArrayList(1);
2676: horizontalDependants.add(this );
2677: horizontalSizes = new int[3];
2678: clear();
2679: }
2680: return horizontalMaster;
2681: } else {
2682: if (verticalMaster == null) {
2683: verticalMaster = this ;
2684: verticalDependants = new ArrayList(1);
2685: verticalDependants.add(this );
2686: verticalSizes = new int[3];
2687: clear();
2688: }
2689: return verticalMaster;
2690: }
2691: }
2692:
2693: public void addChild(ComponentInfo child, int axis) {
2694: if (axis == HORIZONTAL) {
2695: addChild0(child, HORIZONTAL);
2696: } else {
2697: addChild0(child, VERTICAL);
2698: }
2699: }
2700:
2701: private void addChild0(ComponentInfo child, int axis) {
2702: if (axis == HORIZONTAL) {
2703: if (child.horizontalMaster == child) {
2704: // Child is already marked as a master, subsume all it's
2705: // children and reset it's master to us.
2706: horizontalDependants
2707: .addAll(child.horizontalDependants);
2708: child.horizontalDependants = null;
2709: child.horizontalSizes = null;
2710: } else {
2711: // Child isn't a master.
2712: horizontalDependants.add(child);
2713: }
2714: child.horizontalMaster = this ;
2715: } else {
2716: if (child.verticalMaster == child) {
2717: // Child is already marked as a master, subsume all it's
2718: // children and reset it's master to us.
2719: verticalDependants.addAll(child.verticalDependants);
2720: child.verticalDependants = null;
2721: child.verticalSizes = null;
2722: } else {
2723: // Child isn't a master.
2724: verticalDependants.add(child);
2725: }
2726: child.verticalMaster = this ;
2727: }
2728: }
2729:
2730: public void clear() {
2731: clear(horizontalSizes);
2732: clear(verticalSizes);
2733: }
2734:
2735: private void clear(int[] sizes) {
2736: if (sizes != null) {
2737: for (int counter = sizes.length - 1; counter >= 0; counter--) {
2738: sizes[counter] = UNSET;
2739: }
2740: }
2741: }
2742:
2743: // NOTE:
2744: // There is a lot of code that follows relating to the type of
2745: // size being requested (min/pref/max). It's currently ignored
2746: // because we can't deal with resizable components that are linked.
2747: // I've left the code in place in case we come up with a way to
2748: // solve resizing linked components.
2749: int getSize(int axis, int type) {
2750: int sizes[] = null;
2751: List dependants = null;
2752: if (axis == HORIZONTAL) {
2753: if (horizontalMaster != this ) {
2754: return horizontalMaster.getSize(axis, type);
2755: }
2756: sizes = horizontalSizes;
2757: dependants = horizontalDependants;
2758: } else if (axis == VERTICAL) {
2759: if (verticalMaster != this ) {
2760: return verticalMaster.getSize(axis, type);
2761: }
2762: sizes = verticalSizes;
2763: dependants = verticalDependants;
2764: }
2765: if (sizes[type] == UNSET) {
2766: sizes[type] = calcSize(dependants, axis, type);
2767: }
2768: return sizes[type];
2769: }
2770:
2771: private int calcSize(List dependants, int axis, int type) {
2772: int count = dependants.size() - 1;
2773: int size = getSize(dependants, axis, type, count--);
2774: while (count >= 0) {
2775: size = Math.max(size, getSize(dependants, axis, type,
2776: count--));
2777: }
2778: return size;
2779: }
2780:
2781: private int getSize(List dependants, int axis, int type,
2782: int index) {
2783: ComponentInfo ci = (ComponentInfo) dependants.get(index);
2784: ComponentSpring spring;
2785: if (axis == HORIZONTAL) {
2786: spring = ci.horizontalSpring;
2787: } else {
2788: spring = ci.verticalSpring;
2789: }
2790: return spring.getPreferredSize1(axis);
2791: // switch(type) {
2792: // case MIN_SIZE:
2793: // return spring.getMinimumSize1(axis);
2794: // case PREF_SIZE:
2795: // return spring.getPreferredSize1(axis);
2796: // default:
2797: // return spring.getMaximumSize1(axis);
2798: // }
2799: }
2800: }
2801: }
|