001: /*
002: * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: package java.awt;
026:
027: import java.io.ObjectInputStream;
028: import java.io.IOException;
029:
030: /**
031: * A flow layout arranges components in a directional flow, much
032: * like lines of text in a paragraph. The flow direction is
033: * determined by the container's <code>componentOrientation</code>
034: * property and may be one of two values:
035: * <ul>
036: * <li><code>ComponentOrientation.LEFT_TO_RIGHT</code>
037: * <li><code>ComponentOrientation.RIGHT_TO_LEFT</code>
038: * </ul>
039: * Flow layouts are typically used
040: * to arrange buttons in a panel. It arranges buttons
041: * horizontally until no more buttons fit on the same line.
042: * The line alignment is determined by the <code>align</code>
043: * property. The possible values are:
044: * <ul>
045: * <li>{@link #LEFT LEFT}
046: * <li>{@link #RIGHT RIGHT}
047: * <li>{@link #CENTER CENTER}
048: * <li>{@link #LEADING LEADING}
049: * <li>{@link #TRAILING TRAILING}
050: * </ul>
051: * <p>
052: * For example, the following picture shows an applet using the flow
053: * layout manager (its default layout manager) to position three buttons:
054: * <p>
055: * <img src="doc-files/FlowLayout-1.gif"
056: * ALT="Graphic of Layout for Three Buttons"
057: * ALIGN=center HSPACE=10 VSPACE=7>
058: * <p>
059: * Here is the code for this applet:
060: * <p>
061: * <hr><blockquote><pre>
062: * import java.awt.*;
063: * import java.applet.Applet;
064: *
065: * public class myButtons extends Applet {
066: * Button button1, button2, button3;
067: * public void init() {
068: * button1 = new Button("Ok");
069: * button2 = new Button("Open");
070: * button3 = new Button("Close");
071: * add(button1);
072: * add(button2);
073: * add(button3);
074: * }
075: * }
076: * </pre></blockquote><hr>
077: * <p>
078: * A flow layout lets each component assume its natural (preferred) size.
079: *
080: * @version 1.65, 05/05/07
081: * @author Arthur van Hoff
082: * @author Sami Shaio
083: * @since JDK1.0
084: * @see ComponentOrientation
085: */
086: public class FlowLayout implements LayoutManager, java.io.Serializable {
087:
088: /**
089: * This value indicates that each row of components
090: * should be left-justified.
091: */
092: public static final int LEFT = 0;
093:
094: /**
095: * This value indicates that each row of components
096: * should be centered.
097: */
098: public static final int CENTER = 1;
099:
100: /**
101: * This value indicates that each row of components
102: * should be right-justified.
103: */
104: public static final int RIGHT = 2;
105:
106: /**
107: * This value indicates that each row of components
108: * should be justified to the leading edge of the container's
109: * orientation, for example, to the left in left-to-right orientations.
110: *
111: * @see java.awt.Component#getComponentOrientation
112: * @see java.awt.ComponentOrientation
113: * @since 1.2
114: */
115: public static final int LEADING = 3;
116:
117: /**
118: * This value indicates that each row of components
119: * should be justified to the trailing edge of the container's
120: * orientation, for example, to the right in left-to-right orientations.
121: *
122: * @see java.awt.Component#getComponentOrientation
123: * @see java.awt.ComponentOrientation
124: * @since 1.2
125: */
126: public static final int TRAILING = 4;
127:
128: /**
129: * <code>align</code> is the property that determines
130: * how each row distributes empty space.
131: * It can be one of the following values:
132: * <ul>
133: * <code>LEFT</code>
134: * <code>RIGHT</code>
135: * <code>CENTER</code>
136: * </ul>
137: *
138: * @serial
139: * @see #getAlignment
140: * @see #setAlignment
141: */
142: int align; // This is for 1.1 serialization compatibility
143:
144: /**
145: * <code>newAlign</code> is the property that determines
146: * how each row distributes empty space for the Java 2 platform,
147: * v1.2 and greater.
148: * It can be one of the following three values:
149: * <ul>
150: * <code>LEFT</code>
151: * <code>RIGHT</code>
152: * <code>CENTER</code>
153: * <code>LEADING</code>
154: * <code>TRAILING</code>
155: * </ul>
156: *
157: * @serial
158: * @since 1.2
159: * @see #getAlignment
160: * @see #setAlignment
161: */
162: int newAlign; // This is the one we actually use
163:
164: /**
165: * The flow layout manager allows a seperation of
166: * components with gaps. The horizontal gap will
167: * specify the space between components and between
168: * the components and the borders of the
169: * <code>Container</code>.
170: *
171: * @serial
172: * @see #getHgap()
173: * @see #setHgap(int)
174: */
175: int hgap;
176:
177: /**
178: * The flow layout manager allows a seperation of
179: * components with gaps. The vertical gap will
180: * specify the space between rows and between the
181: * the rows and the borders of the <code>Container</code>.
182: *
183: * @serial
184: * @see #getHgap()
185: * @see #setHgap(int)
186: */
187: int vgap;
188:
189: /**
190: * If true, components will be aligned on their baseline.
191: */
192: private boolean alignOnBaseline;
193:
194: /*
195: * JDK 1.1 serialVersionUID
196: */
197: private static final long serialVersionUID = -7262534875583282631L;
198:
199: /**
200: * Constructs a new <code>FlowLayout</code> with a centered alignment and a
201: * default 5-unit horizontal and vertical gap.
202: */
203: public FlowLayout() {
204: this (CENTER, 5, 5);
205: }
206:
207: /**
208: * Constructs a new <code>FlowLayout</code> with the specified
209: * alignment and a default 5-unit horizontal and vertical gap.
210: * The value of the alignment argument must be one of
211: * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
212: * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>,
213: * or <code>FlowLayout.TRAILING</code>.
214: * @param align the alignment value
215: */
216: public FlowLayout(int align) {
217: this (align, 5, 5);
218: }
219:
220: /**
221: * Creates a new flow layout manager with the indicated alignment
222: * and the indicated horizontal and vertical gaps.
223: * <p>
224: * The value of the alignment argument must be one of
225: * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
226: * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>,
227: * or <code>FlowLayout.TRAILING</code>.
228: * @param align the alignment value
229: * @param hgap the horizontal gap between components
230: * and between the components and the
231: * borders of the <code>Container</code>
232: * @param vgap the vertical gap between components
233: * and between the components and the
234: * borders of the <code>Container</code>
235: */
236: public FlowLayout(int align, int hgap, int vgap) {
237: this .hgap = hgap;
238: this .vgap = vgap;
239: setAlignment(align);
240: }
241:
242: /**
243: * Gets the alignment for this layout.
244: * Possible values are <code>FlowLayout.LEFT</code>,
245: * <code>FlowLayout.RIGHT</code>, <code>FlowLayout.CENTER</code>,
246: * <code>FlowLayout.LEADING</code>,
247: * or <code>FlowLayout.TRAILING</code>.
248: * @return the alignment value for this layout
249: * @see java.awt.FlowLayout#setAlignment
250: * @since JDK1.1
251: */
252: public int getAlignment() {
253: return newAlign;
254: }
255:
256: /**
257: * Sets the alignment for this layout.
258: * Possible values are
259: * <ul>
260: * <li><code>FlowLayout.LEFT</code>
261: * <li><code>FlowLayout.RIGHT</code>
262: * <li><code>FlowLayout.CENTER</code>
263: * <li><code>FlowLayout.LEADING</code>
264: * <li><code>FlowLayout.TRAILING</code>
265: * </ul>
266: * @param align one of the alignment values shown above
267: * @see #getAlignment()
268: * @since JDK1.1
269: */
270: public void setAlignment(int align) {
271: this .newAlign = align;
272:
273: // this.align is used only for serialization compatibility,
274: // so set it to a value compatible with the 1.1 version
275: // of the class
276:
277: switch (align) {
278: case LEADING:
279: this .align = LEFT;
280: break;
281: case TRAILING:
282: this .align = RIGHT;
283: break;
284: default:
285: this .align = align;
286: break;
287: }
288: }
289:
290: /**
291: * Gets the horizontal gap between components
292: * and between the components and the borders
293: * of the <code>Container</code>
294: *
295: * @return the horizontal gap between components
296: * and between the components and the borders
297: * of the <code>Container</code>
298: * @see java.awt.FlowLayout#setHgap
299: * @since JDK1.1
300: */
301: public int getHgap() {
302: return hgap;
303: }
304:
305: /**
306: * Sets the horizontal gap between components and
307: * between the components and the borders of the
308: * <code>Container</code>.
309: *
310: * @param hgap the horizontal gap between components
311: * and between the components and the borders
312: * of the <code>Container</code>
313: * @see java.awt.FlowLayout#getHgap
314: * @since JDK1.1
315: */
316: public void setHgap(int hgap) {
317: this .hgap = hgap;
318: }
319:
320: /**
321: * Gets the vertical gap between components and
322: * between the components and the borders of the
323: * <code>Container</code>.
324: *
325: * @return the vertical gap between components
326: * and between the components and the borders
327: * of the <code>Container</code>
328: * @see java.awt.FlowLayout#setVgap
329: * @since JDK1.1
330: */
331: public int getVgap() {
332: return vgap;
333: }
334:
335: /**
336: * Sets the vertical gap between components and between
337: * the components and the borders of the <code>Container</code>.
338: *
339: * @param vgap the vertical gap between components
340: * and between the components and the borders
341: * of the <code>Container</code>
342: * @see java.awt.FlowLayout#getVgap
343: * @since JDK1.1
344: */
345: public void setVgap(int vgap) {
346: this .vgap = vgap;
347: }
348:
349: /**
350: * Sets whether or not components should be vertically aligned along their
351: * baseline. Components that do not have a baseline will be centered.
352: * The default is false.
353: *
354: * @param alignOnBaseline whether or not components should be
355: * vertically aligned on their baseline
356: * @since 1.6
357: */
358: public void setAlignOnBaseline(boolean alignOnBaseline) {
359: this .alignOnBaseline = alignOnBaseline;
360: }
361:
362: /**
363: * Returns true if components are to be vertically aligned along
364: * their baseline. The default is false.
365: *
366: * @return true if components are to be vertically aligned along
367: * their baseline
368: * @since 1.6
369: */
370: public boolean getAlignOnBaseline() {
371: return alignOnBaseline;
372: }
373:
374: /**
375: * Adds the specified component to the layout.
376: * Not used by this class.
377: * @param name the name of the component
378: * @param comp the component to be added
379: */
380: public void addLayoutComponent(String name, Component comp) {
381: }
382:
383: /**
384: * Removes the specified component from the layout.
385: * Not used by this class.
386: * @param comp the component to remove
387: * @see java.awt.Container#removeAll
388: */
389: public void removeLayoutComponent(Component comp) {
390: }
391:
392: /**
393: * Returns the preferred dimensions for this layout given the
394: * <i>visible</i> components in the specified target container.
395: *
396: * @param target the container that needs to be laid out
397: * @return the preferred dimensions to lay out the
398: * subcomponents of the specified container
399: * @see Container
400: * @see #minimumLayoutSize
401: * @see java.awt.Container#getPreferredSize
402: */
403: public Dimension preferredLayoutSize(Container target) {
404: synchronized (target.getTreeLock()) {
405: Dimension dim = new Dimension(0, 0);
406: int nmembers = target.getComponentCount();
407: boolean firstVisibleComponent = true;
408: boolean useBaseline = getAlignOnBaseline();
409: int maxAscent = 0;
410: int maxDescent = 0;
411:
412: for (int i = 0; i < nmembers; i++) {
413: Component m = target.getComponent(i);
414: if (m.isVisible()) {
415: Dimension d = m.getPreferredSize();
416: dim.height = Math.max(dim.height, d.height);
417: if (firstVisibleComponent) {
418: firstVisibleComponent = false;
419: } else {
420: dim.width += hgap;
421: }
422: dim.width += d.width;
423: if (useBaseline) {
424: int baseline = m.getBaseline(d.width, d.height);
425: if (baseline >= 0) {
426: maxAscent = Math.max(maxAscent, baseline);
427: maxDescent = Math.max(maxDescent, d.height
428: - baseline);
429: }
430: }
431: }
432: }
433: if (useBaseline) {
434: dim.height = Math.max(maxAscent + maxDescent,
435: dim.height);
436: }
437: Insets insets = target.getInsets();
438: dim.width += insets.left + insets.right + hgap * 2;
439: dim.height += insets.top + insets.bottom + vgap * 2;
440: return dim;
441: }
442: }
443:
444: /**
445: * Returns the minimum dimensions needed to layout the <i>visible</i>
446: * components contained in the specified target container.
447: * @param target the container that needs to be laid out
448: * @return the minimum dimensions to lay out the
449: * subcomponents of the specified container
450: * @see #preferredLayoutSize
451: * @see java.awt.Container
452: * @see java.awt.Container#doLayout
453: */
454: public Dimension minimumLayoutSize(Container target) {
455: synchronized (target.getTreeLock()) {
456: boolean useBaseline = getAlignOnBaseline();
457: Dimension dim = new Dimension(0, 0);
458: int nmembers = target.getComponentCount();
459: int maxAscent = 0;
460: int maxDescent = 0;
461: boolean firstVisibleComponent = true;
462:
463: for (int i = 0; i < nmembers; i++) {
464: Component m = target.getComponent(i);
465: if (m.visible) {
466: Dimension d = m.getMinimumSize();
467: dim.height = Math.max(dim.height, d.height);
468: if (firstVisibleComponent) {
469: firstVisibleComponent = false;
470: } else {
471: dim.width += hgap;
472: }
473: dim.width += d.width;
474: if (useBaseline) {
475: int baseline = m.getBaseline(d.width, d.height);
476: if (baseline >= 0) {
477: maxAscent = Math.max(maxAscent, baseline);
478: maxDescent = Math.max(maxDescent,
479: dim.height - baseline);
480: }
481: }
482: }
483: }
484:
485: if (useBaseline) {
486: dim.height = Math.max(maxAscent + maxDescent,
487: dim.height);
488: }
489:
490: Insets insets = target.getInsets();
491: dim.width += insets.left + insets.right + hgap * 2;
492: dim.height += insets.top + insets.bottom + vgap * 2;
493: return dim;
494:
495: }
496: }
497:
498: /**
499: * Centers the elements in the specified row, if there is any slack.
500: * @param target the component which needs to be moved
501: * @param x the x coordinate
502: * @param y the y coordinate
503: * @param width the width dimensions
504: * @param height the height dimensions
505: * @param rowStart the beginning of the row
506: * @param rowEnd the the ending of the row
507: * @param useBaseline Whether or not to align on baseline.
508: * @param ascent Ascent for the components. This is only valid if
509: * useBaseline is true.
510: * @param descent Ascent for the components. This is only valid if
511: * useBaseline is true.
512: * @return actual row height
513: */
514: private int moveComponents(Container target, int x, int y,
515: int width, int height, int rowStart, int rowEnd,
516: boolean ltr, boolean useBaseline, int[] ascent,
517: int[] descent) {
518: switch (newAlign) {
519: case LEFT:
520: x += ltr ? 0 : width;
521: break;
522: case CENTER:
523: x += width / 2;
524: break;
525: case RIGHT:
526: x += ltr ? width : 0;
527: break;
528: case LEADING:
529: break;
530: case TRAILING:
531: x += width;
532: break;
533: }
534: int maxAscent = 0;
535: int nonbaselineHeight = 0;
536: int baselineOffset = 0;
537: if (useBaseline) {
538: int maxDescent = 0;
539: for (int i = rowStart; i < rowEnd; i++) {
540: Component m = target.getComponent(i);
541: if (m.visible) {
542: if (ascent[i] >= 0) {
543: maxAscent = Math.max(maxAscent, ascent[i]);
544: maxDescent = Math.max(maxDescent, descent[i]);
545: } else {
546: nonbaselineHeight = Math.max(m.getHeight(),
547: nonbaselineHeight);
548: }
549: }
550: }
551: height = Math
552: .max(maxAscent + maxDescent, nonbaselineHeight);
553: baselineOffset = (height - maxAscent - maxDescent) / 2;
554: }
555: for (int i = rowStart; i < rowEnd; i++) {
556: Component m = target.getComponent(i);
557: if (m.isVisible()) {
558: int cy;
559: if (useBaseline && ascent[i] >= 0) {
560: cy = y + baselineOffset + maxAscent - ascent[i];
561: } else {
562: cy = y + (height - m.height) / 2;
563: }
564: if (ltr) {
565: m.setLocation(x, cy);
566: } else {
567: m.setLocation(target.width - x - m.width, cy);
568: }
569: x += m.width + hgap;
570: }
571: }
572: return height;
573: }
574:
575: /**
576: * Lays out the container. This method lets each
577: * <i>visible</i> component take
578: * its preferred size by reshaping the components in the
579: * target container in order to satisfy the alignment of
580: * this <code>FlowLayout</code> object.
581: *
582: * @param target the specified component being laid out
583: * @see Container
584: * @see java.awt.Container#doLayout
585: */
586: public void layoutContainer(Container target) {
587: synchronized (target.getTreeLock()) {
588: Insets insets = target.getInsets();
589: int maxwidth = target.width
590: - (insets.left + insets.right + hgap * 2);
591: int nmembers = target.getComponentCount();
592: int x = 0, y = insets.top + vgap;
593: int rowh = 0, start = 0;
594:
595: boolean ltr = target.getComponentOrientation()
596: .isLeftToRight();
597:
598: boolean useBaseline = getAlignOnBaseline();
599: int[] ascent = null;
600: int[] descent = null;
601:
602: if (useBaseline) {
603: ascent = new int[nmembers];
604: descent = new int[nmembers];
605: }
606:
607: for (int i = 0; i < nmembers; i++) {
608: Component m = target.getComponent(i);
609: if (m.isVisible()) {
610: Dimension d = m.getPreferredSize();
611: m.setSize(d.width, d.height);
612:
613: if (useBaseline) {
614: int baseline = m.getBaseline(d.width, d.height);
615: if (baseline >= 0) {
616: ascent[i] = baseline;
617: descent[i] = d.height - baseline;
618: } else {
619: ascent[i] = -1;
620: }
621: }
622: if ((x == 0) || ((x + d.width) <= maxwidth)) {
623: if (x > 0) {
624: x += hgap;
625: }
626: x += d.width;
627: rowh = Math.max(rowh, d.height);
628: } else {
629: rowh = moveComponents(target, insets.left
630: + hgap, y, maxwidth - x, rowh, start,
631: i, ltr, useBaseline, ascent, descent);
632: x = d.width;
633: y += vgap + rowh;
634: rowh = d.height;
635: start = i;
636: }
637: }
638: }
639: moveComponents(target, insets.left + hgap, y, maxwidth - x,
640: rowh, start, nmembers, ltr, useBaseline, ascent,
641: descent);
642: }
643: }
644:
645: //
646: // the internal serial version which says which version was written
647: // - 0 (default) for versions before the Java 2 platform, v1.2
648: // - 1 for version >= Java 2 platform v1.2, which includes "newAlign" field
649: //
650: private static final int currentSerialVersion = 1;
651: /**
652: * This represent the <code>currentSerialVersion</code>
653: * which is bein used. It will be one of two values :
654: * <code>0</code> versions before Java 2 platform v1.2..
655: * <code>1</code> versions after Java 2 platform v1.2..
656: *
657: * @serial
658: * @since 1.2
659: */
660: private int serialVersionOnStream = currentSerialVersion;
661:
662: /**
663: * Reads this object out of a serialization stream, handling
664: * objects written by older versions of the class that didn't contain all
665: * of the fields we use now..
666: */
667: private void readObject(ObjectInputStream stream)
668: throws IOException, ClassNotFoundException {
669: stream.defaultReadObject();
670:
671: if (serialVersionOnStream < 1) {
672: // "newAlign" field wasn't present, so use the old "align" field.
673: setAlignment(this .align);
674: }
675: serialVersionOnStream = currentSerialVersion;
676: }
677:
678: /**
679: * Returns a string representation of this <code>FlowLayout</code>
680: * object and its values.
681: * @return a string representation of this layout
682: */
683: public String toString() {
684: String str = "";
685: switch (align) {
686: case LEFT:
687: str = ",align=left";
688: break;
689: case CENTER:
690: str = ",align=center";
691: break;
692: case RIGHT:
693: str = ",align=right";
694: break;
695: case LEADING:
696: str = ",align=leading";
697: break;
698: case TRAILING:
699: str = ",align=trailing";
700: break;
701: }
702: return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap
703: + str + "]";
704: }
705:
706: }
|