GNU Classpath (0.18) | ||
Frames | No Frames |
1: /* BoxLayout.java -- A layout for swing components. 2: Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.swing; 39: 40: import java.awt.AWTError; 41: import java.awt.Component; 42: import java.awt.ComponentOrientation; 43: import java.awt.Container; 44: import java.awt.Dimension; 45: import java.awt.Insets; 46: import java.awt.LayoutManager2; 47: import java.io.Serializable; 48: import java.util.Collection; 49: import java.util.Iterator; 50: import java.util.List; 51: import java.util.Vector; 52: 53: import gnu.java.awt.AWTUtilities; 54: 55: /** 56: * A layout that stacks the children of a container in a Box, either 57: * horizontally or vertically. 58: * 59: * @author Ronald Veldema (rveldema@cs.vu.nl) 60: * @author Roman Kennke (roman@kennke.org) 61: */ 62: public class BoxLayout implements LayoutManager2, Serializable 63: { 64: 65: /** 66: * This is an abstraction that allows the BoxLayout algorithm to 67: * be applied to both direction (X and Y) without duplicating the 68: * algorithm. It defines several methods that access properties of 69: * a component for a specific direction. 70: */ 71: static interface Direction 72: { 73: /** 74: * Returns the correct part of <code>d</code> for this direction. This will 75: * be <code>d.width</code> for horizontal and <code>d.height</code> for 76: * vertical direction. 77: * 78: * @param d the size as Dimension object 79: * 80: * @return the correct part of <code>d</code> for this direction 81: */ 82: int size(Dimension d); 83: 84: /** 85: * Returns the lower bounds of the {@link Insets} object according to this 86: * direction. This will be <code>insets.top</code> for vertical direction 87: * and <code>insets.left</code> for horizontal direction. 88: * 89: * @param the {@link Insets} object from which to return the lower bounds 90: * 91: * @return the lower bounds of the {@link Insets} object according to this 92: * direction 93: */ 94: int lower(Insets insets); 95: 96: /** 97: * Returns the alignment property according to this direction. 98: * 99: * @param comp the Component for which to return the alignment property 100: * 101: * @return the alignment property according to this direction 102: */ 103: float alignment(Component comp); 104: 105: /** 106: * Sets the location for Component <code>c</code>. <code>coord1</code> 107: * specifies the coordinate of the location in this direction, 108: * <code>coord2</code> the coordinate of the location in the opposite 109: * direction. 110: * 111: * @param c the Component for which to set the location 112: * @param coord1 the coordinate in this direction 113: * @param coord2 the coordinate in the opposite direction 114: */ 115: void setLocation(Component c, int coord1, int coord2); 116: 117: /** 118: * Sets the size for Component <code>c</code>. <code>coord1</code> 119: * specifies the size in this direction, 120: * <code>coord2</code> the size in the opposite 121: * direction. 122: * 123: * @param c the Component for which to set the size 124: * @param size1 the size in this direction 125: * @param size2 the size in the opposite direction 126: */ 127: void setSize(Component c, int size1, int size2); 128: } 129: 130: /** 131: * The horizontal direction. 132: */ 133: static class Horizontal implements Direction 134: { 135: /** 136: * Returns the correct part of <code>d</code> for this direction. This will 137: * be <code>d.width</code> for horizontal and <code>d.height</code> for 138: * vertical direction. 139: * 140: * @param d the size as Dimension object 141: * 142: * @return the correct part of <code>d</code> for this direction 143: */ 144: public int size(Dimension d) 145: { 146: return d.width; 147: } 148: 149: /** 150: * Returns the lower bounds of the {@link Insets} object according to this 151: * direction. This will be <code>insets.top</code> for vertical direction 152: * and <code>insets.left</code> for horizontal direction. 153: * 154: * @param insets the {@link Insets} object from which to return the lower 155: * bounds 156: * 157: * @return the lower bounds of the {@link Insets} object according to this 158: * direction 159: */ 160: public int lower(Insets insets) 161: { 162: return insets.left; 163: } 164: 165: /** 166: * Returns the alignment property according to this direction. 167: * 168: * @param comp the Component for which to return the alignment property 169: * 170: * @return the alignment property according to this direction 171: */ 172: public float alignment(Component comp) 173: { 174: return comp.getAlignmentX(); 175: } 176: 177: /** 178: * Sets the location for Component <code>c</code>. <code>coord1</code> 179: * specifies the coordinate of the location in this direction, 180: * <code>coord2</code> the coordinate of the location in the opposite 181: * direction. 182: * 183: * @param c the Component for which to set the location 184: * @param coord1 the coordinate in this direction 185: * @param coord2 the coordinate in the opposite direction 186: */ 187: public void setLocation(Component c, int coord1, int coord2) 188: { 189: c.setLocation(coord1, coord2); 190: } 191: 192: /** 193: * Sets the size for Component <code>c</code>. <code>coord1</code> 194: * specifies the size in this direction, 195: * <code>coord2</code> the size in the opposite 196: * direction. 197: * 198: * @param c the Component for which to set the size 199: * @param size1 the size in this direction 200: * @param size2 the size in the opposite direction 201: */ 202: public void setSize(Component c, int size1, int size2) 203: { 204: c.setSize(size1, size2); 205: } 206: } 207: /** 208: * The vertical direction. 209: */ 210: static class Vertical implements Direction 211: { 212: /** 213: * Returns the correct part of <code>d</code> for this direction. This will 214: * be <code>d.width</code> for horizontal and <code>d.height</code> for 215: * vertical direction. 216: * 217: * @param d the size as Dimension object 218: * 219: * @return the correct part of <code>d</code> for this direction 220: */ 221: public int size(Dimension d) 222: { 223: return d.height; 224: } 225: 226: /** 227: * Returns the lower bounds of the {@link Insets} object according to this 228: * direction. This will be <code>insets.top</code> for vertical direction 229: * and <code>insets.left</code> for horizontal direction. 230: * 231: * @param insets the {@link Insets} object from which to return the lower 232: * bounds 233: * 234: * @return the lower bounds of the {@link Insets} object according to this 235: * direction 236: */ 237: public int lower(Insets insets) 238: { 239: return insets.top; 240: } 241: 242: /** 243: * Returns the alignment property according to this direction. 244: * 245: * @param comp the Component for which to return the alignment property 246: * 247: * @return the alignment property according to this direction 248: */ 249: public float alignment(Component comp) 250: { 251: return comp.getAlignmentY(); 252: } 253: 254: /** 255: * Sets the location for Component <code>c</code>. <code>coord1</code> 256: * specifies the coordinate of the location in this direction, 257: * <code>coord2</code> the coordinate of the location in the opposite 258: * direction. 259: * 260: * @param c the Component for which to set the location 261: * @param coord1 the coordinate in this direction 262: * @param coord2 the coordinate in the opposite direction 263: */ 264: public void setLocation(Component c, int coord1, int coord2) 265: { 266: c.setLocation(coord2, coord1); 267: } 268: 269: /** 270: * Sets the size for Component <code>c</code>. <code>coord1</code> 271: * specifies the size in this direction, 272: * <code>coord2</code> the size in the opposite 273: * direction. 274: * 275: * @param c the Component for which to set the size 276: * @param size1 the size in this direction 277: * @param size2 the size in the opposite direction 278: */ 279: public void setSize(Component c, int size1, int size2) 280: { 281: c.setSize(size2, size1); 282: } 283: } 284: 285: /** 286: * A helper class that temporarily stores the size specs of a component. 287: */ 288: static class SizeReq 289: { 290: int size; 291: int min; 292: int pref; 293: int max; 294: float align; 295: Component comp; 296: SizeReq(Component comp, Direction dir) 297: { 298: this.min = dir.size(comp.getMinimumSize()); 299: this.pref = dir.size(comp.getPreferredSize()); 300: this.max = dir.size(comp.getMaximumSize()); 301: this.size = dir.size(comp.getSize()); 302: this.align = dir.alignment(comp); 303: this.comp = comp; 304: } 305: } 306: 307: /** 308: * Specifies that components are laid out left to right. 309: */ 310: public static final int X_AXIS = 0; 311: 312: /** 313: * Specifies that components are laid out top to bottom. 314: */ 315: public static final int Y_AXIS = 1; 316: 317: /** 318: * Specifies that components are laid out in the direction of a line of text. 319: */ 320: public static final int LINE_AXIS = 2; 321: 322: /** 323: * Sepcifies that components are laid out in the direction of the line flow. 324: */ 325: public static final int PAGE_AXIS = 3; 326: 327: /* 328: * Needed for serialization. 329: */ 330: private static final long serialVersionUID = -2474455742719112368L; 331: 332: /* 333: * The container given to the constructor. 334: */ 335: private Container container; 336: 337: /* 338: * Current type of component layouting. Defaults to X_AXIS. 339: */ 340: private int way = X_AXIS; 341: 342: /** Constant for the horizontal direction. */ 343: private static final Direction HORIZONTAL = new Horizontal(); 344: 345: /** Constant for the vertical direction. */ 346: private static final Direction VERTICAL = new Vertical(); 347: 348: /** 349: * Constructs a <code>BoxLayout</code> object. 350: * 351: * @param container The container that needs to be laid out. 352: * @param way The orientation of the components. 353: * 354: * @exception AWTError If way has an invalid value. 355: */ 356: public BoxLayout(Container container, int way) 357: { 358: int width = 0; 359: int height = 0; 360: this.container = container; 361: this.way = way; 362: } 363: 364: /** 365: * Adds a component to the layout. Not used in BoxLayout. 366: * 367: * @param name The name of the component to add. 368: * @param component the component to add to the layout. 369: */ 370: public void addLayoutComponent(String name, Component component) 371: { 372: } 373: 374: /** 375: * Removes a component from the layout. Not used in BoxLayout. 376: * 377: * @param component The component to remove from the layout. 378: */ 379: public void removeLayoutComponent(Component component) 380: { 381: } 382: 383: private boolean isHorizontalIn(Container parent) 384: { 385: ComponentOrientation orientation = parent.getComponentOrientation(); 386: return this.way == X_AXIS 387: || (this.way == LINE_AXIS 388: && orientation.isHorizontal()) 389: || (this.way == PAGE_AXIS 390: && (!orientation.isHorizontal())); 391: } 392: 393: 394: 395: /** 396: * Returns the preferred size of the layout. 397: * 398: * @param parent The container that needs to be laid out. 399: * 400: * @return The dimension of the layout. 401: */ 402: public Dimension preferredLayoutSize(Container parent) 403: { 404: if (parent != container) 405: throw new AWTError("invalid parent"); 406: 407: Insets insets = parent.getInsets(); 408: int x = 0; 409: int y = 0; 410: 411: List children = AWTUtilities.getVisibleChildren(parent); 412: 413: if (isHorizontalIn(parent)) 414: { 415: x = insets.left + insets.right; 416: // sum up preferred widths of components, find maximum of preferred 417: // heights 418: for (Iterator i = children.iterator(); i.hasNext();) 419: { 420: Component comp = (Component) i.next(); 421: Dimension sz = comp.getPreferredSize(); 422: x += sz.width; 423: y = Math.max(y, sz.height); 424: } 425: y += insets.bottom + insets.top; 426: } 427: else 428: { 429: y = insets.top + insets.bottom; 430: // sum up preferred heights of components, find maximum of 431: // preferred widths 432: for (Iterator i = children.iterator(); i.hasNext();) 433: { 434: Component comp = (Component) i.next(); 435: Dimension sz = comp.getPreferredSize(); 436: y += sz.height; 437: x = Math.max(x, sz.width); 438: } 439: x += insets.left + insets.right; 440: } 441: 442: return new Dimension(x, y); 443: } 444: 445: /** 446: * Returns the minimum size of the layout. 447: * 448: * @param parent The container that needs to be laid out. 449: * 450: * @return The dimension of the layout. 451: */ 452: public Dimension minimumLayoutSize(Container parent) 453: { 454: if (parent != container) 455: throw new AWTError("invalid parent"); 456: 457: Insets insets = parent.getInsets(); 458: int x = insets.left + insets.right; 459: int y = insets.bottom + insets.top; 460: 461: List children = AWTUtilities.getVisibleChildren(parent); 462: 463: if (isHorizontalIn(parent)) 464: { 465: // sum up preferred widths of components, find maximum of preferred 466: // heights 467: for (Iterator i = children.iterator(); i.hasNext();) 468: { 469: Component comp = (Component) i.next(); 470: Dimension sz = comp.getMinimumSize(); 471: x += sz.width; 472: y = Math.max(y, sz.height); 473: } 474: } 475: else 476: { 477: // sum up preferred heights of components, find maximum of 478: // preferred widths 479: for (Iterator i = children.iterator(); i.hasNext();) 480: { 481: Component comp = (Component) i.next(); 482: Dimension sz = comp.getMinimumSize(); 483: y += sz.height; 484: x = Math.max(x, sz.width); 485: } 486: } 487: 488: return new Dimension(x, y); 489: } 490: 491: /** 492: * Lays out the specified container using this layout. 493: * 494: * @param parent The container that needs to be laid out. 495: */ 496: public void layoutContainer(Container parent) 497: { 498: if (isHorizontalIn(parent)) 499: layoutAlgorithm(parent, HORIZONTAL, VERTICAL); 500: else 501: layoutAlgorithm(parent, VERTICAL, HORIZONTAL); 502: } 503: 504: /** 505: * Adds a component to the layout. Not used in BoxLayout 506: * 507: * @param child The component to add to the layout. 508: * @param constraints The constraints for the component in the layout. 509: */ 510: public void addLayoutComponent(Component child, Object constraints) 511: { 512: } 513: 514: /** 515: * Returns the alignment along the X axis for the container. 516: * 517: * @param parent The container that needs to be laid out. 518: * 519: * @return The alignment. 520: */ 521: public float getLayoutAlignmentX(Container parent) 522: { 523: if (parent != container) 524: throw new AWTError("invalid parent"); 525: 526: return 0; 527: } 528: 529: /** 530: * Returns the alignment along the Y axis for the container. 531: * 532: * @param parent The container that needs to be laid out. 533: * 534: * @return The alignment. 535: */ 536: public float getLayoutAlignmentY(Container parent) 537: { 538: if (parent != container) 539: throw new AWTError("invalid parent"); 540: 541: return 0; 542: } 543: 544: /** 545: * Invalidates the layout. 546: * 547: * @param parent The container that needs to be laid out. 548: */ 549: public void invalidateLayout(Container parent) 550: { 551: if (parent != container) 552: throw new AWTError("invalid parent"); 553: } 554: 555: /** 556: * Returns the maximum size of the layout gived the components 557: * in the given container. 558: * 559: * @param parent The container that needs to be laid out. 560: * 561: * @return The dimension of the layout. 562: */ 563: public Dimension maximumLayoutSize(Container parent) 564: { 565: if (parent != container) 566: throw new AWTError("invalid parent"); 567: 568: Insets insets = parent.getInsets(); 569: int x = insets.left + insets.right; 570: int y = insets.top + insets.bottom; 571: 572: List children = AWTUtilities.getVisibleChildren(parent); 573: 574: if (isHorizontalIn(parent)) 575: { 576: 577: // sum up preferred widths of components, find maximum of preferred 578: // heights 579: for (Iterator i = children.iterator(); i.hasNext();) 580: { 581: Component comp = (Component) i.next(); 582: Dimension sz = comp.getMaximumSize(); 583: x += sz.width; 584: // Check for overflow. 585: if (x < 0) 586: x = Integer.MAX_VALUE; 587: y = Math.max(y, sz.height); 588: } 589: } 590: else 591: { 592: // sum up preferred heights of components, find maximum of 593: // preferred widths 594: for (Iterator i = children.iterator(); i.hasNext();) 595: { 596: Component comp = (Component) i.next(); 597: Dimension sz = comp.getMaximumSize(); 598: y += sz.height; 599: // Check for overflow 600: if (y < 0) 601: y = Integer.MAX_VALUE; 602: x = Math.max(x, sz.width); 603: } 604: } 605: return new Dimension(x, y); 606: } 607: 608: /** 609: * Lays out the Container <code>c</code> in the layout direction 610: * <code>layoutDir</code>. The direction that is crossing the layout 611: * direction is specified in <code>crossDir</code>. 612: * 613: * @param parent 614: * @param layoutDir 615: * @param crossDir 616: */ 617: void layoutAlgorithm(Container parent, Direction layoutDir, Direction crossDir) 618: { 619: if (parent != container) 620: throw new AWTError("invalid parent"); 621: 622: Dimension parentSize = parent.getSize(); 623: Insets insets = parent.getInsets(); 624: Dimension innerSize = new Dimension(parentSize.width - insets.left 625: - insets.right, parentSize.height 626: - insets.bottom - insets.top); 627: 628: // Set all components to their preferredSizes and sum up the allocated 629: // space. Create SizeReqs for each component and store them in 630: // sizeReqs. Find the maximum size in the crossing direction. 631: List children = AWTUtilities.getVisibleChildren(parent); 632: Vector sizeReqs = new Vector(); 633: int allocated = 0; 634: for (Iterator i = children.iterator(); i.hasNext();) 635: { 636: Component c = (Component) i.next(); 637: SizeReq sizeReq = new SizeReq(c, layoutDir); 638: int preferred = layoutDir.size(c.getPreferredSize()); 639: sizeReq.size = preferred; 640: allocated += preferred; 641: sizeReqs.add(sizeReq); 642: } 643: 644: // Distribute remaining space (may be positive or negative) over components 645: int remainder = layoutDir.size(innerSize) - allocated; 646: distributeSpace(sizeReqs, remainder, layoutDir); 647: 648: // Resize and relocate components. If the component can be sized to 649: // take the full space in the crossing direction, then do so, otherwise 650: // align according to its alingnmentX or alignmentY property. 651: int loc = 0; 652: int offset1 = layoutDir.lower(insets); 653: int offset2 = crossDir.lower(insets); 654: for (Iterator i = sizeReqs.iterator(); i.hasNext();) 655: { 656: SizeReq sizeReq = (SizeReq) i.next(); 657: Component c = sizeReq.comp; 658: int availCrossSize = crossDir.size(innerSize); 659: int maxCross = crossDir.size(c.getMaximumSize()); 660: int crossSize = Math.min(availCrossSize, maxCross); 661: int crossRemainder = availCrossSize - crossSize; 662: int crossLoc = (int) (crossDir.alignment(c) * crossRemainder); 663: layoutDir.setSize(c, sizeReq.size, crossSize); 664: layoutDir.setLocation(c, offset1 + loc, offset2 + crossLoc); 665: loc += sizeReq.size; 666: } 667: } 668: 669: /** 670: * Distributes some space over a set of components. This implementation 671: * tries to set the components as close as possible to their 672: * <code>preferredSize</code>s, and respects the components 673: * <code>minimumSize</code> and <code>maximumSize</code>. 674: * 675: * The algorithm is implemented as follows: 676: * 677: * <ul> 678: * <li>The <code>remainder</code> is divided by the number of components 679: * in <code>freeComponents</code>.</li> 680: * <li>The result is added to (or substracted from) the size of each 681: * component.</li> 682: * <li>If the <code>minimumSize</code> or <code>maximumSize</code> of a 683: * component is exceeded, then this component is set to its 684: * <code>minimumSize</code> or <code>maximumSize</code>, it is removed from 685: * <code>freeComponents</code> and the difference is added to a new 686: * remainder.</li> 687: * <li>Finally, if there is a new remainer != 0 and the 688: * <code>freeComponents.size() != 0</code>, then this method is called 689: * recursivly to distribute the newly allocated remaining space.</li> 690: * </ul> 691: * 692: * @param freeComponents a SizeReq collection for components that have space 693: * left so that they can be moved freely 694: * @param remainder the space that should be distributed between the 695: * components 696: * @param dir the direction in which we operate 697: */ 698: void distributeSpace(Collection freeComponents, int remainder, Direction dir) 699: { 700: // Sum up total available space in components. If the remainder is negative 701: // then we sum up the difference between minSize and size. If remainder 702: // is positive we sum up the difference between maxSize and size. 703: double totalAvailable = 0; 704: for (Iterator i = freeComponents.iterator(); i.hasNext();) 705: { 706: SizeReq sizeReq = (SizeReq) i.next(); 707: if (remainder >= 0) 708: totalAvailable += sizeReq.max - sizeReq.size; 709: else 710: totalAvailable += sizeReq.min - sizeReq.size; 711: } 712: if (totalAvailable == 0) 713: if (remainder >= 0) 714: totalAvailable = 1; 715: else 716: totalAvailable = -1; 717: 718: int newRemainder = 0; 719: Vector stillFree = new Vector(); 720: for (Iterator i = freeComponents.iterator(); i.hasNext();) 721: { 722: // Add/substract share to component. 723: SizeReq sizeReq = (SizeReq) i.next(); 724: double available = 0; 725: if (remainder >= 0) 726: available = sizeReq.max - sizeReq.size; 727: else 728: available = sizeReq.min - sizeReq.size; 729: int share = (int) ((available / totalAvailable) * remainder); 730: sizeReq.size += share; 731: // check for min/maximumSize 732: if (sizeReq.size < sizeReq.min) 733: { 734: newRemainder += sizeReq.size - sizeReq.min; 735: sizeReq.size = sizeReq.min; 736: } 737: else if (sizeReq.size > sizeReq.max) 738: { 739: newRemainder += sizeReq.size - sizeReq.max; 740: sizeReq.size = sizeReq.max; 741: } 742: else 743: stillFree.add(sizeReq); 744: } 745: // recursivly call this method if necessary 746: if (newRemainder != 0 && stillFree.size() > 0) 747: distributeSpace(stillFree, newRemainder, dir); 748: } 749: }
GNU Classpath (0.18) |