001: /**
002: * L2FProd.com Common Components 7.3 License.
003: *
004: * Copyright 2005-2007 L2FProd.com
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package com.l2fprod.common.swing;
018:
019: import java.awt.Component;
020: import java.awt.Container;
021: import java.awt.Dimension;
022: import java.awt.Insets;
023: import java.awt.LayoutManager2;
024: import java.util.ArrayList;
025: import java.util.Hashtable;
026: import java.util.Iterator;
027:
028: /**
029: * PercentLayout. <BR>Constraint based layout which allow the space to be
030: * splitted using percentages. The following are allowed when adding components
031: * to container:
032: * <ul>
033: * <li>container.add(component); <br>in this case, the component will be
034: * sized to its preferred size
035: * <li>container.add(component, "100"); <br>in this case, the component will
036: * have a width (or height) of 100
037: * <li>container.add(component, "25%"); <br>in this case, the component will
038: * have a width (or height) of 25 % of the container width (or height) <br>
039: * <li>container.add(component, "*"); <br>in this case, the component will
040: * take the remaining space. if several components use the "*" constraint the
041: * space will be divided among the components.
042: * </ul>
043: *
044: * @javabean.class
045: * name="PercentLayout"
046: * shortDescription="A layout supports constraints expressed in percent."
047: */
048: public class PercentLayout implements LayoutManager2 {
049:
050: /**
051: * Useful constant to layout the components horizontally (from top to
052: * bottom).
053: */
054: public final static int HORIZONTAL = 0;
055:
056: /**
057: * Useful constant to layout the components vertically (from left to right).
058: */
059: public final static int VERTICAL = 1;
060:
061: static class Constraint {
062: protected Object value;
063:
064: private Constraint(Object value) {
065: this .value = value;
066: }
067: }
068:
069: static class NumberConstraint extends Constraint {
070: public NumberConstraint(int d) {
071: this (new Integer(d));
072: }
073:
074: public NumberConstraint(Integer d) {
075: super (d);
076: }
077:
078: public int intValue() {
079: return ((Integer) value).intValue();
080: }
081: }
082:
083: static class PercentConstraint extends Constraint {
084: public PercentConstraint(float d) {
085: super (new Float(d));
086: }
087:
088: public float floatValue() {
089: return ((Float) value).floatValue();
090: }
091: }
092:
093: private final static Constraint REMAINING_SPACE = new Constraint(
094: "*");
095:
096: private final static Constraint PREFERRED_SIZE = new Constraint("");
097:
098: private int orientation;
099: private int gap;
100:
101: private Hashtable m_ComponentToConstraint;
102:
103: /**
104: * Creates a new HORIZONTAL PercentLayout with a gap of 0.
105: */
106: public PercentLayout() {
107: this (HORIZONTAL, 0);
108: }
109:
110: public PercentLayout(int orientation, int gap) {
111: setOrientation(orientation);
112: this .gap = gap;
113:
114: m_ComponentToConstraint = new Hashtable();
115: }
116:
117: public void setGap(int gap) {
118: this .gap = gap;
119: }
120:
121: /**
122: * @javabean.property
123: * bound="true"
124: * preferred="true"
125: */
126: public int getGap() {
127: return gap;
128: }
129:
130: public void setOrientation(int orientation) {
131: if (orientation != HORIZONTAL && orientation != VERTICAL) {
132: throw new IllegalArgumentException(
133: "Orientation must be one of HORIZONTAL or VERTICAL");
134: }
135: this .orientation = orientation;
136: }
137:
138: /**
139: * @javabean.property
140: * bound="true"
141: * preferred="true"
142: */
143: public int getOrientation() {
144: return orientation;
145: }
146:
147: public Constraint getConstraint(Component component) {
148: return (Constraint) m_ComponentToConstraint.get(component);
149: }
150:
151: public void setConstraint(Component component, Object constraints) {
152: if (constraints instanceof Constraint) {
153: m_ComponentToConstraint.put(component, constraints);
154: } else if (constraints instanceof Number) {
155: setConstraint(component, new NumberConstraint(
156: ((Number) constraints).intValue()));
157: } else if ("*".equals(constraints)) {
158: setConstraint(component, REMAINING_SPACE);
159: } else if ("".equals(constraints)) {
160: setConstraint(component, PREFERRED_SIZE);
161: } else if (constraints instanceof String) {
162: String s = (String) constraints;
163: if (s.endsWith("%")) {
164: float value = Float.valueOf(
165: s.substring(0, s.length() - 1)).floatValue() / 100;
166: if (value > 1 || value < 0)
167: throw new IllegalArgumentException(
168: "percent value must be >= 0 and <= 100");
169: setConstraint(component, new PercentConstraint(value));
170: } else {
171: setConstraint(component, new NumberConstraint(Integer
172: .valueOf(s)));
173: }
174: } else if (constraints == null) {
175: // null constraint means preferred size
176: setConstraint(component, PREFERRED_SIZE);
177: } else {
178: throw new IllegalArgumentException("Invalid Constraint");
179: }
180: }
181:
182: public void addLayoutComponent(Component component,
183: Object constraints) {
184: setConstraint(component, constraints);
185: }
186:
187: /**
188: * Returns the alignment along the x axis. This specifies how the component
189: * would like to be aligned relative to other components. The value should be
190: * a number between 0 and 1 where 0 represents alignment along the origin, 1
191: * is aligned the furthest away from the origin, 0.5 is centered, etc.
192: */
193: public float getLayoutAlignmentX(Container target) {
194: return 1.0f / 2.0f;
195: }
196:
197: /**
198: * Returns the alignment along the y axis. This specifies how the component
199: * would like to be aligned relative to other components. The value should be
200: * a number between 0 and 1 where 0 represents alignment along the origin, 1
201: * is aligned the furthest away from the origin, 0.5 is centered, etc.
202: */
203: public float getLayoutAlignmentY(Container target) {
204: return 1.0f / 2.0f;
205: }
206:
207: /**
208: * Invalidates the layout, indicating that if the layout manager has cached
209: * information it should be discarded.
210: */
211: public void invalidateLayout(Container target) {
212: }
213:
214: /**
215: * Adds the specified component with the specified name to the layout.
216: *
217: * @param name the component name
218: * @param comp the component to be added
219: */
220: public void addLayoutComponent(String name, Component comp) {
221: }
222:
223: /**
224: * Removes the specified component from the layout.
225: *
226: * @param comp the component ot be removed
227: */
228: public void removeLayoutComponent(Component comp) {
229: m_ComponentToConstraint.remove(comp);
230: }
231:
232: /**
233: * Calculates the minimum size dimensions for the specified panel given the
234: * components in the specified parent container.
235: *
236: * @param parent the component to be laid out
237: * @see #preferredLayoutSize
238: */
239: public Dimension minimumLayoutSize(Container parent) {
240: return preferredLayoutSize(parent);
241: }
242:
243: /**
244: * Returns the maximum size of this component.
245: *
246: * @see java.awt.Component#getMinimumSize()
247: * @see java.awt.Component#getPreferredSize()
248: * @see java.awt.LayoutManager
249: */
250: public Dimension maximumLayoutSize(Container parent) {
251: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
252: }
253:
254: public Dimension preferredLayoutSize(Container parent) {
255: Component[] components = parent.getComponents();
256: Insets insets = parent.getInsets();
257: int width = 0;
258: int height = 0;
259: Dimension componentPreferredSize;
260: boolean firstVisibleComponent = true;
261: for (int i = 0, c = components.length; i < c; i++) {
262: if (components[i].isVisible()) {
263: componentPreferredSize = components[i]
264: .getPreferredSize();
265: if (orientation == HORIZONTAL) {
266: height = Math.max(height,
267: componentPreferredSize.height);
268: width += componentPreferredSize.width;
269: if (firstVisibleComponent) {
270: firstVisibleComponent = false;
271: } else {
272: width += gap;
273: }
274: } else {
275: height += componentPreferredSize.height;
276: width = Math.max(width,
277: componentPreferredSize.width);
278: if (firstVisibleComponent) {
279: firstVisibleComponent = false;
280: } else {
281: height += gap;
282: }
283: }
284: }
285: }
286: return new Dimension(width + insets.right + insets.left, height
287: + insets.top + insets.bottom);
288: }
289:
290: public void layoutContainer(Container parent) {
291: Insets insets = parent.getInsets();
292: Dimension d = parent.getSize();
293:
294: // calculate the available sizes
295: d.width = d.width - insets.left - insets.right;
296: d.height = d.height - insets.top - insets.bottom;
297:
298: // pre-calculate the size of each components
299: Component[] components = parent.getComponents();
300: int[] sizes = new int[components.length];
301:
302: // calculate the available size
303: int totalSize = (HORIZONTAL == orientation ? d.width : d.height)
304: - (components.length - 1) * gap;
305: int availableSize = totalSize;
306:
307: // PENDING(fred): the following code iterates 4 times on the component
308: // array, need to find something more efficient!
309:
310: // give priority to components who want to use their preferred size or who
311: // have a predefined size
312: for (int i = 0, c = components.length; i < c; i++) {
313: if (components[i].isVisible()) {
314: Constraint constraint = (Constraint) m_ComponentToConstraint
315: .get(components[i]);
316: if (constraint == null || constraint == PREFERRED_SIZE) {
317: sizes[i] = (HORIZONTAL == orientation ? components[i]
318: .getPreferredSize().width
319: : components[i].getPreferredSize().height);
320: availableSize -= sizes[i];
321: } else if (constraint instanceof NumberConstraint) {
322: sizes[i] = ((NumberConstraint) constraint)
323: .intValue();
324: availableSize -= sizes[i];
325: }
326: }
327: }
328:
329: // then work with the components who want a percentage of the remaining
330: // space
331: int remainingSize = availableSize;
332: for (int i = 0, c = components.length; i < c; i++) {
333: if (components[i].isVisible()) {
334: Constraint constraint = (Constraint) m_ComponentToConstraint
335: .get(components[i]);
336: if (constraint instanceof PercentConstraint) {
337: sizes[i] = (int) (remainingSize * ((PercentConstraint) constraint)
338: .floatValue());
339: availableSize -= sizes[i];
340: }
341: }
342: }
343:
344: // finally share the remaining space between the other components
345: ArrayList remaining = new ArrayList();
346: for (int i = 0, c = components.length; i < c; i++) {
347: if (components[i].isVisible()) {
348: Constraint constraint = (Constraint) m_ComponentToConstraint
349: .get(components[i]);
350: if (constraint == REMAINING_SPACE) {
351: remaining.add(new Integer(i));
352: sizes[i] = 0;
353: }
354: }
355: }
356:
357: if (remaining.size() > 0) {
358: int rest = availableSize / remaining.size();
359: for (Iterator iter = remaining.iterator(); iter.hasNext();) {
360: sizes[((Integer) iter.next()).intValue()] = rest;
361: }
362: }
363:
364: // all calculations are done, apply the sizes
365: int currentOffset = (HORIZONTAL == orientation ? insets.left
366: : insets.top);
367:
368: for (int i = 0, c = components.length; i < c; i++) {
369: if (components[i].isVisible()) {
370: if (HORIZONTAL == orientation) {
371: components[i].setBounds(currentOffset, insets.top,
372: sizes[i], d.height);
373: } else {
374: components[i].setBounds(insets.left, currentOffset,
375: d.width, sizes[i]);
376: }
377: currentOffset += gap + sizes[i];
378: }
379: }
380: }
381:
382: }
|