1 /* GridBagLayout - Layout manager for components according to GridBagConstraints 2 Copyright (C) 2002, 2003, 2004, 2005, 2006 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 39 package java.awt; 40 41 import java.io.Serializable; 42 import java.util.ArrayList; 43 import java.util.HashMap; 44 import java.util.Hashtable; 45 46 /** 47 * @author Michael Koch (konqueror@gmx.de) 48 * @author Jeroen Frijters (jeroen@frijters.net) 49 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 50 */ 51 public class GridBagLayout 52 implements Serializable, LayoutManager2 53 { 54 private static final long serialVersionUID = 8838754796412211005L; 55 56 protected static final int MINSIZE = 1; 57 protected static final int PREFERREDSIZE = 2; 58 protected static final int MAXGRIDSIZE = 512; 59 60 // comptable remembers the original contraints given to us. 61 // internalcomptable is used to keep track of modified constraint values 62 // that we calculate, particularly when we are given RELATIVE and 63 // REMAINDER constraints. 64 // Constraints kept in comptable are never modified, and constraints 65 // kept in internalcomptable can be modified internally only. 66 protected Hashtable<Component,GridBagConstraints> comptable; 67 private Hashtable<Component,GridBagConstraints> internalcomptable; 68 protected GridBagLayoutInfo layoutInfo; 69 protected GridBagConstraints defaultConstraints; 70 71 public double[] columnWeights; 72 public int[] columnWidths; 73 public double[] rowWeights; 74 public int[] rowHeights; 75 GridBagLayout()76 public GridBagLayout () 77 { 78 this.comptable = new Hashtable<Component,GridBagConstraints>(); 79 this.internalcomptable = new Hashtable<Component,GridBagConstraints>(); 80 this.defaultConstraints= new GridBagConstraints(); 81 } 82 83 /** 84 * Helper method to calc the sum of a range of elements in an int array. 85 */ sumIntArray(int[] array, int upto)86 private int sumIntArray (int[] array, int upto) 87 { 88 int result = 0; 89 90 for (int i = 0; i < upto; i++) 91 result += array [i]; 92 93 return result; 94 } 95 96 /** 97 * Helper method to calc the sum of all elements in an int array. 98 */ sumIntArray(int[] array)99 private int sumIntArray (int[] array) 100 { 101 return sumIntArray(array, array.length); 102 } 103 104 /** 105 * Helper method to calc the sum of all elements in an double array. 106 */ sumDoubleArray(double[] array)107 private double sumDoubleArray (double[] array) 108 { 109 double result = 0; 110 111 for (int i = 0; i < array.length; i++) 112 result += array [i]; 113 114 return result; 115 } 116 addLayoutComponent(String name, Component component)117 public void addLayoutComponent (String name, Component component) 118 { 119 // do nothing here. 120 } 121 removeLayoutComponent(Component component)122 public void removeLayoutComponent (Component component) 123 { 124 // do nothing here 125 } 126 addLayoutComponent(Component component, Object constraints)127 public void addLayoutComponent (Component component, Object constraints) 128 { 129 if (constraints == null) 130 return; 131 132 if (!(constraints instanceof GridBagConstraints)) 133 throw new IllegalArgumentException("constraints " 134 + constraints 135 + " are not an instance of GridBagConstraints"); 136 137 setConstraints (component, (GridBagConstraints) constraints); 138 } 139 preferredLayoutSize(Container parent)140 public Dimension preferredLayoutSize (Container parent) 141 { 142 if (parent == null) 143 return new Dimension (0, 0); 144 145 GridBagLayoutInfo li = getLayoutInfo (parent, PREFERREDSIZE); 146 return getMinSize (parent, li); 147 } 148 minimumLayoutSize(Container parent)149 public Dimension minimumLayoutSize (Container parent) 150 { 151 if (parent == null) 152 return new Dimension (0, 0); 153 154 GridBagLayoutInfo li = getLayoutInfo (parent, MINSIZE); 155 return getMinSize (parent, li); 156 } 157 maximumLayoutSize(Container target)158 public Dimension maximumLayoutSize (Container target) 159 { 160 return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE); 161 } 162 layoutContainer(Container parent)163 public void layoutContainer (Container parent) 164 { 165 arrangeGrid (parent); 166 } 167 getLayoutAlignmentX(Container target)168 public float getLayoutAlignmentX (Container target) 169 { 170 return Component.CENTER_ALIGNMENT; 171 } 172 getLayoutAlignmentY(Container target)173 public float getLayoutAlignmentY (Container target) 174 { 175 return Component.CENTER_ALIGNMENT; 176 } 177 invalidateLayout(Container target)178 public void invalidateLayout (Container target) 179 { 180 this.layoutInfo = null; 181 } 182 setConstraints(Component component, GridBagConstraints constraints)183 public void setConstraints (Component component, 184 GridBagConstraints constraints) 185 { 186 GridBagConstraints clone = (GridBagConstraints) constraints.clone(); 187 188 if (clone.gridx < 0) 189 clone.gridx = GridBagConstraints.RELATIVE; 190 191 if (clone.gridy < 0) 192 clone.gridy = GridBagConstraints.RELATIVE; 193 194 if (clone.gridwidth == 0) 195 clone.gridwidth = GridBagConstraints.REMAINDER; 196 else if (clone.gridwidth < 0) 197 clone.gridwidth = 1; 198 199 if (clone.gridheight == 0) 200 clone.gridheight = GridBagConstraints.REMAINDER; 201 else if (clone.gridheight < 0) 202 clone.gridheight = 1; 203 204 comptable.put (component, clone); 205 } 206 getConstraints(Component component)207 public GridBagConstraints getConstraints (Component component) 208 { 209 return (GridBagConstraints) (lookupConstraints (component).clone()); 210 } 211 lookupConstraints(Component component)212 protected GridBagConstraints lookupConstraints (Component component) 213 { 214 GridBagConstraints result = comptable.get (component); 215 216 if (result == null) 217 { 218 setConstraints (component, defaultConstraints); 219 result = comptable.get (component); 220 } 221 222 return result; 223 } 224 lookupInternalConstraints(Component component)225 private GridBagConstraints lookupInternalConstraints (Component component) 226 { 227 GridBagConstraints result = internalcomptable.get (component); 228 229 if (result == null) 230 { 231 result = (GridBagConstraints) lookupConstraints(component).clone(); 232 internalcomptable.put (component, result); 233 } 234 235 return result; 236 } 237 238 /** 239 * @since 1.1 240 */ getLayoutOrigin()241 public Point getLayoutOrigin () 242 { 243 if (layoutInfo == null) 244 return new Point (0, 0); 245 246 return new Point (layoutInfo.pos_x, layoutInfo.pos_y); 247 } 248 249 /** 250 * @since 1.1 251 */ getLayoutDimensions()252 public int[][] getLayoutDimensions () 253 { 254 int[][] result = new int [2][]; 255 if (layoutInfo == null) 256 { 257 result[0] = new int[0]; 258 result[1] = new int[0]; 259 260 return result; 261 } 262 263 result [0] = new int [layoutInfo.cols]; 264 System.arraycopy (layoutInfo.colWidths, 0, result [0], 0, layoutInfo.cols); 265 result [1] = new int [layoutInfo.rows]; 266 System.arraycopy (layoutInfo.rowHeights, 0, result [1], 0, layoutInfo.rows); 267 return result; 268 } 269 getLayoutWeights()270 public double[][] getLayoutWeights () 271 { 272 double[][] result = new double [2][]; 273 if (layoutInfo == null) 274 { 275 result[0] = new double[0]; 276 result[1] = new double[0]; 277 278 return result; 279 } 280 281 result [0] = new double [layoutInfo.cols]; 282 System.arraycopy (layoutInfo.colWeights, 0, result [0], 0, layoutInfo.cols); 283 result [1] = new double [layoutInfo.rows]; 284 System.arraycopy (layoutInfo.rowWeights, 0, result [1], 0, layoutInfo.rows); 285 return result; 286 } 287 288 /** 289 * @since 1.1 290 */ location(int x, int y)291 public Point location (int x, int y) 292 { 293 if (layoutInfo == null) 294 return new Point (0, 0); 295 296 int col; 297 int row; 298 int pixel_x = layoutInfo.pos_x; 299 int pixel_y = layoutInfo.pos_y; 300 301 for (col = 0; col < layoutInfo.cols; col++) 302 { 303 int w = layoutInfo.colWidths [col]; 304 if (x < pixel_x + w) 305 break; 306 307 pixel_x += w; 308 } 309 310 for (row = 0; row < layoutInfo.rows; row++) 311 { 312 int h = layoutInfo.rowHeights [row]; 313 if (y < pixel_y + h) 314 break; 315 316 pixel_y += h; 317 } 318 319 return new Point (col, row); 320 } 321 322 /** 323 * Return a string representation of this GridBagLayout. 324 * 325 * @return a string representation 326 */ toString()327 public String toString() 328 { 329 return getClass().getName(); 330 } 331 332 /** 333 * Move and resize a rectangle according to a set of grid bag 334 * constraints. The x, y, width and height fields of the 335 * rectangle argument are adjusted to the new values. 336 * 337 * @param constraints position and size constraints 338 * @param r rectangle to be moved and resized 339 */ AdjustForGravity(GridBagConstraints constraints, Rectangle r)340 protected void AdjustForGravity (GridBagConstraints constraints, 341 Rectangle r) 342 { 343 Insets insets = constraints.insets; 344 if (insets != null) 345 { 346 r.x += insets.left; 347 r.y += insets.top; 348 r.width -= insets.left + insets.right; 349 r.height -= insets.top + insets.bottom; 350 } 351 } 352 353 /** 354 * Obsolete. 355 */ ArrangeGrid(Container parent)356 protected void ArrangeGrid (Container parent) 357 { 358 Component[] components = parent.getComponents(); 359 360 if (components.length == 0) 361 return; 362 363 GridBagLayoutInfo info = getLayoutInfo (parent, PREFERREDSIZE); 364 if (info.cols == 0 && info.rows == 0) 365 return; 366 367 // DEBUG 368 //dumpLayoutInfo (info); 369 370 // Calling setBounds on these components causes this layout to 371 // be invalidated, clearing the layout information cache, 372 // layoutInfo. So we wait until after this for loop to set 373 // layoutInfo. 374 Component lastComp = null; 375 376 Rectangle cell = new Rectangle(); 377 378 for (int i = 0; i < components.length; i++) 379 { 380 Component component = components[i]; 381 382 // If component is not visible we dont have to care about it. 383 if (! component.isVisible()) 384 continue; 385 386 Dimension dim = component.getPreferredSize(); 387 GridBagConstraints constraints = lookupInternalConstraints(component); 388 389 if (lastComp != null 390 && constraints.gridheight == GridBagConstraints.REMAINDER) 391 cell.y += cell.height; 392 else 393 cell.y = sumIntArray(info.rowHeights, constraints.gridy); 394 395 if (lastComp != null 396 && constraints.gridwidth == GridBagConstraints.REMAINDER) 397 cell.x += cell.width; 398 else 399 cell.x = sumIntArray(info.colWidths, constraints.gridx); 400 401 cell.width = sumIntArray(info.colWidths, constraints.gridx 402 + constraints.gridwidth) - cell.x; 403 cell.height = sumIntArray(info.rowHeights, constraints.gridy 404 + constraints.gridheight) - cell.y; 405 406 // Adjust for insets. 407 AdjustForGravity( constraints, cell ); 408 409 // Note: Documentation says that padding is added on both sides, but 410 // visual inspection shows that the Sun implementation only adds it 411 // once, so we do the same. 412 dim.width += constraints.ipadx; 413 dim.height += constraints.ipady; 414 415 switch (constraints.fill) 416 { 417 case GridBagConstraints.HORIZONTAL: 418 dim.width = cell.width; 419 break; 420 case GridBagConstraints.VERTICAL: 421 dim.height = cell.height; 422 break; 423 case GridBagConstraints.BOTH: 424 dim.width = cell.width; 425 dim.height = cell.height; 426 break; 427 } 428 429 int x = 0; 430 int y = 0; 431 432 switch (constraints.anchor) 433 { 434 case GridBagConstraints.NORTH: 435 x = cell.x + (cell.width - dim.width) / 2; 436 y = cell.y; 437 break; 438 case GridBagConstraints.SOUTH: 439 x = cell.x + (cell.width - dim.width) / 2; 440 y = cell.y + cell.height - dim.height; 441 break; 442 case GridBagConstraints.WEST: 443 x = cell.x; 444 y = cell.y + (cell.height - dim.height) / 2; 445 break; 446 case GridBagConstraints.EAST: 447 x = cell.x + cell.width - dim.width; 448 y = cell.y + (cell.height - dim.height) / 2; 449 break; 450 case GridBagConstraints.NORTHEAST: 451 x = cell.x + cell.width - dim.width; 452 y = cell.y; 453 break; 454 case GridBagConstraints.NORTHWEST: 455 x = cell.x; 456 y = cell.y; 457 break; 458 case GridBagConstraints.SOUTHEAST: 459 x = cell.x + cell.width - dim.width; 460 y = cell.y + cell.height - dim.height; 461 break; 462 case GridBagConstraints.SOUTHWEST: 463 x = cell.x; 464 y = cell.y + cell.height - dim.height; 465 break; 466 default: 467 x = cell.x + (cell.width - dim.width) / 2; 468 y = cell.y + (cell.height - dim.height) / 2; 469 break; 470 } 471 component.setBounds(info.pos_x + x, info.pos_y + y, dim.width, 472 dim.height); 473 lastComp = component; 474 } 475 476 // DEBUG 477 //dumpLayoutInfo(info); 478 479 // Cache layout information. 480 layoutInfo = getLayoutInfo(parent, PREFERREDSIZE); 481 } 482 483 /** 484 * Obsolete. 485 */ GetLayoutInfo(Container parent, int sizeflag)486 protected GridBagLayoutInfo GetLayoutInfo (Container parent, int sizeflag) 487 { 488 if (sizeflag != MINSIZE && sizeflag != PREFERREDSIZE) 489 throw new IllegalArgumentException(); 490 491 Dimension parentDim = parent.getSize (); 492 Insets parentInsets = parent.getInsets (); 493 parentDim.width -= parentInsets.left + parentInsets.right; 494 parentDim.height -= parentInsets.top + parentInsets.bottom; 495 496 int current_y = 0; 497 int max_x = 0; 498 int max_y = 0; 499 500 // Guaranteed to contain the last component added to the given row 501 // or column, whose gridwidth/height is not REMAINDER. 502 HashMap<Integer,Component> lastInRow = new HashMap<Integer,Component>(); 503 HashMap<Integer,Component> lastInCol = new HashMap<Integer,Component>(); 504 505 Component[] components = parent.getComponents(); 506 507 // Components sorted by gridwidths/heights, 508 // smallest to largest, with REMAINDER and RELATIVE at the end. 509 // These are useful when determining sizes and weights. 510 ArrayList<Component> sortedByWidth = 511 new ArrayList<Component>(components.length); 512 ArrayList<Component> sortedByHeight = 513 new ArrayList<Component>(components.length); 514 515 // STEP 1: first we figure out how many rows/columns 516 for (int i = 0; i < components.length; i++) 517 { 518 Component component = components [i]; 519 // If component is not visible we dont have to care about it. 520 if (!component.isVisible()) 521 continue; 522 523 // When looking up the constraint for the first time, check the 524 // original unmodified constraint. After the first time, always 525 // refer to the internal modified constraint. 526 GridBagConstraints originalConstraints = lookupConstraints (component); 527 GridBagConstraints constraints = (GridBagConstraints) originalConstraints.clone(); 528 internalcomptable.put(component, constraints); 529 530 // Cases: 531 // 532 // 1. gridy == RELATIVE, gridx == RELATIVE 533 // 534 // use y as the row number; check for the next 535 // available slot at row y 536 // 537 // 2. only gridx == RELATIVE 538 // 539 // check for the next available slot at row gridy 540 // 541 // 3. only gridy == RELATIVE 542 // 543 // check for the next available slot at column gridx 544 // 545 // 4. neither gridx or gridy == RELATIVE 546 // 547 // nothing to check; just add it 548 549 // cases 1 and 2 550 if(constraints.gridx == GridBagConstraints.RELATIVE) 551 { 552 if (constraints.gridy == GridBagConstraints.RELATIVE) 553 constraints.gridy = current_y; 554 555 int x; 556 557 // Check the component that occupies the right-most spot in this 558 // row. We want to add this component after it. 559 // If this row is empty, add to the 0 position. 560 if (!lastInRow.containsKey(new Integer(constraints.gridy))) 561 x = 0; 562 else 563 { 564 Component lastComponent = lastInRow.get(new Integer(constraints.gridy)); 565 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 566 x = lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth); 567 } 568 569 // Determine if this component will fit in the slot vertically. 570 // If not, bump it over to where it does fit. 571 for (int y = constraints.gridy + 1; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) 572 { 573 if (lastInRow.containsKey(new Integer(y))) 574 { 575 Component lastComponent = lastInRow.get(new Integer(y)); 576 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 577 x = Math.max (x, 578 lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth)); 579 } 580 } 581 582 constraints.gridx = x; 583 } 584 // case 3 585 else if(constraints.gridy == GridBagConstraints.RELATIVE) 586 { 587 int y; 588 // Check the component that occupies the bottom-most spot in 589 // this column. We want to add this component below it. 590 // If this column is empty, add to the 0 position. 591 if (!lastInCol.containsKey(new Integer(constraints.gridx))) 592 { 593 y = current_y; 594 } 595 else 596 { 597 Component lastComponent = lastInCol.get(new Integer(constraints.gridx)); 598 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 599 y = lastConstraints.gridy + Math.max(1, lastConstraints.gridheight); 600 } 601 602 // Determine if this component will fit in the slot horizontally. 603 // If not, bump it down to where it does fit. 604 for (int x = constraints.gridx + 1; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) 605 { 606 if (lastInCol.containsKey(new Integer(x))) 607 { 608 Component lastComponent = lastInCol.get(new Integer(x)); 609 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 610 y = Math.max (y, 611 lastConstraints.gridy + Math.max(1, lastConstraints.gridheight)); 612 } 613 } 614 615 constraints.gridy = y; 616 } 617 // case 4: do nothing 618 619 max_x = Math.max(max_x, 620 constraints.gridx + Math.max(1, constraints.gridwidth)); 621 max_y = Math.max(max_y, 622 constraints.gridy + Math.max(1, constraints.gridheight)); 623 624 sortBySpan(component, constraints.gridwidth, sortedByWidth, true); 625 sortBySpan(component, constraints.gridheight, sortedByHeight, false); 626 627 // Update our reference points for RELATIVE gridx and gridy. 628 if(constraints.gridwidth == GridBagConstraints.REMAINDER) 629 { 630 current_y = constraints.gridy + Math.max(1, constraints.gridheight); 631 } 632 else if (constraints.gridwidth != GridBagConstraints.REMAINDER) 633 { 634 for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) 635 { 636 if(lastInRow.containsKey(new Integer(y))) 637 { 638 Component lastComponent = lastInRow.get(new Integer(y)); 639 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 640 if (constraints.gridx > lastConstraints.gridx) 641 { 642 lastInRow.put(new Integer(y), component); 643 } 644 } 645 else 646 { 647 lastInRow.put(new Integer(y), component); 648 } 649 } 650 651 for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) 652 { 653 if(lastInCol.containsKey(new Integer(x))) 654 { 655 Component lastComponent = lastInCol.get(new Integer(x)); 656 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 657 if (constraints.gridy > lastConstraints.gridy) 658 { 659 lastInCol.put(new Integer(x), component); 660 } 661 } 662 else 663 { 664 lastInCol.put(new Integer(x), component); 665 } 666 } 667 } 668 } // end of STEP 1 669 670 GridBagLayoutInfo info = new GridBagLayoutInfo(max_x, max_y); 671 672 // Check if column widths and row heights are overridden. 673 674 for (int x = 0; x < max_x; x++) 675 { 676 if(columnWidths != null && columnWidths.length > x) 677 info.colWidths[x] = columnWidths[x]; 678 if(columnWeights != null && columnWeights.length > x) 679 info.colWeights[x] = columnWeights[x]; 680 } 681 682 for (int y = 0; y < max_y; y++) 683 { 684 if(rowHeights != null && rowHeights.length > y) 685 info.rowHeights[y] = rowHeights[y]; 686 if(rowWeights != null && rowWeights.length > y) 687 info.rowWeights[y] = rowWeights[y]; 688 } 689 690 // STEP 2: Fix up any cells with width/height as REMAINDER/RELATIVE. 691 for (int i = 0; i < components.length; i++) 692 { 693 Component component = components [i]; 694 695 // If component is not visible we dont have to care about it. 696 if (!component.isVisible()) 697 continue; 698 699 GridBagConstraints constraints = lookupInternalConstraints (component); 700 701 if(constraints.gridwidth == GridBagConstraints.REMAINDER || constraints.gridwidth == GridBagConstraints.RELATIVE) 702 { 703 if(constraints.gridwidth == GridBagConstraints.REMAINDER) 704 { 705 for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) 706 { 707 if (lastInRow.containsKey(new Integer(y))) 708 { 709 Component lastComponent = lastInRow.get(new Integer(y)); 710 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 711 712 if (lastConstraints.gridwidth == GridBagConstraints.RELATIVE) 713 { 714 constraints.gridx = max_x - 1; 715 break; 716 } 717 else 718 { 719 constraints.gridx = Math.max (constraints.gridx, 720 lastConstraints.gridx + Math.max (1, lastConstraints.gridwidth)); 721 } 722 } 723 } 724 constraints.gridwidth = max_x - constraints.gridx; 725 } 726 else if (constraints.gridwidth == GridBagConstraints.RELATIVE) 727 { 728 constraints.gridwidth = max_x - constraints.gridx - 1; 729 } 730 731 // Re-sort 732 sortedByWidth.remove(sortedByWidth.indexOf(component)); 733 sortBySpan(component, constraints.gridwidth, sortedByWidth, true); 734 } 735 736 if(constraints.gridheight == GridBagConstraints.REMAINDER || constraints.gridheight == GridBagConstraints.RELATIVE) 737 { 738 if(constraints.gridheight == GridBagConstraints.REMAINDER) 739 { 740 for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) 741 { 742 if (lastInCol.containsKey(new Integer(x))) 743 { 744 Component lastComponent = lastInRow.get(new Integer(x)); 745 if (lastComponent != null) 746 { 747 GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); 748 749 if (lastConstraints.gridheight == GridBagConstraints.RELATIVE) 750 { 751 constraints.gridy = max_y - 1; 752 break; 753 } 754 else 755 { 756 constraints.gridy = Math.max (constraints.gridy, 757 lastConstraints.gridy + Math.max (1, lastConstraints.gridheight)); 758 } 759 } 760 } 761 } 762 constraints.gridheight = max_y - constraints.gridy; 763 } 764 else if (constraints.gridheight == GridBagConstraints.RELATIVE) 765 { 766 constraints.gridheight = max_y - constraints.gridy - 1; 767 } 768 769 // Re-sort 770 sortedByHeight.remove(sortedByHeight.indexOf(component)); 771 sortBySpan(component, constraints.gridheight, sortedByHeight, false); 772 } 773 } // end of STEP 2 774 775 // STEP 3: Determine sizes and weights for columns. 776 for (int i = 0; i < sortedByWidth.size(); i++) 777 { 778 Component component = sortedByWidth.get(i); 779 780 // If component is not visible we dont have to care about it. 781 if (!component.isVisible()) 782 continue; 783 784 GridBagConstraints constraints = lookupInternalConstraints (component); 785 786 int width = (sizeflag == PREFERREDSIZE) ? 787 component.getPreferredSize().width : 788 component.getMinimumSize().width; 789 790 if(constraints.insets != null) 791 width += constraints.insets.left + constraints.insets.right; 792 793 width += constraints.ipadx; 794 795 distributeSizeAndWeight(width, 796 constraints.weightx, 797 constraints.gridx, 798 constraints.gridwidth, 799 info.colWidths, 800 info.colWeights); 801 } // end of STEP 3 802 803 // STEP 4: Determine sizes and weights for rows. 804 for (int i = 0; i < sortedByHeight.size(); i++) 805 { 806 Component component = sortedByHeight.get(i); 807 808 // If component is not visible we dont have to care about it. 809 if (!component.isVisible()) 810 continue; 811 812 GridBagConstraints constraints = lookupInternalConstraints (component); 813 814 int height = (sizeflag == PREFERREDSIZE) ? 815 component.getPreferredSize().height : 816 component.getMinimumSize().height; 817 818 if(constraints.insets != null) 819 height += constraints.insets.top + constraints.insets.bottom; 820 821 height += constraints.ipady; 822 823 distributeSizeAndWeight(height, 824 constraints.weighty, 825 constraints.gridy, 826 constraints.gridheight, 827 info.rowHeights, 828 info.rowWeights); 829 } // end of STEP 4 830 831 // Adjust cell sizes iff parent size not zero. 832 if (parentDim.width > 0 && parentDim.height > 0) 833 { 834 calcCellSizes (info.colWidths, info.colWeights, parentDim.width); 835 calcCellSizes (info.rowHeights, info.rowWeights, parentDim.height); 836 } 837 838 int totalWidth = sumIntArray(info.colWidths); 839 int totalHeight = sumIntArray(info.rowHeights); 840 841 // Make sure pos_x and pos_y are never negative. 842 if (totalWidth >= parentDim.width) 843 info.pos_x = parentInsets.left; 844 else 845 info.pos_x = parentInsets.left + (parentDim.width - totalWidth) / 2; 846 847 if (totalHeight >= parentDim.height) 848 info.pos_y = parentInsets.top; 849 else 850 info.pos_y = parentInsets.top + (parentDim.height - totalHeight) / 2; 851 852 // DEBUG 853 //dumpLayoutInfo (info); 854 855 return info; 856 } 857 858 /** 859 * Obsolete. 860 */ GetMinSize(Container parent, GridBagLayoutInfo info)861 protected Dimension GetMinSize (Container parent, GridBagLayoutInfo info) 862 { 863 if (parent == null || info == null) 864 return new Dimension (0, 0); 865 866 Insets insets = parent.getInsets(); 867 int width = sumIntArray (info.colWidths) + insets.left + insets.right; 868 int height = sumIntArray (info.rowHeights) + insets.top + insets.bottom; 869 return new Dimension (width, height); 870 } 871 872 /** 873 * @since 1.4 874 */ getMinSize(Container parent, GridBagLayoutInfo info)875 protected Dimension getMinSize (Container parent, GridBagLayoutInfo info) 876 { 877 return GetMinSize (parent, info); 878 } 879 880 /** 881 * Helper method used by GetLayoutInfo to keep components sorted, either 882 * by gridwidth or gridheight. 883 * 884 * @param component Component to add to the sorted list. 885 * @param span Either the component's gridwidth or gridheight. 886 * @param list <code>ArrayList</code> of components, sorted by 887 * their span. 888 * @param sortByWidth Flag indicating sorting index. If true, sort by 889 * width. Otherwise, sort by height. 890 * FIXME: Use a better sorting algorithm. 891 */ sortBySpan(Component component, int span, ArrayList<Component> list, boolean sortByWidth)892 private void sortBySpan (Component component, int span, 893 ArrayList<Component> list, boolean sortByWidth) 894 { 895 if (span == GridBagConstraints.REMAINDER 896 || span == GridBagConstraints.RELATIVE) 897 { 898 // Put all RELATIVE and REMAINDER components at the end. 899 list.add(component); 900 } 901 else 902 { 903 int i = 0; 904 if (list.size() > 0) 905 { 906 GridBagConstraints gbc = lookupInternalConstraints(list.get(i)); 907 int otherspan = sortByWidth ? 908 gbc.gridwidth : 909 gbc.gridheight; 910 while (otherspan != GridBagConstraints.REMAINDER 911 && otherspan != GridBagConstraints.RELATIVE 912 && span >= otherspan) 913 { 914 i++; 915 if (i < list.size()) 916 { 917 gbc = lookupInternalConstraints(list.get(i)); 918 otherspan = sortByWidth ? 919 gbc.gridwidth : 920 gbc.gridheight; 921 } 922 else 923 break; 924 } 925 } 926 list.add(i, component); 927 } 928 } 929 930 /** 931 * Helper method used by GetLayoutInfo to distribute a component's size 932 * and weight. 933 * 934 * @param size Preferred size of component, with inset and padding 935 * already added. 936 * @param weight Weight of component. 937 * @param start Starting position of component. Either 938 * constraints.gridx or gridy. 939 * @param span Span of component. either contraints.gridwidth or 940 * gridheight. 941 * @param sizes Sizes of rows or columns. 942 * @param weights Weights of rows or columns. 943 */ distributeSizeAndWeight(int size, double weight, int start, int span, int[] sizes, double[] weights)944 private void distributeSizeAndWeight (int size, double weight, 945 int start, int span, 946 int[] sizes, double[] weights) 947 { 948 if (span == 1) 949 { 950 sizes[start] = Math.max(sizes[start], size); 951 weights[start] = Math.max(weights[start], weight); 952 } 953 else 954 { 955 int numOccupied = span; 956 int lastOccupied = -1; 957 958 for(int i = start; i < start + span; i++) 959 { 960 if (sizes[i] == 0.0) 961 numOccupied--; 962 else 963 { 964 size -= sizes[i]; 965 lastOccupied = i; 966 } 967 } 968 969 // A component needs to occupy at least one row. 970 if(numOccupied == 0) 971 sizes[start + span - 1] = size; 972 else if (size > 0) 973 sizes[lastOccupied] += size; 974 975 calcCellWeights(weight, weights, start, span); 976 } 977 } 978 979 /** 980 * Helper method used by GetLayoutInfo to calculate weight distribution. 981 * @param weight Weight of component. 982 * @param weights Weights of rows/columns. 983 * @param start Starting position of component in grid (gridx/gridy). 984 * @param span Span of component (gridwidth/gridheight). 985 */ calcCellWeights(double weight, double[] weights, int start, int span)986 private void calcCellWeights (double weight, double[] weights, int start, int span) 987 { 988 double totalWeight = 0.0; 989 for(int k = start; k < start + span; k++) 990 totalWeight += weights[k]; 991 992 if(weight > totalWeight) 993 { 994 if (totalWeight == 0.0) 995 { 996 weights[start + span - 1] += weight; 997 } 998 else 999 { 1000 double diff = weight - totalWeight ; 1001 double remaining = diff; 1002 1003 for(int k = start; k < start + span; k++) 1004 { 1005 double extraWeight = diff * weights[k] / totalWeight; 1006 weights[k] += extraWeight; 1007 remaining -= extraWeight; 1008 } 1009 1010 if (remaining > 0.0 && weights[start + span - 1] != 0.0) 1011 { 1012 weights[start + span - 1] += remaining; 1013 } 1014 } 1015 } 1016 } 1017 1018 /** 1019 * Helper method used by GetLayoutInfo to distribute extra space 1020 * based on weight distribution. 1021 * 1022 * @param sizes Sizes of rows/columns. 1023 * @param weights Weights of rows/columns. 1024 * @param range Dimension of container. 1025 */ calcCellSizes(int[] sizes, double[] weights, int range)1026 private void calcCellSizes (int[] sizes, double[] weights, int range) 1027 { 1028 int totalSize = sumIntArray (sizes); 1029 double totalWeight = sumDoubleArray (weights); 1030 1031 int diff = range - totalSize; 1032 1033 if (diff == 0) 1034 return; 1035 1036 for (int i = 0; i < sizes.length; i++) 1037 { 1038 int newsize = (int) (sizes[i] + (((double) diff) * weights [i] / totalWeight )); 1039 1040 if (newsize > 0) 1041 sizes[i] = newsize; 1042 } 1043 } 1044 dumpLayoutInfo(GridBagLayoutInfo info)1045 private void dumpLayoutInfo (GridBagLayoutInfo info) 1046 { 1047 System.out.println ("GridBagLayoutInfo:"); 1048 System.out.println ("cols: " + info.cols + ", rows: " + info.rows); 1049 System.out.print ("colWidths: "); 1050 dumpArray(info.colWidths); 1051 System.out.print ("rowHeights: "); 1052 dumpArray(info.rowHeights); 1053 System.out.print ("colWeights: "); 1054 dumpArray(info.colWeights); 1055 System.out.print ("rowWeights: "); 1056 dumpArray(info.rowWeights); 1057 } 1058 dumpArray(int[] array)1059 private void dumpArray(int[] array) 1060 { 1061 String sep = ""; 1062 for(int i = 0; i < array.length; i++) 1063 { 1064 System.out.print(sep); 1065 System.out.print(array[i]); 1066 sep = ", "; 1067 } 1068 System.out.println(); 1069 } 1070 dumpArray(double[] array)1071 private void dumpArray(double[] array) 1072 { 1073 String sep = ""; 1074 for(int i = 0; i < array.length; i++) 1075 { 1076 System.out.print(sep); 1077 System.out.print(array[i]); 1078 sep = ", "; 1079 } 1080 System.out.println(); 1081 } 1082 1083 /** 1084 * @since 1.4 1085 */ arrangeGrid(Container parent)1086 protected void arrangeGrid (Container parent) 1087 { 1088 ArrangeGrid (parent); 1089 } 1090 1091 /** 1092 * @since 1.4 1093 */ getLayoutInfo(Container parent, int sizeflag)1094 protected GridBagLayoutInfo getLayoutInfo (Container parent, int sizeflag) 1095 { 1096 return GetLayoutInfo (parent, sizeflag); 1097 } 1098 1099 /** 1100 * Move and resize a rectangle according to a set of grid bag 1101 * constraints. The x, y, width and height fields of the 1102 * rectangle argument are adjusted to the new values. 1103 * 1104 * @param constraints position and size constraints 1105 * @param r rectangle to be moved and resized 1106 * 1107 * @since 1.4 1108 */ adjustForGravity(GridBagConstraints constraints, Rectangle r)1109 protected void adjustForGravity (GridBagConstraints constraints, 1110 Rectangle r) 1111 { 1112 AdjustForGravity (constraints, r); 1113 } 1114 } 1115