1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing.components;
20
21 import java.awt.Component;
22 import java.awt.Container;
23 import java.awt.Dimension;
24 import java.awt.FlowLayout;
25 import java.awt.Insets;
26
27 /***
28 * BetterFlowLayout works just like FlowLayout, except that
29 * you can specify a vertical orientation in addition to the
30 * usual horizontal orientations. You can also specify that
31 * all the components be sized to the same height and/or width.
32 * By default, the behavior is identical to FlowLayout.
33 *
34 * @author michael@mpowers.net
35 * @author $Author: cgruber $
36 * @version $Revision: 904 $
37 * $Date: 2006-02-18 23:19:05 +0000 (Sat, 18 Feb 2006) $
38 */
39 public class BetterFlowLayout extends FlowLayout {
40
41 /***
42 * This value indicates vertical orientation and
43 * that each column of components should be top-justified.
44 */
45 public static final int TOP = 32;
46
47 /***
48 * This value indicates vertical orientation and
49 * that each column of components should be centered.
50 */
51 public static final int CENTER_VERTICAL = 16;
52
53 /***
54 * This value indicates vertical orientation and
55 * that each column of components should be bottom-justified.
56 */
57 public static final int BOTTOM = 8;
58
59 /***
60 * Tracks orientation.
61 */
62 protected boolean isHorizontal = true;
63
64 /***
65 * Tracks component sizing of width.
66 */
67 protected boolean isWidthUniform = false;
68 /***
69 * Tracks component sizing of height.
70 */
71 protected boolean isHeightUniform = false;
72
73 /***
74 * Constructs a new Flow Layout with a centered alignment and a
75 * default 5-unit horizontal and vertical gap.
76 */
77 public BetterFlowLayout() {
78 this(CENTER, 5, 5);
79 }
80
81 /***
82 * Constructs a new Flow Layout with the specified alignment and a
83 * default 5-unit horizontal and vertical gap.
84 * The value of the alignment argument must be one of
85 * <code>BetterFlowLayout.LEFT</code>, <code>BetterFlowLayout.RIGHT</code>,
86 * or <code>BetterFlowLayout.CENTER</code>.
87 * @param align the alignment value
88 */
89 public BetterFlowLayout(int align) {
90 this(align, 5, 5);
91 }
92
93 /***
94 * Creates a new flow layout manager with the indicated alignment
95 * and the indicated horizontal and vertical gaps.
96 * <p>
97 * The value of the alignment argument must be one of
98 * <code>BetterFlowLayout.LEFT</code>, <code>BetterFlowLayout.RIGHT</code>,
99 * or <code>BetterFlowLayout.CENTER</code>.
100 * @param align the alignment value.
101 * @param hgap the horizontal gap between components.
102 * @param vgap the vertical gap between components.
103 */
104 public BetterFlowLayout(int align, int hgap, int vgap) {
105 setHgap(hgap);
106 setVgap(vgap);
107 setAlignment(align);
108 }
109
110 /***
111 * Sets whether all components should have the same height.
112 * @param isUniform the new value.
113 * @see #isHeightUniform
114 */
115 public void setHeightUniform(boolean isUniform) {
116 isHeightUniform = isUniform;
117 }
118
119 /***
120 * Sets whether all components should have the same width.
121 * @param isUniform the new value.
122 * @see #isWidthUniform
123 */
124 public void setWidthUniform(boolean isUniform) {
125 isWidthUniform = isUniform;
126 }
127
128 /***
129 * Determines whether all components will have the same height.
130 * The uniform height will be the maximum of the preferred heights
131 * of all the components in the container.
132 * This value defaults to false.
133 * @return whether components will have the same height.
134 */
135 public boolean isHeightUniform() {
136 return isHeightUniform;
137 }
138
139 /***
140 * Determines whether all components will have the same width.
141 * The uniform height will be the maximum of the preferred widths
142 * of all the components in the container.
143 * This value defaults to false.
144 * @return whether components will have the same width.
145 */
146 public boolean isWidthUniform() {
147 return isWidthUniform;
148 }
149
150 /***
151 * Sets the alignment for this layout.
152 * Possible values for horizontal orientation are <code>LEFT</code>,
153 * <code>RIGHT</code>, and <code>CENTER</code>.
154 * Possible values for vertical orientation are <code>TOP</code>,
155 * <code>BOTTOM</code>, and <code>CENTER_VERTICAL</code>.
156 * @param align the alignment value.
157 * @see java.awt.FlowLayout#getAlignment
158 */
159 public void setAlignment(int align) {
160 if ( ( align == TOP ) || ( align == BOTTOM ) || ( align == CENTER_VERTICAL ) )
161 {
162 isHorizontal = false;
163 }
164 else
165 {
166 isHorizontal = true;
167 }
168
169 super.setAlignment( align );
170 }
171
172 /***
173 * Returns the preferred dimensions for this layout given the components
174 * in the specified target container.
175 * @param target the component which needs to be laid out
176 * @return the preferred dimensions to lay out the
177 * subcomponents of the specified container.
178 * @see Container
179 * @see #minimumLayoutSize
180 * @see java.awt.Container#getPreferredSize
181 */
182 public Dimension preferredLayoutSize(Container target) {
183 if ( isHorizontal ) {
184 return preferredLayoutSizeHorizontal( target );
185 } else {
186 return preferredLayoutSizeVertical( target );
187 }
188 }
189
190 /***
191 * Returns the preferred dimensions for this layout given the components
192 * in the specified target container.
193 * @param target the component which needs to be laid out
194 * @return the preferred dimensions to lay out the
195 * subcomponents of the specified container.
196 * @see Container
197 * @see #minimumLayoutSize
198 * @see java.awt.Container#getPreferredSize
199 */
200 public Dimension preferredLayoutSizeHorizontal(Container target) {
201 synchronized (target.getTreeLock()) {
202 Dimension dim = new Dimension(0, 0);
203 int nmembers = target.getComponentCount();
204 int maxWidth = 0;
205
206 for (int i = 0 ; i < nmembers ; i++) {
207 Component m = target.getComponent(i);
208 if (m.isVisible()) {
209 Dimension d = m.getPreferredSize();
210 dim.height = Math.max(dim.height, d.height);
211 maxWidth = Math.max(maxWidth, d.width);
212 if (i > 0) {
213 dim.width += getHgap();
214 }
215 dim.width += d.width;
216 }
217 }
218 if ( isWidthUniform )
219 dim.width = ( maxWidth + getHgap() ) * nmembers - getHgap();
220 Insets insets = target.getInsets();
221 dim.width += insets.left + insets.right + getHgap()*2;
222 dim.height += insets.top + insets.bottom + getVgap()*2;
223 return dim;
224 }
225 }
226
227 /***
228 * Returns the preferred dimensions for this layout given the components
229 * in the specified target container.
230 * @param target the component which needs to be laid out
231 * @return the preferred dimensions to lay out the
232 * subcomponents of the specified container.
233 * @see Container
234 * @see #minimumLayoutSize
235 * @see java.awt.Container#getPreferredSize
236 */
237 public Dimension preferredLayoutSizeVertical(Container target) {
238 synchronized (target.getTreeLock()) {
239 Dimension dim = new Dimension(0, 0);
240 int nmembers = target.getComponentCount();
241 int maxHeight = 0;
242
243 for (int i = 0 ; i < nmembers ; i++) {
244 Component m = target.getComponent(i);
245 if (m.isVisible()) {
246 Dimension d = m.getPreferredSize();
247 dim.width = Math.max(dim.width, d.width);
248 maxHeight = Math.max(maxHeight, d.height);
249 if (i > 0) {
250 dim.height += getVgap();
251 }
252 dim.height += d.height;
253 }
254 }
255 if ( isHeightUniform )
256 dim.height = ( maxHeight + getVgap() ) * nmembers - getVgap();
257 Insets insets = target.getInsets();
258 dim.width += insets.left + insets.right + getHgap()*2;
259 dim.height += insets.top + insets.bottom + getVgap()*2;
260 return dim;
261 }
262 }
263
264 /***
265 * Returns the minimum dimensions needed to layout the components
266 * contained in the specified target container.
267 * @param target the component which needs to be laid out
268 * @return the minimum dimensions to lay out the
269 * subcomponents of the specified container.
270 * @see #preferredLayoutSize
271 * @see java.awt.Container
272 * @see java.awt.Container#doLayout
273 */
274 public Dimension minimumLayoutSize(Container target) {
275
276 if ( isHorizontal ) {
277 return preferredLayoutSizeHorizontal( target );
278 } else {
279 return preferredLayoutSizeVertical( target );
280 }
281 }
282
283 /***
284 * Lays out the container. This method lets each component take
285 * its preferred size by reshaping the components in the
286 * target container in order to satisfy the constraints of
287 * this <code>BetterFlowLayout</code> object.
288 * @param target the specified component being laid out.
289 * @see Container
290 * @see java.awt.Container#doLayout
291 */
292 public void layoutContainer(Container target) {
293 if ( isHorizontal ) {
294 layoutContainerHorizontal( target );
295 } else {
296 layoutContainerVertical( target );
297 }
298 }
299
300 /***
301 * Lays out the container. This method lets each component take
302 * its preferred size by reshaping the components in the
303 * target container in order to satisfy the constraints of
304 * this <code>BetterFlowLayout</code> object.
305 * @param target the specified component being laid out.
306 * @see Container
307 * @see java.awt.Container#doLayout
308 */
309 protected void layoutContainerHorizontal(Container target) {
310 synchronized (target.getTreeLock()) {
311 Insets insets = target.getInsets();
312 int maxwidth = target.getSize().width - (insets.left + insets.right + getHgap()*2);
313 int nmembers = target.getComponentCount();
314 int x = 0, y = insets.top + getVgap();
315 int rowh = 0, start = 0;
316
317 boolean ltr = true;
318 Dimension uniform = getUniformDimension( target );
319
320 for (int i = 0 ; i < nmembers ; i++) {
321 Component m = target.getComponent(i);
322 if (m.isVisible()) {
323 Dimension d = m.getPreferredSize();
324 if ( isWidthUniform )
325 d.width = uniform.width;
326 if ( isHeightUniform )
327 d.height = uniform.height;
328 m.setSize(d.width, d.height);
329
330 if ((x == 0) || ((x + d.width) <= maxwidth)) {
331 if (x > 0) {
332 x += getHgap();
333 }
334 x += d.width;
335 rowh = Math.max(rowh, d.height);
336 } else {
337 moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, i, ltr);
338 x = d.width;
339 y += getVgap() + rowh;
340 rowh = d.height;
341 start = i;
342 }
343 }
344 }
345 moveComponentsHorizontal(target, insets.left + getHgap(), y, maxwidth - x, rowh, start, nmembers, ltr);
346 }
347 }
348
349 /***
350 * Centers the elements in the specified row, if there is any slack.
351 * @param target the component which needs to be moved
352 * @param x the x coordinate
353 * @param y the y coordinate
354 * @param width the width dimensions
355 * @param height the height dimensions
356 * @param rowStart the beginning of the row
357 * @param rowEnd the the ending of the row
358 */
359 private void moveComponentsHorizontal(Container target, int x, int y, int width, int height,
360 int rowStart, int rowEnd, boolean ltr) {
361 synchronized (target.getTreeLock()) {
362 switch (getAlignment()) {
363 case LEFT:
364 x += ltr ? 0 : width;
365 break;
366 case CENTER:
367 x += width / 2;
368 break;
369 case RIGHT:
370 x += ltr ? width : 0;
371 break;
372
373
374
375
376
377 }
378 for (int i = rowStart ; i < rowEnd ; i++) {
379 Component m = target.getComponent(i);
380 if (m.isVisible()) {
381 if (ltr) {
382 m.setLocation(x, y + (height - m.getBounds().height) / 2);
383 } else {
384 m.setLocation(target.getBounds().width - x - m.getBounds().width, y + (height - m.getBounds().height) / 2);
385 }
386 x += m.getBounds().width + getHgap();
387 }
388 }
389 }
390 }
391
392 /***
393 * Lays out the container. This method lets each component take
394 * its preferred size by reshaping the components in the
395 * target container in order to satisfy the constraints of
396 * this <code>BetterFlowLayout</code> object.
397 * @param target the specified component being laid out.
398 * @see Container
399 * @see java.awt.Container#doLayout
400 */
401 protected void layoutContainerVertical(Container target) {
402 synchronized (target.getTreeLock()) {
403
404 Insets insets = target.getInsets();
405 int maxheight = target.getBounds().height - (insets.top + insets.bottom + getVgap()*2);
406 int nmembers = target.getComponentCount();
407 int y = 0, x = insets.left + getHgap();
408 int colw = 0, start = 0;
409
410 Dimension uniform = getUniformDimension( target );
411 for (int i = 0 ; i < nmembers ; i++) {
412 Component m = target.getComponent(i);
413 if (m.isVisible()) {
414 Dimension d = m.getPreferredSize();
415 if ( isWidthUniform )
416 d.width = uniform.width;
417 if ( isHeightUniform )
418 d.height = uniform.height;
419 m.setSize(d.width, d.height);
420
421 if ((y == 0) || ((y + d.height) <= maxheight)) {
422 if (y > 0) {
423 y += getVgap();
424 }
425 y += d.height;
426 colw = Math.max(colw, d.width);
427 } else {
428 moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, i );
429 y = d.height;
430 x += getHgap() + colw;
431 colw = d.width;
432 start = i;
433 }
434 }
435 }
436 moveComponentsVertical(target, x, insets.top + getVgap(), colw, maxheight - y, start, nmembers );
437 }
438 }
439
440 /***
441 * Centers the elements in the specified row, if there is any slack.
442 * @param target the component which needs to be moved
443 * @param x the x coordinate
444 * @param y the y coordinate
445 * @param width the width dimensions
446 * @param height the height dimensions
447 * @param colStart the beginning of the column
448 * @param colEnd the the ending of the column
449 */
450 private void moveComponentsVertical(Container target, int x, int y, int width, int height,
451 int colStart, int colEnd) {
452 synchronized (target.getTreeLock()) {
453 switch (getAlignment()) {
454 case TOP:
455 y += 0;
456 break;
457 case CENTER_VERTICAL:
458 y += ( height / 2 );
459 break;
460 case BOTTOM:
461 y += height;
462 break;
463 }
464 for (int i = colStart ; i < colEnd ; i++) {
465 Component m = target.getComponent(i);
466 if (m.isVisible()) {
467 m.setLocation(x + (width - m.getBounds().width) / 2, y );
468
469
470 y += m.getBounds().height + getVgap();
471 }
472 }
473 }
474 }
475
476 /***
477 * Returns a dimension representing the maximum preferred
478 * height and width of all the components in the container.
479 * @param target the container to scan.
480 * @return a dimension containing the maximum values.
481 */
482 protected Dimension getUniformDimension(Container target) {
483 Component m = null;
484 Dimension preferred = null;
485 int maxWidth = 0, maxHeight = 0;
486 int nmembers = target.getComponentCount();
487 for ( int i = 0; i < nmembers; i++ ) {
488 m = target.getComponent( i );
489 if ( m.isVisible() ) {
490 preferred = m.getPreferredSize();
491 maxWidth = Math.max( maxWidth, preferred.width );
492 maxHeight = Math.max( maxHeight, preferred.height );
493 }
494 }
495 return new Dimension( maxWidth, maxHeight );
496 }
497
498 /***
499 * Returns a string representation of this <code>BetterFlowLayout</code>
500 * object and its values.
501 * @return a string representation of this layout.
502 */
503 public String toString() {
504 String str = "";
505 switch (getAlignment()) {
506 case TOP: str = ",align=top"; break;
507 case CENTER_VERTICAL: str = ",align=vertical"; break;
508 case BOTTOM: str = ",align=bottom"; break;
509 default: return super.toString();
510 }
511 return getClass().getName() + "[hgap=" + getHgap() + ",vgap=" + getVgap() + str + "]";
512 }
513
514
515 }