Source for java.awt.GridBagLayout

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