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