1 /* SpringLayout.java --
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.awt.Component;
42 import java.awt.Container;
43 import java.awt.Dimension;
44 import java.awt.LayoutManager2;
46 import java.util.HashMap;
49 import javax.swing.Spring;
52 * A very flexible layout manager. Components are laid out by defining the
53 * relationships between them. The relationships are expressed as
54 * {@link Spring}s. You can attach a Spring for each edge of a component and
55 * link it to an edge of a different component. For example, you can say,
56 * the northern edge of component A should be attached to the southern edge
57 * of component B, and the space between them should be something between
58 * x and y pixels, and preferably z pixels.
59 * <p>While quite simple, this layout manager can be used to emulate most other
60 * layout managers, and can also be used to solve some layout problems, which
61 * would be hard to solve with other layout managers.</p>
63 * @author Roman Kennke (roman@ontographics.com)
65 public class SpringLayout implements LayoutManager2
68 /** The right edge of a component. */
69 public static final String EAST = "East";
71 /** The top edge of a component. */
72 public static final String NORTH = "North";
74 /** The bottom edge of a component. */
75 public static final String SOUTH = "South";
77 /** The left edge of a component. */
78 public static final String WEST = "West";
80 /** maps components to their constraints. */
81 private Map constraintsMap;
84 * The constraints that define the relationships between components.
85 * Each Constraints object can hold 4 Springs: one for each edge of the
86 * component. Additionally it can hold Springs for the components width
87 * and the components height. Since the height and width constraints are
88 * dependend on the other constraints, a component can be over-constraint.
89 * In this case (like when all of NORTH, SOUTH and HEIGHT are constraint),
90 * the values are adjusted, so that the mathematics still hold true.
92 * @author Roman Kennke (roman@ontographics.com)
94 public final static class Constraints
97 // The constraints for each edge, and width and height.
98 /** The Spring for the left edge. */
101 /** The Spring for the upper edge. */
104 /** The Spring for the height. */
105 private Spring height;
107 /** The Spring for the width. */
108 private Spring width;
110 /** The Spring for the right edge. */
113 /** The Spring for the bottom edge. */
114 private Spring south;
117 * Creates a new Constraints object.
118 * There is no constraint set.
122 x = y = height = width = east = south = null;
126 * Creates a new Constraints object.
128 * @param x the constraint for the left edge of the component.
129 * @param y the constraint for the upper edge of the component.
131 public Constraints(Spring x, Spring y)
135 width = height = east = south = null;
139 * Creates a new Constraints object.
141 * @param x the constraint for the left edge of the component.
142 * @param y the constraint for the upper edge of the component.
143 * @param width the constraint for the width of the component.
144 * @param height the constraint for the height of the component.
146 public Constraints(Spring x, Spring y, Spring width, Spring height)
151 this.height = height;
156 * Returns the constraint for the edge with the <code>edgeName</code>.
157 * This is expected to be one of
158 * {@link #EAST}, {@link #WEST}, {@link NORTH} or {@link SOUTH}.
160 * @param edgeName the name of the edge.
161 * @return the constraint for the specified edge.
163 public Spring getConstraint(String edgeName)
165 Spring retVal = null;
166 if (edgeName.equals(SpringLayout.NORTH))
168 else if (edgeName.equals(SpringLayout.WEST))
170 else if (edgeName.equals(SpringLayout.SOUTH))
173 if ((retVal == null) && (y != null) && (height != null))
174 retVal = Spring.sum(y, height);
176 else if (edgeName.equals(SpringLayout.EAST))
179 if ((retVal == null) && (x != null) && (width != null))
180 retVal = Spring.sum(x, width);
187 * Returns the constraint for the height of the component.
189 * @return the height constraint.
191 public Spring getHeight()
193 Spring retVal = height;
194 if ((retVal == null) && (y != null) && (south != null))
196 retVal = Spring.sum(south, Spring.minus(y));
202 * Returns the constraint for the width of the component.
204 * @return the width constraint.
206 public Spring getWidth()
208 Spring retVal = width;
209 if ((retVal == null) && (x != null) && (east != null))
211 retVal = Spring.sum(east, Spring.minus(x));
217 * Returns the constraint for the left edge of the component.
219 * @return the left-edge constraint (== WEST).
224 if ((retVal == null) && (width != null) && (east != null))
226 retVal = Spring.sum(east, Spring.minus(width));
232 * Returns the constraint for the upper edge of the component.
234 * @return the upper-edge constraint (== NORTH).
239 if ((retVal == null) && (height != null) && (south != null))
241 retVal = Spring.sum(south, Spring.minus(height));
247 * Sets a constraint for the specified edge. If this leads to an
248 * over-constrained situation, the constraints get adjusted, so that
249 * the mathematics still hold true.
251 * @param edgeName the name of the edge, one of {@link #EAST},
252 * {@link #WEST}, {@link NORTH} or {@link SOUTH}.
253 * @param s the constraint to be set.
255 public void setConstraint(String edgeName, Spring s)
258 if (edgeName.equals(SpringLayout.WEST))
261 if ((width != null) && (east != null))
262 width = Spring.sum(east, Spring.minus(x));
264 else if (edgeName.equals(SpringLayout.NORTH))
267 if ((height != null) && (south != null))
268 height = Spring.sum(south, Spring.minus(y));
270 else if (edgeName.equals(SpringLayout.EAST))
273 if ((x != null) && (width != null))
274 x = Spring.sum(east, Spring.minus(width));
276 else if (edgeName.equals(SpringLayout.SOUTH))
279 if ((height != null) && (y != null))
280 y = Spring.sum(south, Spring.minus(height));
286 * Sets the height-constraint.
288 * @param s the constraint to be set.
290 public void setHeight(Spring s)
293 if ((south != null) && (y != null))
294 south = Spring.sum(y, height);
299 * Sets the width-constraint.
301 * @param s the constraint to be set.
303 public void setWidth(Spring s)
306 if ((east != null) && (x != null))
307 east = Spring.sum(x, width);
312 * Sets the WEST-constraint.
314 * @param s the constraint to be set.
316 public void setX(Spring s)
319 if ((width != null) && (east != null))
320 width = Spring.sum(east, Spring.minus(x));
325 * Sets the NORTH-constraint.
327 * @param s the constraint to be set.
329 public void setY(Spring s)
332 if ((height != null) && (south != null))
333 height = Spring.sum(south, Spring.minus(y));
339 * Creates a new SpringLayout.
341 public SpringLayout()
344 constraintsMap = new HashMap();
348 * Adds a layout component and a constraint object to this layout.
349 * This method is usually only called by a {@java.awt.Container}s add
352 * @param component the component to be added.
353 * @param constraint the constraint to be set.
355 public void addLayoutComponent(Component component, Object constraint)
357 constraintsMap.put(component, constraint);
362 * Adds a layout component and a constraint object to this layout.
363 * This method is usually only called by a {@java.awt.Container}s add
364 * Method. This method does nothing, since SpringLayout does not manage
365 * String-indexed components.
367 * @param component the component to be added.
368 * @param constraint the constraint to be set.
370 public void addLayoutComponent(String name, Component c)
376 * Returns the constraint of the edge named by <code>edgeName</code>.
378 * @param c the component from which to get the constraint.
379 * @param edgeName the name of the edge, one of {@link #EAST},
380 * {@link #WEST}, {@link NORTH} or {@link SOUTH}.
381 * @return the constraint of the edge <code>edgeName</code> of the
384 public Spring getConstraint(String edgeName, Component c)
386 Constraints constraints = getConstraints(c);
387 return constraints.getConstraint(edgeName);
391 * Returns the {@link Constraints} object associated with the specified
394 * @param c the component for which to determine the constraint.
395 * @return the {@link Constraints} object associated with the specified
398 public SpringLayout.Constraints getConstraints(Component c)
400 Constraints constraints = (Constraints) constraintsMap.get(c);
401 if (constraints == null)
403 Container parent = c.getParent();
404 constraints = new Constraints();
408 (Spring.constant(parent.getInsets().left));
410 (Spring.constant(parent.getInsets().top));
415 (Spring.constant(0));
417 (Spring.constant(0));
421 (Spring.constant(c.getMinimumSize().width,
422 c.getPreferredSize().width,
423 c.getMaximumSize().width));
424 constraints.setHeight
425 (Spring.constant(c.getMinimumSize().height,
426 c.getPreferredSize().height,
427 c.getMaximumSize().height));
429 constraintsMap.put(c, constraints);
437 * Returns the X alignment of the Container <code>p</code>.
439 * @param p the {@link java.awt.Container} for which to determine the X
443 public float getLayoutAlignmentX(Container p)
449 * Returns the Y alignment of the Container <code>p</code>.
451 * @param p the {@link java.awt.Container} for which to determine the Y
455 public float getLayoutAlignmentY(Container p)
461 * Recalculate a possibly cached layout.
463 public void invalidateLayout(Container p)
465 // nothing to do here yet
469 * Lays out the container <code>p</code>.
471 * @param p the container to be laid out.
473 public void layoutContainer(Container p)
476 addLayoutComponent(p, new Constraints(Spring.constant(0),
477 Spring.constant(0)));
479 int offsetX = p.getInsets().left;
480 int offsetY = p.getInsets().right;
482 Component[] components = p.getComponents();
483 for (int index = 0; index < components.length; index++)
485 Component c = components[index];
486 Constraints constraints = getConstraints(c);
487 int x = constraints.getX().getValue();
488 int y = constraints.getY().getValue();
489 int width = constraints.getWidth().getValue();
490 int height = constraints.getHeight().getValue();
492 c.setLocation(x + offsetX, y + offsetY);
493 c.setSize(width, height);
499 * Calculates the maximum size of the layed out container. This
500 * respects the maximum sizes of all contained components.
502 * @param p the container to be laid out.
503 * @return the maximum size of the container.
505 public Dimension maximumLayoutSize(Container p)
510 int offsetX = p.getInsets().left;
511 int offsetY = p.getInsets().right;
513 Component[] components = p.getComponents();
514 for (int index = 0; index < components.length; index++)
516 Component c = components[index];
517 Constraints constraints = getConstraints(c);
518 int x = constraints.getX().getMaximumValue();
519 int y = constraints.getY().getMaximumValue();
520 int width = constraints.getWidth().getMaximumValue();
521 int height = constraints.getHeight().getMaximumValue();
523 int rightEdge = offsetX + x + width;
524 if (rightEdge > maxX)
526 int bottomEdge = offsetY + y + height;
527 if (bottomEdge > maxY)
531 return new Dimension(maxX, maxY);
536 * Calculates the minimum size of the layed out container. This
537 * respects the minimum sizes of all contained components.
539 * @param p the container to be laid out.
540 * @return the minimum size of the container.
542 public Dimension minimumLayoutSize(Container p)
547 int offsetX = p.getInsets().left;
548 int offsetY = p.getInsets().right;
550 Component[] components = p.getComponents();
551 for (int index = 0; index < components.length; index++)
553 Component c = components[index];
554 Constraints constraints = getConstraints(c);
555 int x = constraints.getX().getMinimumValue();
556 int y = constraints.getY().getMinimumValue();
557 int width = constraints.getWidth().getMinimumValue();
558 int height = constraints.getHeight().getMinimumValue();
560 int rightEdge = offsetX + x + width;
561 if (rightEdge > maxX)
563 int bottomEdge = offsetY + y + height;
564 if (bottomEdge > maxY)
568 return new Dimension(maxX, maxY);
572 * Calculates the preferred size of the layed out container. This
573 * respects the preferred sizes of all contained components.
575 * @param p the container to be laid out.
576 * @return the preferred size of the container.
578 public Dimension preferredLayoutSize(Container p)
583 int offsetX = p.getInsets().left;
584 int offsetY = p.getInsets().right;
586 Component[] components = p.getComponents();
587 for (int index = 0; index < components.length; index++)
589 Component c = components[index];
590 Constraints constraints = getConstraints(c);
591 int x = constraints.getX().getPreferredValue();
592 int y = constraints.getY().getPreferredValue();
593 int width = constraints.getWidth().getPreferredValue();
594 int height = constraints.getHeight().getPreferredValue();
596 int rightEdge = offsetX + x + width;
597 if (rightEdge > maxX)
599 int bottomEdge = offsetY + y + height;
600 if (bottomEdge > maxY)
604 return new Dimension(maxX, maxY);
608 * Attaches the edge <code>e1</code> of component <code>c1</code> to
609 * the edge <code>e2</code> of component <code>c2</code> width the
610 * fixed strut <code>pad</code>.
612 * @param e1 the edge of component 1.
613 * @param c1 the component 1.
614 * @param pad the space between the components in pixels.
615 * @param e2 the edge of component 2.
616 * @param c2 the component 2.
618 public void putConstraint(String e1, Component c1, int pad, String e2,
621 Constraints constraints1 = getConstraints(c1);
622 Constraints constraints2 = getConstraints(c2);
624 Spring strut = Spring.constant(pad);
625 Spring otherEdge = constraints2.getConstraint(e2);
626 constraints1.setConstraint(e1, Spring.sum(strut, otherEdge));
631 * Attaches the edge <code>e1</code> of component <code>c1</code> to
632 * the edge <code>e2</code> of component <code>c2</code> width the
633 * {@link Spring} <code>s</code>.
635 * @param e1 the edge of component 1.
636 * @param c1 the component 1.
637 * @param s the space between the components as a {@link Spring} object.
638 * @param e2 the edge of component 2.
639 * @param c2 the component 2.
641 public void putConstraint(String e1, Component c1, Spring s, String e2,
644 Constraints constraints1 = getConstraints(c1);
645 Constraints constraints2 = getConstraints(c2);
647 Spring otherEdge = constraints2.getConstraint(e2);
648 constraints1.setConstraint(e1, Spring.sum(s, otherEdge));
653 * Removes a layout component.
654 * @param c the layout component to remove.
656 public void removeLayoutComponent(Component c)