001: /*
002: * StatusBarLayout.java
003: *
004: * Copyright (C) 2002, 2003, 2004, 2005, 2006 Takis Diakoumis
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021:
022: package org.underworldlabs.swing;
023:
024: import java.awt.Component;
025: import java.awt.Container;
026: import java.awt.Dimension;
027: import java.awt.Insets;
028: import java.awt.LayoutManager2;
029: import java.awt.Rectangle;
030: import java.io.Serializable;
031: import java.util.Collections;
032: import java.util.Comparator;
033: import java.util.Enumeration;
034: import java.util.Hashtable;
035: import java.util.Vector;
036:
037: /* ----------------------------------------------------------
038: * CVS NOTE: Changes to the CVS repository prior to the
039: * release of version 3.0.0beta1 has meant a
040: * resetting of CVS revision numbers.
041: * ----------------------------------------------------------
042: */
043:
044: /**
045: * Simple horizontal layout where components are effectively set
046: * to a specified size and resized optionally to fill the width
047: * of a status bar within a frame, panel etc.
048: * Effective when some labels within a status bar are a fixed width
049: * and where others should fill the remaining space.
050: *
051: * @author Takis Diakoumis
052: * @version $Revision: 1.4 $
053: * @date $Date: 2006/05/14 06:56:07 $
054: */
055: public class StatusBarLayout implements LayoutManager2, Serializable {
056:
057: /** the height of this status bar */
058: private int height;
059:
060: /** default layout height of 20px */
061: public static final int DEFAULT_HEIGHT = 20;
062:
063: /** The constraints/component pairs */
064: private Hashtable<StatusBarLayoutConstraints, Component> componentsMap;
065:
066: /** The tool bar constraints */
067: private Vector<StatusBarLayoutConstraints> constraintsList;
068:
069: /** the constraints comparator for ordering */
070: private ConstraintsComparator comparator;
071:
072: /** Creates a new instance of StatusBarLayout */
073: public StatusBarLayout() {
074: this (DEFAULT_HEIGHT);
075: }
076:
077: /**
078: * Creates a new instance of StatusBarLayout with
079: * the specified height.
080: *
081: * @param the height of the status bar
082: */
083: public StatusBarLayout(int height) {
084: this .height = height;
085: constraintsList = new Vector<StatusBarLayoutConstraints>();
086: componentsMap = new Hashtable<StatusBarLayoutConstraints, Component>();
087: comparator = new ConstraintsComparator();
088: }
089:
090: /**
091: * Indicates that a child has changed its layout related information,
092: * and thus any cached calculations should be flushed.
093: * <p>
094: * This method is called by AWT when the invalidate method is called
095: * on the Container. Since the invalidate method may be called
096: * asynchronously to the event thread, this method may be called
097: * asynchronously.
098: *
099: * @param target the affected container
100: */
101: public synchronized void invalidateLayout(Container target) {
102: }
103:
104: /**
105: * Not used by this class.
106: *
107: * @param name the name of the component
108: * @param comp the component
109: */
110: public void addLayoutComponent(String name, Component comp) {
111: }
112:
113: /**
114: * Not used by this class.
115: *
116: * @param comp the component
117: */
118: public void removeLayoutComponent(Component comp) {
119: if (componentsMap.containsValue(comp)) {
120: for (Enumeration i = componentsMap.keys(); i
121: .hasMoreElements();) {
122: Object object = i.nextElement();
123: if (componentsMap.get(object) == comp) {
124: componentsMap.remove(object);
125: constraintsList.remove(object);
126: break;
127: }
128: }
129: }
130: }
131:
132: /**
133: * Adds the specified component with the specified constraints
134: * to the layout. Constraints must be an instance of
135: * <code>StatusBarLayoutConstraints</code>.
136: *
137: * @param comp the component
138: * @param constraints constraints
139: */
140: public void addLayoutComponent(Component comp, Object constraints) {
141: //StatusBarLayoutConstraints
142: if (constraints instanceof StatusBarLayoutConstraints) {
143:
144: StatusBarLayoutConstraints _constraints = (StatusBarLayoutConstraints) constraints;
145:
146: componentsMap.put(_constraints, comp);
147: constraintsList.add(_constraints);
148:
149: } else if (constraints != null) {
150: throw new IllegalArgumentException(
151: "cannot add to layout: constraints must be a StatusBarLayoutConstraints");
152: }
153:
154: }
155:
156: /**
157: * Returns the preferred dimensions for this layout, given the components
158: * in the specified target container.
159: *
160: * @param target the container that needs to be laid out
161: * @return the dimension
162: */
163: public Dimension preferredLayoutSize(Container target) {
164: // parent container size
165: Dimension targetDim = target.getSize();
166:
167: // calculate component layout positions
168: int _width = 0;
169: int _height = height;
170:
171: Rectangle[] rects = computePositions(target);
172: for (int i = 0; i < rects.length; i++) {
173: _width += rects[i].width;
174: }
175:
176: Insets insets = target.getInsets();
177: _width += (insets.left + insets.right);
178: _height += (insets.top + insets.bottom);// + 1;
179:
180: return new Dimension(_width, _height);
181: }
182:
183: /**
184: * Returns the minimum dimensions needed to lay out the components
185: * contained in the specified target container.
186: *
187: * @param target the container that needs to be laid out
188: * @return the dimension
189: */
190: public Dimension minimumLayoutSize(Container target) {
191: //return preferredLayoutSize(target);
192: int _height = height;
193: Insets insets = target.getInsets();
194: _height += (insets.top + insets.bottom);
195: return new Dimension(1, _height);
196: }
197:
198: /**
199: * Returns the maximum dimensions the target container can use
200: * to lay out the components it contains.
201: *
202: * @param target the container that needs to be laid out
203: * @return the dimenion
204: */
205: public Dimension maximumLayoutSize(Container target) {
206: return preferredLayoutSize(target);
207: }
208:
209: /**
210: * Returns the alignment along the X axis for the container.
211: * If the box is horizontal, the default
212: * alignment will be returned. Otherwise, the alignment needed
213: * to place the children along the X axis will be returned.
214: *
215: * @param target the container
216: * @return the alignment >= 0.0f && <= 1.0f
217: */
218: public synchronized float getLayoutAlignmentX(Container target) {
219: return 0.5f;
220: }
221:
222: /**
223: * Returns the alignment along the Y axis for the container.
224: * If the box is vertical, the default
225: * alignment will be returned. Otherwise, the alignment needed
226: * to place the children along the Y axis will be returned.
227: *
228: * @param target the container
229: * @return the alignment >= 0.0f && <= 1.0f
230: */
231: public synchronized float getLayoutAlignmentY(Container target) {
232: return 0.5f;
233: }
234:
235: /**
236: * Called by the AWT <!-- XXX CHECK! --> when the specified container
237: * needs to be laid out.
238: *
239: * @param target the container to lay out
240: *
241: * @exception AWTError if the target isn't the container specified to the
242: * BoxLayout constructor
243: */
244: public void layoutContainer(Container target) {
245:
246: // order the indexes
247: Collections.sort(constraintsList, comparator);
248:
249: // calculate component layout positions
250: Rectangle[] rects = computePositions(target);
251:
252: // flush changes to the container
253: for (int i = 0; i < rects.length; i++) {
254: Component c = componentsMap.get(constraintsList.get(i));
255: c.setBounds(rects[i]);
256: }
257:
258: }
259:
260: private Rectangle[] computePositions(Container target) {
261: // parent container size
262: Dimension targetDim = target.getSize();
263:
264: // parent container insets
265: Insets insets = target.getInsets();
266:
267: int resizeCount = 0;
268: int totalComponentWidth = 0;
269: Rectangle[] rects = new Rectangle[constraintsList.size()];
270:
271: for (int i = 0; i < rects.length; i++) {
272: StatusBarLayoutConstraints cons = constraintsList.get(i);
273: int preferredWidth = cons.getPreferredWidth();
274: boolean resizeToFit = cons.isResizeable();
275:
276: // will need to reset x values after all
277: // widths have been calculated
278:
279: if (resizeToFit) {
280: resizeCount++;
281: }
282: rects[i] = new Rectangle(0, insets.top, preferredWidth,
283: height);
284: totalComponentWidth += preferredWidth;
285: }
286:
287: // resize components as required
288: int fillWidth = 0;
289: // let the resizable components fill the rest
290: if (resizeCount > 0 && totalComponentWidth < targetDim.width) {
291: fillWidth = ((targetDim.width - insets.left - insets.right - totalComponentWidth) / resizeCount);// - 1;
292: }
293:
294: int xPosn = insets.left;
295: for (int i = 0; i < rects.length; i++) {
296: Rectangle rect = rects[i];
297: rect.x = xPosn;
298:
299: StatusBarLayoutConstraints cons = constraintsList.get(i);
300: if (cons.isResizeable()) {
301: rect.width = cons.getPreferredWidth() + fillWidth;
302: }
303:
304: xPosn += rect.width;
305: }
306:
307: return rects;
308: }
309:
310: class ConstraintsComparator implements Comparator {
311:
312: /** <p>Compares the two objects. */
313: public int compare(Object obj1, Object obj2) {
314: StatusBarLayoutConstraints cons1 = (StatusBarLayoutConstraints) obj1;
315: StatusBarLayoutConstraints cons2 = (StatusBarLayoutConstraints) obj2;
316:
317: int index1 = cons1.getIndex();
318: int index2 = cons2.getIndex();
319:
320: if (index1 < index2) {
321: return -1;
322: } else if (index1 > index2) {
323: return 1;
324: } else {
325: return 0;
326: }
327: }
328: }
329:
330: }
|