1 /* 2 * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package java.awt; 26 27 import java.io.ObjectInputStream; 28 import java.io.IOException; 29 30 /** 31 * A flow layout arranges components in a directional flow, much 32 * like lines of text in a paragraph. The flow direction is 33 * determined by the container's <code>componentOrientation</code> 34 * property and may be one of two values: 35 * <ul> 36 * <li><code>ComponentOrientation.LEFT_TO_RIGHT</code> 37 * <li><code>ComponentOrientation.RIGHT_TO_LEFT</code> 38 * </ul> 39 * Flow layouts are typically used 40 * to arrange buttons in a panel. It arranges buttons 41 * horizontally until no more buttons fit on the same line. 42 * The line alignment is determined by the <code>align</code> 43 * property. The possible values are: 44 * <ul> 45 * <li>{@link #LEFT LEFT} 46 * <li>{@link #RIGHT RIGHT} 47 * <li>{@link #CENTER CENTER} 48 * <li>{@link #LEADING LEADING} 49 * <li>{@link #TRAILING TRAILING} 50 * </ul> 51 * <p> 52 * For example, the following picture shows an applet using the flow 53 * layout manager (its default layout manager) to position three buttons: 54 * <p> 55 * <img src="doc-files/FlowLayout-1.gif" 56 * ALT="Graphic of Layout for Three Buttons" 57 * style="float:center; margin: 7px 10px;"> 58 * <p> 59 * Here is the code for this applet: 60 * 61 * <hr><blockquote><pre> 62 * import java.awt.*; 63 * import java.applet.Applet; 64 * 65 * public class myButtons extends Applet { 66 * Button button1, button2, button3; 67 * public void init() { 68 * button1 = new Button("Ok"); 69 * button2 = new Button("Open"); 70 * button3 = new Button("Close"); 71 * add(button1); 72 * add(button2); 73 * add(button3); 74 * } 75 * } 76 * </pre></blockquote><hr> 77 * <p> 78 * A flow layout lets each component assume its natural (preferred) size. 79 * 80 * @author Arthur van Hoff 81 * @author Sami Shaio 82 * @since 1.0 83 * @see ComponentOrientation 84 */ 85 public class FlowLayout implements LayoutManager, java.io.Serializable { 86 87 /** 88 * This value indicates that each row of components 89 * should be left-justified. 90 */ 91 public static final int LEFT = 0; 92 93 /** 94 * This value indicates that each row of components 95 * should be centered. 96 */ 97 public static final int CENTER = 1; 98 99 /** 100 * This value indicates that each row of components 101 * should be right-justified. 102 */ 103 public static final int RIGHT = 2; 104 105 /** 106 * This value indicates that each row of components 107 * should be justified to the leading edge of the container's 108 * orientation, for example, to the left in left-to-right orientations. 109 * 110 * @see java.awt.Component#getComponentOrientation 111 * @see java.awt.ComponentOrientation 112 * @since 1.2 113 */ 114 public static final int LEADING = 3; 115 116 /** 117 * This value indicates that each row of components 118 * should be justified to the trailing edge of the container's 119 * orientation, for example, to the right in left-to-right orientations. 120 * 121 * @see java.awt.Component#getComponentOrientation 122 * @see java.awt.ComponentOrientation 123 * @since 1.2 124 */ 125 public static final int TRAILING = 4; 126 127 /** 128 * <code>align</code> is the property that determines 129 * how each row distributes empty space. 130 * It can be one of the following values: 131 * <ul> 132 * <li><code>LEFT</code> 133 * <li><code>RIGHT</code> 134 * <li><code>CENTER</code> 135 * </ul> 136 * 137 * @serial 138 * @see #getAlignment 139 * @see #setAlignment 140 */ 141 int align; // This is for 1.1 serialization compatibility 142 143 /** 144 * <code>newAlign</code> is the property that determines 145 * how each row distributes empty space for the Java 2 platform, 146 * v1.2 and greater. 147 * It can be one of the following three values: 148 * <ul> 149 * <li><code>LEFT</code> 150 * <li><code>RIGHT</code> 151 * <li><code>CENTER</code> 152 * <li><code>LEADING</code> 153 * <li><code>TRAILING</code> 154 * </ul> 155 * 156 * @serial 157 * @since 1.2 158 * @see #getAlignment 159 * @see #setAlignment 160 */ 161 int newAlign; // This is the one we actually use 162 163 /** 164 * The flow layout manager allows a separation of 165 * components with gaps. The horizontal gap will 166 * specify the space between components and between 167 * the components and the borders of the 168 * <code>Container</code>. 169 * 170 * @serial 171 * @see #getHgap() 172 * @see #setHgap(int) 173 */ 174 int hgap; 175 176 /** 177 * The flow layout manager allows a separation of 178 * components with gaps. The vertical gap will 179 * specify the space between rows and between the 180 * the rows and the borders of the <code>Container</code>. 181 * 182 * @serial 183 * @see #getHgap() 184 * @see #setHgap(int) 185 */ 186 int vgap; 187 188 /** 189 * If true, components will be aligned on their baseline. 190 */ 191 private boolean alignOnBaseline; 192 193 /* 194 * JDK 1.1 serialVersionUID 195 */ 196 private static final long serialVersionUID = -7262534875583282631L; 197 198 /** 199 * Constructs a new <code>FlowLayout</code> with a centered alignment and a 200 * default 5-unit horizontal and vertical gap. 201 */ 202 public FlowLayout() { 203 this(CENTER, 5, 5); 204 } 205 206 /** 207 * Constructs a new <code>FlowLayout</code> with the specified 208 * alignment and a default 5-unit horizontal and vertical gap. 209 * The value of the alignment argument must be one of 210 * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>, 211 * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>, 212 * or <code>FlowLayout.TRAILING</code>. 213 * @param align the alignment value 214 */ 215 public FlowLayout(int align) { 216 this(align, 5, 5); 217 } 218 219 /** 220 * Creates a new flow layout manager with the indicated alignment 221 * and the indicated horizontal and vertical gaps. 222 * <p> 223 * The value of the alignment argument must be one of 224 * <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>, 225 * <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>, 226 * or <code>FlowLayout.TRAILING</code>. 227 * @param align the alignment value 228 * @param hgap the horizontal gap between components 229 * and between the components and the 230 * borders of the <code>Container</code> 231 * @param vgap the vertical gap between components 232 * and between the components and the 233 * borders of the <code>Container</code> 234 */ 235 public FlowLayout(int align, int hgap, int vgap) { 236 this.hgap = hgap; 237 this.vgap = vgap; 238 setAlignment(align); 239 } 240 241 /** 242 * Gets the alignment for this layout. 243 * Possible values are <code>FlowLayout.LEFT</code>, 244 * <code>FlowLayout.RIGHT</code>, <code>FlowLayout.CENTER</code>, 245 * <code>FlowLayout.LEADING</code>, 246 * or <code>FlowLayout.TRAILING</code>. 247 * @return the alignment value for this layout 248 * @see java.awt.FlowLayout#setAlignment 249 * @since 1.1 250 */ 251 public int getAlignment() { 252 return newAlign; 253 } 254 255 /** 256 * Sets the alignment for this layout. 257 * Possible values are 258 * <ul> 259 * <li><code>FlowLayout.LEFT</code> 260 * <li><code>FlowLayout.RIGHT</code> 261 * <li><code>FlowLayout.CENTER</code> 262 * <li><code>FlowLayout.LEADING</code> 263 * <li><code>FlowLayout.TRAILING</code> 264 * </ul> 265 * @param align one of the alignment values shown above 266 * @see #getAlignment() 267 * @since 1.1 268 */ 269 public void setAlignment(int align) { 270 this.newAlign = align; 271 272 // this.align is used only for serialization compatibility, 273 // so set it to a value compatible with the 1.1 version 274 // of the class 275 276 switch (align) { 277 case LEADING: 278 this.align = LEFT; 279 break; 280 case TRAILING: 281 this.align = RIGHT; 282 break; 283 default: 284 this.align = align; 285 break; 286 } 287 } 288 289 /** 290 * Gets the horizontal gap between components 291 * and between the components and the borders 292 * of the <code>Container</code> 293 * 294 * @return the horizontal gap between components 295 * and between the components and the borders 296 * of the <code>Container</code> 297 * @see java.awt.FlowLayout#setHgap 298 * @since 1.1 299 */ 300 public int getHgap() { 301 return hgap; 302 } 303 304 /** 305 * Sets the horizontal gap between components and 306 * between the components and the borders of the 307 * <code>Container</code>. 308 * 309 * @param hgap the horizontal gap between components 310 * and between the components and the borders 311 * of the <code>Container</code> 312 * @see java.awt.FlowLayout#getHgap 313 * @since 1.1 314 */ 315 public void setHgap(int hgap) { 316 this.hgap = hgap; 317 } 318 319 /** 320 * Gets the vertical gap between components and 321 * between the components and the borders of the 322 * <code>Container</code>. 323 * 324 * @return the vertical gap between components 325 * and between the components and the borders 326 * of the <code>Container</code> 327 * @see java.awt.FlowLayout#setVgap 328 * @since 1.1 329 */ 330 public int getVgap() { 331 return vgap; 332 } 333 334 /** 335 * Sets the vertical gap between components and between 336 * the components and the borders of the <code>Container</code>. 337 * 338 * @param vgap the vertical gap between components 339 * and between the components and the borders 340 * of the <code>Container</code> 341 * @see java.awt.FlowLayout#getVgap 342 * @since 1.1 343 */ 344 public void setVgap(int vgap) { 345 this.vgap = vgap; 346 } 347 348 /** 349 * Sets whether or not components should be vertically aligned along their 350 * baseline. Components that do not have a baseline will be centered. 351 * The default is false. 352 * 353 * @param alignOnBaseline whether or not components should be 354 * vertically aligned on their baseline 355 * @since 1.6 356 */ 357 public void setAlignOnBaseline(boolean alignOnBaseline) { 358 this.alignOnBaseline = alignOnBaseline; 359 } 360 361 /** 362 * Returns true if components are to be vertically aligned along 363 * their baseline. The default is false. 364 * 365 * @return true if components are to be vertically aligned along 366 * their baseline 367 * @since 1.6 368 */ 369 public boolean getAlignOnBaseline() { 370 return alignOnBaseline; 371 } 372 373 /** 374 * Adds the specified component to the layout. 375 * Not used by this class. 376 * @param name the name of the component 377 * @param comp the component to be added 378 */ 379 public void addLayoutComponent(String name, Component comp) { 380 } 381 382 /** 383 * Removes the specified component from the layout. 384 * Not used by this class. 385 * @param comp the component to remove 386 * @see java.awt.Container#removeAll 387 */ 388 public void removeLayoutComponent(Component comp) { 389 } 390 391 /** 392 * Returns the preferred dimensions for this layout given the 393 * <i>visible</i> components in the specified target container. 394 * 395 * @param target the container that needs to be laid out 396 * @return the preferred dimensions to lay out the 397 * subcomponents of the specified container 398 * @see Container 399 * @see #minimumLayoutSize 400 * @see java.awt.Container#getPreferredSize 401 */ 402 public Dimension preferredLayoutSize(Container target) { 403 synchronized (target.getTreeLock()) { 404 Dimension dim = new Dimension(0, 0); 405 int nmembers = target.getComponentCount(); 406 boolean firstVisibleComponent = true; 407 boolean useBaseline = getAlignOnBaseline(); 408 int maxAscent = 0; 409 int maxDescent = 0; 410 411 for (int i = 0 ; i < nmembers ; i++) { 412 Component m = target.getComponent(i); 413 if (m.isVisible()) { 414 Dimension d = m.getPreferredSize(); 415 dim.height = Math.max(dim.height, d.height); 416 if (firstVisibleComponent) { 417 firstVisibleComponent = false; 418 } else { 419 dim.width += hgap; 420 } 421 dim.width += d.width; 422 if (useBaseline) { 423 int baseline = m.getBaseline(d.width, d.height); 424 if (baseline >= 0) { 425 maxAscent = Math.max(maxAscent, baseline); 426 maxDescent = Math.max(maxDescent, d.height - baseline); 427 } 428 } 429 } 430 } 431 if (useBaseline) { 432 dim.height = Math.max(maxAscent + maxDescent, dim.height); 433 } 434 Insets insets = target.getInsets(); 435 dim.width += insets.left + insets.right + hgap*2; 436 dim.height += insets.top + insets.bottom + vgap*2; 437 return dim; 438 } 439 } 440 441 /** 442 * Returns the minimum dimensions needed to layout the <i>visible</i> 443 * components contained in the specified target container. 444 * @param target the container that needs to be laid out 445 * @return the minimum dimensions to lay out the 446 * subcomponents of the specified container 447 * @see #preferredLayoutSize 448 * @see java.awt.Container 449 * @see java.awt.Container#doLayout 450 */ 451 public Dimension minimumLayoutSize(Container target) { 452 synchronized (target.getTreeLock()) { 453 boolean useBaseline = getAlignOnBaseline(); 454 Dimension dim = new Dimension(0, 0); 455 int nmembers = target.getComponentCount(); 456 int maxAscent = 0; 457 int maxDescent = 0; 458 boolean firstVisibleComponent = true; 459 460 for (int i = 0 ; i < nmembers ; i++) { 461 Component m = target.getComponent(i); 462 if (m.visible) { 463 Dimension d = m.getMinimumSize(); 464 dim.height = Math.max(dim.height, d.height); 465 if (firstVisibleComponent) { 466 firstVisibleComponent = false; 467 } else { 468 dim.width += hgap; 469 } 470 dim.width += d.width; 471 if (useBaseline) { 472 int baseline = m.getBaseline(d.width, d.height); 473 if (baseline >= 0) { 474 maxAscent = Math.max(maxAscent, baseline); 475 maxDescent = Math.max(maxDescent, 476 dim.height - baseline); 477 } 478 } 479 } 480 } 481 482 if (useBaseline) { 483 dim.height = Math.max(maxAscent + maxDescent, dim.height); 484 } 485 486 Insets insets = target.getInsets(); 487 dim.width += insets.left + insets.right + hgap*2; 488 dim.height += insets.top + insets.bottom + vgap*2; 489 return dim; 490 491 492 493 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, int width, int height, 515 int rowStart, int rowEnd, boolean ltr, 516 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 } 546 else { 547 nonbaselineHeight = Math.max(m.getHeight(), 548 nonbaselineHeight); 549 } 550 } 551 } 552 height = Math.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 } 562 else { 563 cy = y + (height - m.height) / 2; 564 } 565 if (ltr) { 566 m.setLocation(x, cy); 567 } else { 568 m.setLocation(target.width - x - m.width, cy); 569 } 570 x += m.width + hgap; 571 } 572 } 573 return height; 574 } 575 576 /** 577 * Lays out the container. This method lets each 578 * <i>visible</i> component take 579 * its preferred size by reshaping the components in the 580 * target container in order to satisfy the alignment of 581 * this <code>FlowLayout</code> object. 582 * 583 * @param target the specified component being laid out 584 * @see Container 585 * @see java.awt.Container#doLayout 586 */ 587 public void layoutContainer(Container target) { 588 synchronized (target.getTreeLock()) { 589 Insets insets = target.getInsets(); 590 int maxwidth = target.width - (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().isLeftToRight(); 596 597 boolean useBaseline = getAlignOnBaseline(); 598 int[] ascent = null; 599 int[] descent = null; 600 601 if (useBaseline) { 602 ascent = new int[nmembers]; 603 descent = new int[nmembers]; 604 } 605 606 for (int i = 0 ; i < nmembers ; i++) { 607 Component m = target.getComponent(i); 608 if (m.isVisible()) { 609 Dimension d = m.getPreferredSize(); 610 m.setSize(d.width, d.height); 611 612 if (useBaseline) { 613 int baseline = m.getBaseline(d.width, d.height); 614 if (baseline >= 0) { 615 ascent[i] = baseline; 616 descent[i] = d.height - baseline; 617 } 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 + hgap, y, 630 maxwidth - x, rowh, start, i, ltr, 631 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, rowh, 640 start, nmembers, ltr, useBaseline, ascent, descent); 641 } 642 } 643 644 // 645 // the internal serial version which says which version was written 646 // - 0 (default) for versions before the Java 2 platform, v1.2 647 // - 1 for version >= Java 2 platform v1.2, which includes "newAlign" field 648 // 649 private static final int currentSerialVersion = 1; 650 /** 651 * This represent the <code>currentSerialVersion</code> 652 * which is bein used. It will be one of two values : 653 * <code>0</code> versions before Java 2 platform v1.2.. 654 * <code>1</code> versions after Java 2 platform v1.2.. 655 * 656 * @serial 657 * @since 1.2 658 */ 659 private int serialVersionOnStream = currentSerialVersion; 660 661 /** 662 * Reads this object out of a serialization stream, handling 663 * objects written by older versions of the class that didn't contain all 664 * of the fields we use now.. 665 */ 666 private void readObject(ObjectInputStream stream) 667 throws IOException, ClassNotFoundException 668 { 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: str = ",align=left"; break; 687 case CENTER: str = ",align=center"; break; 688 case RIGHT: str = ",align=right"; break; 689 case LEADING: str = ",align=leading"; break; 690 case TRAILING: str = ",align=trailing"; break; 691 } 692 return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]"; 693 } 694 695 696 }