001: /*
002: * VariableGridLayout.java - a grid layout manager with variable cell sizes
003: *
004: * Originally written by Dirk Moebius for the jEdit project. This work has been
005: * placed into the public domain. You may use this work in any way and for any
006: * purpose you wish.
007: *
008: * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
009: * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
010: * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
011: * OR REDISTRIBUTION OF THIS SOFTWARE.
012: */
013:
014: package installer;
015:
016: import java.awt.*;
017:
018: // This is copied from jEdit's org.gjt.sp.jedit.gui package.
019:
020: /**
021: * The <code>VariableGridLayout</code> class is a layout manager
022: * that lays out a container's components in a rectangular grid
023: * with variable cell sizes.<p>
024: *
025: * The container is divided into rectangles, and one component is placed
026: * in each rectangle. Each row is as large as the largest component in
027: * that row, and each column is as wide as the widest component in
028: * that column.<p>
029: *
030: * This behavior is basically the same as in
031: * <code>java.awt.GridLayout</code>, but with different row heights and
032: * column widths for each row/column.<p>
033: *
034: * For example, the following is an applet that lays out six buttons
035: * into three rows and two columns:<p>
036: *
037: * <blockquote><pre>
038: * import java.awt.*;
039: * import java.applet.Applet;
040: * public class ButtonGrid extends Applet {
041: * public void init() {
042: * setLayout(new VariableGridLayout(VariableGridLayout.FIXED_NUM_COLUMNS, 2));
043: * add(new Button("1"));
044: * add(new Button("2"));
045: * add(new Button("3"));
046: * add(new Button("4"));
047: * add(new Button("5"));
048: * add(new Button("6"));
049: * }
050: * }
051: * </pre></blockquote><p>
052: *
053: * <b>Programmer's remark:</b> VariableGridLayout could be faster, if it would
054: * reside in the package java.awt, because then it could access some
055: * package private fields of <code>Container</code> or
056: * <code>Component</code>. Instead, it has to call
057: * <code>Component.getSize()</code>,
058: * which allocates memory on the heap.<p>
059: *
060: * <b>Todo:</b>
061: * <ul>
062: * <li>Use alignmentX/Y property if the grid cell is larger than the preferred size of the component.
063: * <li>Ability to span components over more than one cell horizontally
064: * </ul>
065: *
066: * @author Dirk Moebius
067: * @version 1.0
068: * @see java.awt.GridLayout
069: */
070: public class VariableGridLayout implements LayoutManager2,
071: java.io.Serializable {
072:
073: public static final int FIXED_NUM_ROWS = 1;
074: public static final int FIXED_NUM_COLUMNS = 2;
075:
076: public VariableGridLayout(int mode, int size, int hgap, int vgap) {
077: if (mode != FIXED_NUM_ROWS && mode != FIXED_NUM_COLUMNS) {
078: throw new IllegalArgumentException(
079: "illegal mode; value is " + mode);
080: }
081: if (size <= 0) {
082: throw new IllegalArgumentException(
083: "size cannot be zero or less; value is " + size);
084: }
085: if (hgap < 0) {
086: throw new IllegalArgumentException(
087: "hgap cannot be negative; value is " + hgap);
088: }
089: if (vgap < 0) {
090: throw new IllegalArgumentException(
091: "vgap cannot be negative; value is " + vgap);
092: }
093: this .mode = mode;
094: this .size = size;
095: this .hgap = hgap;
096: this .vgap = vgap;
097: }
098:
099: /**
100: * Creates a variable grid layout manager with the specified mode
101: * and zero horizontal and vertical gap.
102: */
103: public VariableGridLayout(int mode, int size) {
104: this (mode, size, 0, 0);
105: }
106:
107: /**
108: * Creates a variable grid layout manager with mode FIXED_NUM_ROWS,
109: * number of rows == 1 and zero horizontal and vertical gap.
110: */
111: public VariableGridLayout() {
112: this (FIXED_NUM_ROWS, 1, 0, 0);
113: }
114:
115: /**
116: * Not used in this class.
117: */
118: public void addLayoutComponent(String name, Component component) {
119: }
120:
121: /**
122: * Not used in this class.
123: */
124: public void addLayoutComponent(Component component,
125: Object constraints) {
126: }
127:
128: /**
129: * Not used in this class.
130: */
131: public void removeLayoutComponent(Component component) {
132: }
133:
134: /**
135: * Always returns 0.5.
136: */
137: public float getLayoutAlignmentX(Container container) {
138: return 0.5f;
139: }
140:
141: /**
142: * Always returns 0.5.
143: */
144: public float getLayoutAlignmentY(Container container) {
145: return 0.5f;
146: }
147:
148: public Dimension preferredLayoutSize(Container parent) {
149: return getLayoutSize(parent, 2);
150: }
151:
152: public Dimension minimumLayoutSize(Container parent) {
153: return getLayoutSize(parent, 0);
154: }
155:
156: public Dimension maximumLayoutSize(Container parent) {
157: return getLayoutSize(parent, 1);
158: }
159:
160: public void layoutContainer(Container parent) {
161: synchronized (parent.getTreeLock()) {
162: update(parent);
163:
164: int ncomponents = parent.getComponentCount();
165:
166: if (ncomponents == 0) {
167: return;
168: }
169:
170: // Pass 1: compute preferred row heights / column widths
171: int total_height = 0;
172: for (int r = 0, i = 0; r < nrows; r++) {
173: for (int c = 0; c < ncols; c++, i++) {
174: if (i < ncomponents) {
175: Dimension d = parent.getComponent(i)
176: .getPreferredSize();
177: row_heights[r] = Math.max(row_heights[r],
178: d.height);
179: col_widths[c] = Math
180: .max(col_widths[c], d.width);
181: } else {
182: break;
183: }
184: }
185: total_height += row_heights[r];
186: }
187:
188: int total_width = 0;
189: for (int c = 0; c < ncols; c++) {
190: total_width += col_widths[c];
191: }
192:
193: // Pass 2: redistribute free space
194: Dimension parent_size = parent.getSize();
195: Insets insets = parent.getInsets();
196: int free_height = parent_size.height - insets.top
197: - insets.bottom - (nrows - 1) * vgap;
198: int free_width = parent_size.width - insets.left
199: - insets.right - (ncols - 1) * hgap;
200:
201: if (total_height != free_height) {
202: double dy = (double) free_height
203: / (double) total_height;
204: for (int r = 0; r < nrows; r++) {
205: row_heights[r] = (int) ((double) row_heights[r] * dy);
206: }
207: }
208:
209: if (total_width != free_width) {
210: double dx = ((double) free_width)
211: / ((double) total_width);
212: for (int c = 0; c < ncols; c++) {
213: col_widths[c] = (int) ((double) col_widths[c] * dx);
214: }
215: }
216:
217: // Pass 3: layout components
218: for (int r = 0, y = insets.top, i = 0; r < nrows; y += row_heights[r]
219: + vgap, r++) {
220: for (int c = 0, x = insets.left; c < ncols; x += col_widths[c]
221: + hgap, c++, i++) {
222: if (i < ncomponents) {
223: parent.getComponent(i).setBounds(x, y,
224: col_widths[c], row_heights[r]);
225: }
226: }
227: }
228:
229: } // synchronized
230: }
231:
232: public void invalidateLayout(Container container) {
233: }
234:
235: /**
236: * Returns the string representation of this variable grid layout's values.
237: * @return a string representation of this variable grid layout.
238: */
239: public String toString() {
240: return getClass().getName() + "[mode=" + mode + ",size=" + size
241: + ",hgap=" + hgap + ",vgap=" + vgap + "]";
242: }
243:
244: /**
245: * @param which if 0 compute minimum layout size,
246: * if 1 compute maximum layout size,
247: * otherwise compute preferred layout size.
248: */
249: private Dimension getLayoutSize(Container parent, int which) {
250: synchronized (parent.getTreeLock()) {
251: update(parent);
252:
253: int ncomponents = parent.getComponentCount();
254: int h = 0;
255: int w = 0;
256:
257: for (int r = 0, i = 0; r < nrows; r++) {
258: int row_height = 0;
259: for (int c = 0; c < ncols; c++, i++) {
260: if (i < ncomponents) {
261: switch (which) {
262: case 0:
263: row_height = Math.max(row_height,
264: parent.getComponent(i)
265: .getMinimumSize().height);
266: break;
267: case 1:
268: row_height = Math.max(row_height,
269: parent.getComponent(i)
270: .getMaximumSize().height);
271: break;
272: default:
273: row_height = Math.max(row_height,
274: parent.getComponent(i)
275: .getPreferredSize().height);
276: break;
277: }
278: } else {
279: break;
280: }
281: }
282: h += row_height;
283: }
284:
285: for (int c = 0; c < ncols; c++) {
286: int col_width = 0;
287: for (int r = 0; r < nrows; r++) {
288: int i = r * ncols + c;
289: if (i < ncomponents) {
290: switch (which) {
291: case 0:
292: col_width = Math.max(col_width,
293: parent.getComponent(i)
294: .getMinimumSize().width);
295: break;
296: case 1:
297: col_width = Math.max(col_width,
298: parent.getComponent(i)
299: .getMaximumSize().width);
300: break;
301: default:
302: col_width = Math.max(col_width,
303: parent.getComponent(i)
304: .getPreferredSize().width);
305: break;
306: }
307: } else {
308: break;
309: }
310: }
311: w += col_width;
312: }
313:
314: Insets insets = parent.getInsets();
315: return new Dimension(w + insets.left + insets.right
316: + ((ncols - 1) * hgap), h + insets.top
317: + insets.bottom + ((nrows - 1) * vgap));
318: }
319: }
320:
321: private void update(Container container) {
322: int ncomponents = container.getComponentCount();
323: int old_nrows = nrows;
324: int old_ncols = ncols;
325: if (this .mode == FIXED_NUM_ROWS) {
326: nrows = this .size;
327: ncols = (ncomponents + nrows - 1) / nrows;
328: } else {
329: ncols = this .size;
330: nrows = (ncomponents + ncols - 1) / ncols;
331: }
332: if (old_nrows != nrows) {
333: row_heights = new int[nrows];
334: }
335: if (old_ncols != ncols) {
336: col_widths = new int[ncols];
337: }
338: }
339:
340: private int mode;
341: private int size;
342: private int hgap;
343: private int vgap;
344: private transient int nrows = -1;
345: private transient int ncols = -1;
346: private transient int[] row_heights = null;
347: private transient int[] col_widths = null;
348: }
|