summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/BoxLayout.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/BoxLayout.java')
-rw-r--r--libjava/classpath/javax/swing/BoxLayout.java746
1 files changed, 746 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java
new file mode 100644
index 00000000000..5dfe1d6b735
--- /dev/null
+++ b/libjava/classpath/javax/swing/BoxLayout.java
@@ -0,0 +1,746 @@
+/* BoxLayout.java -- A layout for swing components.
+ Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing;
+
+import java.awt.AWTError;
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager2;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import gnu.java.awt.AWTUtilities;
+
+/**
+ * A layout for swing components.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class BoxLayout implements LayoutManager2, Serializable
+{
+
+ /**
+ * This is an abstraction that allows the BoxLayout algorithm to
+ * be applied to both direction (X and Y) without duplicating the
+ * algorithm. It defines several methods that access properties of
+ * a component for a specific direction.
+ */
+ static interface Direction
+ {
+ /**
+ * Returns the correct part of <code>d</code> for this direction. This will
+ * be <code>d.width</code> for horizontal and <code>d.height</code> for
+ * vertical direction.
+ *
+ * @param d the size as Dimension object
+ *
+ * @return the correct part of <code>d</code> for this direction
+ */
+ int size(Dimension d);
+
+ /**
+ * Returns the lower bounds of the {@link Insets} object according to this
+ * direction. This will be <code>insets.top</code> for vertical direction
+ * and <code>insets.left</code> for horizontal direction.
+ *
+ * @param the {@link Insets} object from which to return the lower bounds
+ *
+ * @return the lower bounds of the {@link Insets} object according to this
+ * direction
+ */
+ int lower(Insets insets);
+
+ /**
+ * Returns the alignment property according to this direction.
+ *
+ * @param comp the Component for which to return the alignment property
+ *
+ * @return the alignment property according to this direction
+ */
+ float alignment(Component comp);
+
+ /**
+ * Sets the location for Component <code>c</code>. <code>coord1</code>
+ * specifies the coordinate of the location in this direction,
+ * <code>coord2</code> the coordinate of the location in the opposite
+ * direction.
+ *
+ * @param c the Component for which to set the location
+ * @param coord1 the coordinate in this direction
+ * @param coord2 the coordinate in the opposite direction
+ */
+ void setLocation(Component c, int coord1, int coord2);
+
+ /**
+ * Sets the size for Component <code>c</code>. <code>coord1</code>
+ * specifies the size in this direction,
+ * <code>coord2</code> the size in the opposite
+ * direction.
+ *
+ * @param c the Component for which to set the size
+ * @param size1 the size in this direction
+ * @param size2 the size in the opposite direction
+ */
+ void setSize(Component c, int size1, int size2);
+ }
+
+ /**
+ * The horizontal direction.
+ */
+ static class Horizontal implements Direction
+ {
+ /**
+ * Returns the correct part of <code>d</code> for this direction. This will
+ * be <code>d.width</code> for horizontal and <code>d.height</code> for
+ * vertical direction.
+ *
+ * @param d the size as Dimension object
+ *
+ * @return the correct part of <code>d</code> for this direction
+ */
+ public int size(Dimension d)
+ {
+ return d.width;
+ }
+
+ /**
+ * Returns the lower bounds of the {@link Insets} object according to this
+ * direction. This will be <code>insets.top</code> for vertical direction
+ * and <code>insets.left</code> for horizontal direction.
+ *
+ * @param the {@link Insets} object from which to return the lower bounds
+ *
+ * @return the lower bounds of the {@link Insets} object according to this
+ * direction
+ */
+ public int lower(Insets insets)
+ {
+ return insets.left;
+ }
+
+ /**
+ * Returns the alignment property according to this direction.
+ *
+ * @param comp the Component for which to return the alignment property
+ *
+ * @return the alignment property according to this direction
+ */
+ public float alignment(Component comp)
+ {
+ return comp.getAlignmentX();
+ }
+
+ /**
+ * Sets the location for Component <code>c</code>. <code>coord1</code>
+ * specifies the coordinate of the location in this direction,
+ * <code>coord2</code> the coordinate of the location in the opposite
+ * direction.
+ *
+ * @param c the Component for which to set the location
+ * @param coord1 the coordinate in this direction
+ * @param coord2 the coordinate in the opposite direction
+ */
+ public void setLocation(Component c, int coord1, int coord2)
+ {
+ c.setLocation(coord1, coord2);
+ }
+
+ /**
+ * Sets the size for Component <code>c</code>. <code>coord1</code>
+ * specifies the size in this direction,
+ * <code>coord2</code> the size in the opposite
+ * direction.
+ *
+ * @param c the Component for which to set the size
+ * @param size1 the size in this direction
+ * @param size2 the size in the opposite direction
+ */
+ public void setSize(Component c, int size1, int size2)
+ {
+ c.setSize(size1, size2);
+ }
+ }
+ /**
+ * The vertical direction.
+ */
+ static class Vertical implements Direction
+ {
+ /**
+ * Returns the correct part of <code>d</code> for this direction. This will
+ * be <code>d.width</code> for horizontal and <code>d.height</code> for
+ * vertical direction.
+ *
+ * @param d the size as Dimension object
+ *
+ * @return the correct part of <code>d</code> for this direction
+ */
+ public int size(Dimension d)
+ {
+ return d.height;
+ }
+
+ /**
+ * Returns the lower bounds of the {@link Insets} object according to this
+ * direction. This will be <code>insets.top</code> for vertical direction
+ * and <code>insets.left</code> for horizontal direction.
+ *
+ * @param the {@link Insets} object from which to return the lower bounds
+ *
+ * @return the lower bounds of the {@link Insets} object according to this
+ * direction
+ */
+ public int lower(Insets insets)
+ {
+ return insets.top;
+ }
+
+ /**
+ * Returns the alignment property according to this direction.
+ *
+ * @param comp the Component for which to return the alignment property
+ *
+ * @return the alignment property according to this direction
+ */
+ public float alignment(Component comp)
+ {
+ return comp.getAlignmentY();
+ }
+
+ /**
+ * Sets the location for Component <code>c</code>. <code>coord1</code>
+ * specifies the coordinate of the location in this direction,
+ * <code>coord2</code> the coordinate of the location in the opposite
+ * direction.
+ *
+ * @param c the Component for which to set the location
+ * @param coord1 the coordinate in this direction
+ * @param coord2 the coordinate in the opposite direction
+ */
+ public void setLocation(Component c, int coord1, int coord2)
+ {
+ c.setLocation(coord2, coord1);
+ }
+
+ /**
+ * Sets the size for Component <code>c</code>. <code>coord1</code>
+ * specifies the size in this direction,
+ * <code>coord2</code> the size in the opposite
+ * direction.
+ *
+ * @param c the Component for which to set the size
+ * @param size1 the size in this direction
+ * @param size2 the size in the opposite direction
+ */
+ public void setSize(Component c, int size1, int size2)
+ {
+ c.setSize(size2, size1);
+ }
+ }
+
+ /**
+ * A helper class that temporarily stores the size specs of a component.
+ */
+ static class SizeReq
+ {
+ int size;
+ int min;
+ int pref;
+ int max;
+ float align;
+ Component comp;
+ SizeReq(Component comp, Direction dir)
+ {
+ this.min = dir.size(comp.getMinimumSize());
+ this.pref = dir.size(comp.getPreferredSize());
+ this.max = dir.size(comp.getMaximumSize());
+ this.size = dir.size(comp.getSize());
+ this.align = dir.alignment(comp);
+ this.comp = comp;
+ }
+ }
+
+ /**
+ * Specifies that components are laid out left to right.
+ */
+ public static final int X_AXIS = 0;
+
+ /**
+ * Specifies that components are laid out top to bottom.
+ */
+ public static final int Y_AXIS = 1;
+
+ /**
+ * Specifies that components are laid out in the direction of a line of text.
+ */
+ public static final int LINE_AXIS = 2;
+
+ /**
+ * Sepcifies that components are laid out in the direction of the line flow.
+ */
+ public static final int PAGE_AXIS = 3;
+
+ /*
+ * Needed for serialization.
+ */
+ private static final long serialVersionUID = -2474455742719112368L;
+
+ /*
+ * The container given to the constructor.
+ */
+ private Container container;
+
+ /*
+ * Current type of component layouting. Defaults to X_AXIS.
+ */
+ private int way = X_AXIS;
+
+ /** Constant for the horizontal direction. */
+ private static final Direction HORIZONTAL = new Horizontal();
+
+ /** Constant for the vertical direction. */
+ private static final Direction VERTICAL = new Vertical();
+
+ /**
+ * Constructs a <code>BoxLayout</code> object.
+ *
+ * @param container The container that needs to be laid out.
+ * @param way The orientation of the components.
+ *
+ * @exception AWTError If way has an invalid value.
+ */
+ public BoxLayout(Container container, int way)
+ {
+ int width = 0;
+ int height = 0;
+ this.container = container;
+ this.way = way;
+ }
+
+ /**
+ * Adds a component to the layout. Not used in BoxLayout.
+ *
+ * @param name The name of the component to add.
+ * @param component the component to add to the layout.
+ */
+ public void addLayoutComponent(String name, Component component)
+ {
+ }
+
+ /**
+ * Removes a component from the layout. Not used in BoxLayout.
+ *
+ * @param component The component to remove from the layout.
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ }
+
+ private boolean isHorizontalIn(Container parent)
+ {
+ ComponentOrientation orientation = parent.getComponentOrientation();
+ return this.way == X_AXIS
+ || (this.way == LINE_AXIS
+ && orientation.isHorizontal())
+ || (this.way == PAGE_AXIS
+ && (!orientation.isHorizontal()));
+ }
+
+
+
+ /**
+ * Returns the preferred size of the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ if (parent != container)
+ throw new AWTError("invalid parent");
+
+ Insets insets = parent.getInsets();
+ int x = 0;
+ int y = 0;
+
+ List children = AWTUtilities.getVisibleChildren(parent);
+
+ if (isHorizontalIn(parent))
+ {
+ x = insets.left + insets.right;
+ // sum up preferred widths of components, find maximum of preferred
+ // heights
+ for (Iterator i = children.iterator(); i.hasNext();)
+ {
+ Component comp = (Component) i.next();
+ Dimension sz = comp.getPreferredSize();
+ x += sz.width;
+ y = Math.max(y, sz.height);
+ }
+ y += insets.bottom + insets.top;
+ }
+ else
+ {
+ y = insets.top + insets.bottom;
+ // sum up preferred heights of components, find maximum of
+ // preferred widths
+ for (Iterator i = children.iterator(); i.hasNext();)
+ {
+ Component comp = (Component) i.next();
+ Dimension sz = comp.getPreferredSize();
+ y += sz.height;
+ x = Math.max(x, sz.width);
+ }
+ x += insets.left + insets.right;
+ }
+
+ return new Dimension(x, y);
+ }
+
+ /**
+ * Returns the minimum size of the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ if (parent != container)
+ throw new AWTError("invalid parent");
+
+ Insets insets = parent.getInsets();
+ int x = insets.left + insets.right;
+ int y = insets.bottom + insets.top;
+
+ List children = AWTUtilities.getVisibleChildren(parent);
+
+ if (isHorizontalIn(parent))
+ {
+ // sum up preferred widths of components, find maximum of preferred
+ // heights
+ for (Iterator i = children.iterator(); i.hasNext();)
+ {
+ Component comp = (Component) i.next();
+ Dimension sz = comp.getMinimumSize();
+ x += sz.width;
+ y = Math.max(y, sz.height);
+ }
+ }
+ else
+ {
+ // sum up preferred heights of components, find maximum of
+ // preferred widths
+ for (Iterator i = children.iterator(); i.hasNext();)
+ {
+ Component comp = (Component) i.next();
+ Dimension sz = comp.getMinimumSize();
+ y += sz.height;
+ x = Math.max(x, sz.width);
+ }
+ }
+
+ return new Dimension(x, y);
+ }
+
+ /**
+ * Lays out the specified container using this layout.
+ *
+ * @param parent The container that needs to be laid out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ if (isHorizontalIn(parent))
+ layoutAlgorithm(parent, HORIZONTAL, VERTICAL);
+ else
+ layoutAlgorithm(parent, VERTICAL, HORIZONTAL);
+ }
+
+ /**
+ * Adds a component to the layout. Not used in BoxLayout
+ *
+ * @param child The component to add to the layout.
+ * @param constraints The constraints for the component in the layout.
+ */
+ public void addLayoutComponent(Component child, Object constraints)
+ {
+ }
+
+ /**
+ * Returns the alignment along the X axis for the container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The alignment.
+ */
+ public float getLayoutAlignmentX(Container parent)
+ {
+ if (parent != container)
+ throw new AWTError("invalid parent");
+
+ return 0;
+ }
+
+ /**
+ * Returns the alignment along the Y axis for the container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The alignment.
+ */
+ public float getLayoutAlignmentY(Container parent)
+ {
+ if (parent != container)
+ throw new AWTError("invalid parent");
+
+ return 0;
+ }
+
+ /**
+ * Invalidates the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ */
+ public void invalidateLayout(Container parent)
+ {
+ if (parent != container)
+ throw new AWTError("invalid parent");
+ }
+
+ /**
+ * Returns the maximum size of the layout gived the components
+ * in the given container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension maximumLayoutSize(Container parent)
+ {
+ if (parent != container)
+ throw new AWTError("invalid parent");
+
+ Insets insets = parent.getInsets();
+ int x = insets.left + insets.right;
+ int y = insets.top + insets.bottom;
+
+ List children = AWTUtilities.getVisibleChildren(parent);
+
+ if (isHorizontalIn(parent))
+ {
+
+ // sum up preferred widths of components, find maximum of preferred
+ // heights
+ for (Iterator i = children.iterator(); i.hasNext();)
+ {
+ Component comp = (Component) i.next();
+ Dimension sz = comp.getMaximumSize();
+ x += sz.width;
+ // Check for overflow.
+ if (x < 0)
+ x = Integer.MAX_VALUE;
+ y = Math.max(y, sz.height);
+ }
+ }
+ else
+ {
+ // sum up preferred heights of components, find maximum of
+ // preferred widths
+ for (Iterator i = children.iterator(); i.hasNext();)
+ {
+ Component comp = (Component) i.next();
+ Dimension sz = comp.getMaximumSize();
+ y += sz.height;
+ // Check for overflow
+ if (y < 0)
+ y = Integer.MAX_VALUE;
+ x = Math.max(x, sz.width);
+ }
+ }
+ return new Dimension(x, y);
+ }
+
+ /**
+ * Lays out the Container <code>c</code> in the layout direction
+ * <code>layoutDir</code>. The direction that is crossing the layout
+ * direction is specified in <code>crossDir</code>.
+ *
+ * @param parent
+ * @param layoutDir
+ * @param crossDir
+ */
+ void layoutAlgorithm(Container parent, Direction layoutDir, Direction crossDir)
+ {
+ if (parent != container)
+ throw new AWTError("invalid parent");
+
+ Dimension parentSize = parent.getSize();
+ Insets insets = parent.getInsets();
+ Dimension innerSize = new Dimension(parentSize.width - insets.left
+ - insets.right, parentSize.height
+ - insets.bottom - insets.top);
+
+ // Set all components to their preferredSizes and sum up the allocated
+ // space. Create SizeReqs for each component and store them in
+ // sizeReqs. Find the maximum size in the crossing direction.
+ List children = AWTUtilities.getVisibleChildren(parent);
+ Vector sizeReqs = new Vector();
+ int allocated = 0;
+ for (Iterator i = children.iterator(); i.hasNext();)
+ {
+ Component c = (Component) i.next();
+ SizeReq sizeReq = new SizeReq(c, layoutDir);
+ int preferred = layoutDir.size(c.getPreferredSize());
+ sizeReq.size = preferred;
+ allocated += preferred;
+ sizeReqs.add(sizeReq);
+ }
+
+ // Distribute remaining space (may be positive or negative) over components
+ int remainder = layoutDir.size(innerSize) - allocated;
+ distributeSpace(sizeReqs, remainder, layoutDir);
+
+ // Resize and relocate components. If the component can be sized to
+ // take the full space in the crossing direction, then do so, otherwise
+ // align according to its alingnmentX or alignmentY property.
+ int loc = 0;
+ int offset1 = layoutDir.lower(insets);
+ int offset2 = crossDir.lower(insets);
+ for (Iterator i = sizeReqs.iterator(); i.hasNext();)
+ {
+ SizeReq sizeReq = (SizeReq) i.next();
+ Component c = sizeReq.comp;
+ int availCrossSize = crossDir.size(innerSize);
+ int maxCross = crossDir.size(c.getMaximumSize());
+ int crossSize = Math.min(availCrossSize, maxCross);
+ int crossRemainder = availCrossSize - crossSize;
+ int crossLoc = (int) (crossDir.alignment(c) * crossRemainder);
+ layoutDir.setSize(c, sizeReq.size, crossSize);
+ layoutDir.setLocation(c, offset1 + loc, offset2 + crossLoc);
+ loc += sizeReq.size;
+ }
+ }
+
+ /**
+ * Distributes some space over a set of components. This implementation
+ * tries to set the components as close as possible to their
+ * <code>preferredSize</code>s, and respects the components
+ * <code>minimumSize</code> and <code>maximumSize</code>.
+ *
+ * The algorithm is implemented as follows:
+ *
+ * <ul>
+ * <li>The <code>remainder</code> is divided by the number of components
+ * in <code>freeComponents</code>.</li>
+ * <li>The result is added to (or substracted from) the size of each
+ * component.</li>
+ * <li>If the <code>minimumSize</code> or <code>maximumSize</code> of a
+ * component is exceeded, then this component is set to its
+ * <code>minimumSize</code> or <code>maximumSize</code>, it is removed from
+ * <code>freeComponents</code> and the difference is added to a new
+ * remainder.</li>
+ * <li>Finally, if there is a new remainer != 0 and the
+ * <code>freeComponents.size() != 0</code>, then this method is called
+ * recursivly to distribute the newly allocated remaining space.</li>
+ * </ul>
+ *
+ * @param freeComponents a SizeReq collection for components that have space
+ * left so that they can be moved freely
+ * @param remainder the space that should be distributed between the
+ * components
+ * @param dir the direction in which we operate
+ */
+ void distributeSpace(Collection freeComponents, int remainder, Direction dir)
+ {
+ // Sum up total available space in components. If the remainder is negative
+ // then we sum up the difference between minSize and size. If remainder
+ // is positive we sum up the difference between maxSize and size.
+ double totalAvailable = 0;
+ for (Iterator i = freeComponents.iterator(); i.hasNext();)
+ {
+ SizeReq sizeReq = (SizeReq) i.next();
+ if (remainder >= 0)
+ totalAvailable += sizeReq.max - sizeReq.size;
+ else
+ totalAvailable += sizeReq.min - sizeReq.size;
+ }
+ if (totalAvailable == 0)
+ if (remainder >= 0)
+ totalAvailable = 1;
+ else
+ totalAvailable = -1;
+
+ int newRemainder = 0;
+ Vector stillFree = new Vector();
+ for (Iterator i = freeComponents.iterator(); i.hasNext();)
+ {
+ // Add/substract share to component.
+ SizeReq sizeReq = (SizeReq) i.next();
+ double available = 0;
+ if (remainder >= 0)
+ available = sizeReq.max - sizeReq.size;
+ else
+ available = sizeReq.min - sizeReq.size;
+ int share = (int) ((available / totalAvailable) * remainder);
+ sizeReq.size += share;
+ // check for min/maximumSize
+ if (sizeReq.size < sizeReq.min)
+ {
+ newRemainder += sizeReq.size - sizeReq.min;
+ sizeReq.size = sizeReq.min;
+ }
+ else if (sizeReq.size > sizeReq.max)
+ {
+ newRemainder += sizeReq.size - sizeReq.max;
+ sizeReq.size = sizeReq.max;
+ }
+ else
+ stillFree.add(sizeReq);
+ }
+ // recursivly call this method if necessary
+ if (newRemainder != 0 && stillFree.size() > 0)
+ distributeSpace(stillFree, newRemainder, dir);
+ }
+}