PageRenderTime 85ms CodeModel.GetById 72ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/gui/VariableGridLayout.java

#
Java | 332 lines | 183 code | 52 blank | 97 comment | 40 complexity | e809a3c56e47162c5ddca09b2d0d6c80 MD5 | raw file
  1/*
  2 * VariableGridLayout.java - a grid layout manager with variable cell sizes
  3 *
  4 * Originally written by Dirk Moebius for the jEdit project. This work has been
  5 * placed into the public domain. You may use this work in any way and for any
  6 * purpose you wish.
  7 *
  8 * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
  9 * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
 10 * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
 11 * OR REDISTRIBUTION OF THIS SOFTWARE.
 12 */
 13
 14package org.gjt.sp.jedit.gui;
 15
 16
 17import java.awt.*;
 18
 19/**
 20 * The <code>VariableGridLayout</code> class is a layout manager
 21 * that lays out a container's components in a rectangular grid
 22 * with variable cell sizes.<p>
 23 *
 24 * The container is divided into rectangles, and one component is placed
 25 * in each rectangle. Each row is as large as the largest component in
 26 * that row, and each column is as wide as the widest component in
 27 * that column.<p>
 28 *
 29 * This behavior is basically the same as in
 30 * <code>java.awt.GridLayout</code>, but with different row heights and
 31 * column widths for each row/column.<p>
 32 *
 33 * For example, the following is an applet that lays out six buttons
 34 * into three rows and two columns:<p>
 35 *
 36 * <blockquote><pre>
 37 * import java.awt.*;
 38 * import java.applet.Applet;
 39 * public class ButtonGrid extends Applet {
 40 *     public void init() {
 41 *         setLayout(new VariableGridLayout(VariableGridLayout.FIXED_NUM_COLUMNS, 2));
 42 *         add(new Button("1"));
 43 *         add(new Button("2"));
 44 *         add(new Button("3"));
 45 *         add(new Button("4"));
 46 *         add(new Button("5"));
 47 *         add(new Button("6"));
 48 *     }
 49 * }
 50 * </pre></blockquote><p>
 51 *
 52 * <b>Programmer's remark:</b> VariableGridLayout could be faster, if it would
 53 * reside in the package java.awt, because then it could access some
 54 * package private fields of <code>Container</code> or
 55 * <code>Component</code>. Instead, it has to call
 56 * <code>Component.getSize()</code>,
 57 * which allocates memory on the heap.<p>
 58 *
 59 * <b>Todo:</b>
 60 * <ul>
 61 * <li>Use alignmentX/Y property if the grid cell is larger than the preferred size of the component.
 62 * <li>Ability to span components over more than one cell horizontally
 63 * </ul>
 64 *
 65 * @author Dirk Moebius
 66 * @version 1.0
 67 * @see java.awt.GridLayout
 68 */
 69public class VariableGridLayout implements LayoutManager2, java.io.Serializable
 70{
 71
 72	public static final int FIXED_NUM_ROWS = 1;
 73	public static final int FIXED_NUM_COLUMNS = 2;
 74
 75
 76	public VariableGridLayout(int mode, int size, int hgap, int vgap) {
 77		if (mode != FIXED_NUM_ROWS && mode != FIXED_NUM_COLUMNS) {
 78			throw new IllegalArgumentException("illegal mode; value is " + mode);
 79		}
 80		if (size <= 0) {
 81			throw new IllegalArgumentException("size cannot be zero or less; value is " + size);
 82		}
 83		if (hgap < 0) {
 84			throw new IllegalArgumentException("hgap cannot be negative; value is " + hgap);
 85		}
 86		if (vgap < 0) {
 87			throw new IllegalArgumentException("vgap cannot be negative; value is " + vgap);
 88		}
 89		this.mode = mode;
 90		this.size = size;
 91		this.hgap = hgap;
 92		this.vgap = vgap;
 93	}
 94
 95
 96	/**
 97	 * Creates a variable grid layout manager with the specified mode
 98	 * and zero horizontal and vertical gap.
 99	 */
100	public VariableGridLayout(int mode, int size) {
101		this(mode, size, 0, 0);
102	}
103
104
105	/**
106	 * Creates a variable grid layout manager with mode FIXED_NUM_ROWS,
107	 * number of rows == 1 and zero horizontal and vertical gap.
108	 */
109	public VariableGridLayout() {
110		this(FIXED_NUM_ROWS, 1, 0, 0);
111	}
112
113
114	/**
115	 * Not used in this class.
116	 */
117	public void addLayoutComponent(String name, Component component) { }
118
119
120	/**
121	 * Not used in this class.
122	 */
123	public void addLayoutComponent(Component component, Object constraints) { }
124
125
126	/**
127	 * Not used in this class.
128	 */
129	public void removeLayoutComponent(Component component) { }
130
131
132	/**
133	 * Always returns 0.5.
134	 */
135	public float getLayoutAlignmentX(Container container) {
136		return 0.5f;
137	}
138
139
140	/**
141	 * Always returns 0.5.
142	 */
143	public float getLayoutAlignmentY(Container container) {
144		return 0.5f;
145	}
146
147
148	public Dimension preferredLayoutSize(Container parent) {
149		return getLayoutSize(parent, 2);
150	}
151
152
153	public Dimension minimumLayoutSize(Container parent) {
154		return getLayoutSize(parent, 0);
155	}
156
157
158	public Dimension maximumLayoutSize(Container parent) {
159		return getLayoutSize(parent, 1);
160	}
161
162
163	public void layoutContainer(Container parent) {
164		synchronized (parent.getTreeLock()) {
165			update(parent);
166
167			int ncomponents = parent.getComponentCount();
168
169			if (ncomponents == 0) {
170				return;
171			}
172
173			// Pass 1: compute preferred row heights / column widths
174			int total_height = 0;
175			for (int r = 0, i = 0; r < nrows; r++) {
176				for (int c = 0; c < ncols; c++, i++) {
177					if (i < ncomponents) {
178						Dimension d = parent.getComponent(i).getPreferredSize();
179						row_heights[r] = Math.max(row_heights[r], d.height);
180						col_widths[c] = Math.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 - insets.bottom - (nrows - 1) * vgap;
197			int free_width = parent_size.width - insets.left - insets.right - (ncols - 1) * hgap;
198
199			if (total_height != free_height) {
200				double dy = (double)free_height / (double)total_height;
201				for (int r = 0; r < nrows; r++) {
202					row_heights[r] = (int) ((double)row_heights[r] * dy);
203				}
204			}
205
206			if (total_width != free_width) {
207				double dx = ((double)free_width) / ((double)total_width);
208				for (int c = 0; c < ncols; c++) {
209					col_widths[c] = (int) ((double)col_widths[c] * dx);
210				}
211			}
212
213			// Pass 3: layout components
214			for (int r = 0, y = insets.top, i = 0; r < nrows; y += row_heights[r] + vgap, r++) {
215				for (int c = 0, x = insets.left; c < ncols; x += col_widths[c] + hgap, c++, i++) {
216					if (i < ncomponents) {
217						parent.getComponent(i).setBounds(x, y, col_widths[c], row_heights[r]);
218					}
219				}
220			}
221
222		} // synchronized
223	}
224
225
226	public void invalidateLayout(Container container) {}
227
228
229	/**
230	 * Returns the string representation of this variable grid layout's values.
231	 * @return  a string representation of this variable grid layout.
232	 */
233	public String toString() {
234		return getClass().getName() + "[mode=" + mode + ",size=" + size
235			   + ",hgap=" + hgap + ",vgap=" + vgap + "]";
236	}
237
238
239	/**
240	 * @param  which  if 0 compute minimum layout size,
241	 *				if 1 compute maximum layout size,
242	 *				otherwise compute preferred layout size.
243	 */
244	private Dimension getLayoutSize(Container parent, int which) {
245		synchronized (parent.getTreeLock()){
246			update(parent);
247
248			int ncomponents = parent.getComponentCount();
249			int h = 0;
250			int w = 0;
251
252			for (int r = 0, i = 0; r < nrows; r++) {
253				int row_height = 0;
254				for (int c = 0; c < ncols; c++, i++) {
255					if (i < ncomponents) {
256						switch (which) {
257							case 0:
258								row_height = Math.max(row_height, parent.getComponent(i).getMinimumSize().height);
259								break;
260							case 1:
261								row_height = Math.max(row_height, parent.getComponent(i).getMaximumSize().height);
262								break;
263							default:
264								row_height = Math.max(row_height, parent.getComponent(i).getPreferredSize().height);
265								break;
266						}
267					} else {
268						break;
269					}
270				}
271				h += row_height;
272			}
273
274			for (int c = 0; c < ncols; c++) {
275				int col_width = 0;
276				for (int r = 0; r < nrows; r++) {
277					int i = r * ncols + c;
278					if (i < ncomponents) {
279						switch (which) {
280							case 0:
281								col_width = Math.max(col_width, parent.getComponent(i).getMinimumSize().width);
282								break;
283							case 1:
284								col_width = Math.max(col_width, parent.getComponent(i).getMaximumSize().width);
285								break;
286							default:
287								col_width = Math.max(col_width, parent.getComponent(i).getPreferredSize().width);
288								break;
289						}
290					} else {
291						break;
292					}
293				}
294				w += col_width;
295			}
296
297			Insets insets = parent.getInsets();
298			return new Dimension(w + insets.left + insets.right + ((ncols - 1) * hgap),
299								 h + insets.top + insets.bottom + ((nrows - 1) * vgap));
300		}
301	}
302
303
304	private void update(Container container) {
305		int ncomponents = container.getComponentCount();
306		int old_nrows = nrows;
307		int old_ncols = ncols;
308		if (this.mode == FIXED_NUM_ROWS) {
309			nrows = this.size;
310			ncols = (ncomponents + nrows - 1) / nrows;
311		} else {
312			ncols = this.size;
313			nrows = (ncomponents + ncols - 1) / ncols;
314		}
315		if (old_nrows != nrows) {
316			row_heights = new int[nrows];
317		}
318		if (old_ncols != ncols) {
319			col_widths = new int[ncols];
320		}
321	}
322
323
324	private int mode;
325	private int size;
326	private int hgap;
327	private int vgap;
328	private transient int nrows = -1;
329	private transient int ncols = -1;
330	private transient int[] row_heights = null;
331	private transient int[] col_widths = null;
332}