GridBagLayout.java 37.5 KB
Newer Older
Tom Tromey committed
1
/* GridBagLayout - Layout manager for components according to GridBagConstraints
2
   Copyright (C) 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
Tom Tromey committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package java.awt;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;

/**
 * @author Michael Koch (konqueror@gmx.de)
 * @author Jeroen Frijters (jeroen@frijters.net)
49
 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
Tom Tromey committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
 */
public class GridBagLayout
    implements Serializable, LayoutManager2
{
    private static final long serialVersionUID = 8838754796412211005L;

    protected static final int MINSIZE = 1;
    protected static final int PREFERREDSIZE = 2;
    protected static final int MAXGRIDSIZE = 512;

    // comptable remembers the original contraints given to us.
    // internalcomptable is used to keep track of modified constraint values
    // that we calculate, particularly when we are given RELATIVE and
    // REMAINDER constraints.
    // Constraints kept in comptable are never modified, and constraints
    // kept in internalcomptable can be modified internally only.
66 67
    protected Hashtable<Component,GridBagConstraints> comptable;
    private Hashtable<Component,GridBagConstraints> internalcomptable;
Tom Tromey committed
68 69 70 71 72 73 74 75 76 77
    protected GridBagLayoutInfo layoutInfo;
    protected GridBagConstraints defaultConstraints;

    public double[] columnWeights;
    public int[] columnWidths;
    public double[] rowWeights;
    public int[] rowHeights;

    public GridBagLayout ()
    {
78 79 80
        this.comptable = new Hashtable<Component,GridBagConstraints>();
        this.internalcomptable = new Hashtable<Component,GridBagConstraints>();
        this.defaultConstraints= new GridBagConstraints();
Tom Tromey committed
81 82 83 84 85 86 87
    }

    /**
     * Helper method to calc the sum of a range of elements in an int array.
     */
    private int sumIntArray (int[] array, int upto)
    {
88
        int result = 0;
Tom Tromey committed
89

90 91
        for (int i = 0; i < upto; i++)
            result += array [i];
Tom Tromey committed
92

93
        return result;
Tom Tromey committed
94 95 96 97 98 99 100
    }

    /**
     * Helper method to calc the sum of all elements in an int array.
     */
    private int sumIntArray (int[] array)
    {
101
        return sumIntArray(array, array.length);
Tom Tromey committed
102 103 104 105 106 107 108
    }

    /**
     * Helper method to calc the sum of all elements in an double array.
     */
    private double sumDoubleArray (double[] array)
    {
109
        double result = 0;
Tom Tromey committed
110

111 112
        for (int i = 0; i < array.length; i++)
            result += array [i];
Tom Tromey committed
113

114
        return result;
Tom Tromey committed
115 116 117 118
    }

    public void addLayoutComponent (String name, Component component)
    {
119
        // do nothing here.
Tom Tromey committed
120 121 122 123
    }

    public void removeLayoutComponent (Component component)
    {
124
        // do nothing here
Tom Tromey committed
125 126 127 128
    }

    public void addLayoutComponent (Component component, Object constraints)
    {
129 130
        if (constraints == null)
            return;
Tom Tromey committed
131

132 133 134 135
        if (!(constraints instanceof GridBagConstraints))
            throw new IllegalArgumentException("constraints "
                                               + constraints
                                               + " are not an instance of GridBagConstraints");
Tom Tromey committed
136

137
        setConstraints (component, (GridBagConstraints) constraints);
Tom Tromey committed
138 139 140 141
    }

    public Dimension preferredLayoutSize (Container parent)
    {
142 143 144 145 146
        if (parent == null)
            return new Dimension (0, 0);

        GridBagLayoutInfo li = getLayoutInfo (parent, PREFERREDSIZE);
        return getMinSize (parent, li);
Tom Tromey committed
147 148 149 150
    }

    public Dimension minimumLayoutSize (Container parent)
    {
151 152 153 154 155
        if (parent == null)
            return new Dimension (0, 0);

        GridBagLayoutInfo li = getLayoutInfo (parent, MINSIZE);
        return getMinSize (parent, li);
Tom Tromey committed
156 157 158 159
    }

    public Dimension maximumLayoutSize (Container target)
    {
160
        return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE);
Tom Tromey committed
161 162 163 164 165 166 167 168 169
    }

    public void layoutContainer (Container parent)
    {
      arrangeGrid (parent);
    }

    public float getLayoutAlignmentX (Container target)
    {
170
        return Component.CENTER_ALIGNMENT;
Tom Tromey committed
171 172 173 174
    }

    public float getLayoutAlignmentY (Container target)
    {
175
        return Component.CENTER_ALIGNMENT;
Tom Tromey committed
176 177 178 179
    }

    public void invalidateLayout (Container target)
    {
180
        this.layoutInfo = null;
Tom Tromey committed
181 182 183
    }

    public void setConstraints (Component component,
184
        GridBagConstraints constraints)
Tom Tromey committed
185
    {
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
        GridBagConstraints clone = (GridBagConstraints) constraints.clone();

        if (clone.gridx < 0)
            clone.gridx = GridBagConstraints.RELATIVE;

        if (clone.gridy < 0)
            clone.gridy = GridBagConstraints.RELATIVE;

        if (clone.gridwidth == 0)
            clone.gridwidth = GridBagConstraints.REMAINDER;
        else if (clone.gridwidth < 0)
            clone.gridwidth = 1;

        if (clone.gridheight == 0)
            clone.gridheight = GridBagConstraints.REMAINDER;
        else if (clone.gridheight < 0)
            clone.gridheight = 1;

        comptable.put (component, clone);
Tom Tromey committed
205 206 207 208
    }

    public GridBagConstraints getConstraints (Component component)
    {
209
        return (GridBagConstraints) (lookupConstraints (component).clone());
Tom Tromey committed
210 211 212 213
    }

    protected GridBagConstraints lookupConstraints (Component component)
    {
214 215 216 217 218 219 220 221 222
        GridBagConstraints result = comptable.get (component);

        if (result == null)
        {
            setConstraints (component, defaultConstraints);
            result = comptable.get (component);
        }

        return result;
Tom Tromey committed
223 224 225 226
    }

    private GridBagConstraints lookupInternalConstraints (Component component)
    {
227 228 229 230 231 232 233 234 235
        GridBagConstraints result = internalcomptable.get (component);

        if (result == null)
        {
            result = (GridBagConstraints) lookupConstraints(component).clone();
            internalcomptable.put (component, result);
        }

        return result;
Tom Tromey committed
236 237 238 239 240 241 242
    }

    /**
     * @since 1.1
     */
    public Point getLayoutOrigin ()
    {
243 244 245 246
        if (layoutInfo == null)
            return new Point (0, 0);

        return new Point (layoutInfo.pos_x, layoutInfo.pos_y);
Tom Tromey committed
247 248 249 250 251 252 253
    }

    /**
     * @since 1.1
     */
    public int[][] getLayoutDimensions ()
    {
254 255 256 257 258 259 260 261 262 263 264 265 266 267
        int[][] result = new int [2][];
        if (layoutInfo == null)
          {
            result[0] = new int[0];
            result[1] = new int[0];

            return result;
          }

        result [0] = new int [layoutInfo.cols];
        System.arraycopy (layoutInfo.colWidths, 0, result [0], 0, layoutInfo.cols);
        result [1] = new int [layoutInfo.rows];
        System.arraycopy (layoutInfo.rowHeights, 0, result [1], 0, layoutInfo.rows);
        return result;
Tom Tromey committed
268 269 270 271
    }

    public double[][] getLayoutWeights ()
    {
272 273 274 275 276 277 278 279 280 281 282 283 284 285
        double[][] result = new double [2][];
        if (layoutInfo == null)
          {
            result[0] = new double[0];
            result[1] = new double[0];

            return result;
          }

        result [0] = new double [layoutInfo.cols];
        System.arraycopy (layoutInfo.colWeights, 0, result [0], 0, layoutInfo.cols);
        result [1] = new double [layoutInfo.rows];
        System.arraycopy (layoutInfo.rowWeights, 0, result [1], 0, layoutInfo.rows);
        return result;
Tom Tromey committed
286 287 288 289 290 291 292
    }

    /**
     * @since 1.1
     */
    public Point location (int x, int y)
    {
293 294
        if (layoutInfo == null)
            return new Point (0, 0);
Tom Tromey committed
295

296 297 298 299
        int col;
        int row;
        int pixel_x = layoutInfo.pos_x;
        int pixel_y = layoutInfo.pos_y;
Tom Tromey committed
300

301 302 303 304 305
        for (col = 0; col < layoutInfo.cols; col++)
        {
            int w = layoutInfo.colWidths [col];
            if (x < pixel_x + w)
                break;
Tom Tromey committed
306

307 308
            pixel_x += w;
        }
Tom Tromey committed
309

310 311 312 313 314
        for (row = 0; row < layoutInfo.rows; row++)
        {
            int h = layoutInfo.rowHeights [row];
            if (y < pixel_y + h)
                break;
Tom Tromey committed
315

316 317
            pixel_y += h;
        }
Tom Tromey committed
318

319
        return new Point (col, row);
Tom Tromey committed
320 321 322
    }

    /**
323 324 325 326 327 328 329 330
     * Return a string representation of this GridBagLayout.
     *
     * @return a string representation
     */
    public String toString()
    {
      return getClass().getName();
    }
331

332
    /**
333 334 335 336 337 338
     * Move and resize a rectangle according to a set of grid bag
     * constraints.  The x, y, width and height fields of the
     * rectangle argument are adjusted to the new values.
     *
     * @param constraints position and size constraints
     * @param r rectangle to be moved and resized
Tom Tromey committed
339
     */
340 341
    protected void AdjustForGravity (GridBagConstraints constraints,
                                     Rectangle r)
Tom Tromey committed
342
    {
343 344
      Insets insets = constraints.insets;
      if (insets != null)
345 346 347 348 349 350
        {
          r.x += insets.left;
          r.y += insets.top;
          r.width -= insets.left + insets.right;
          r.height -= insets.top + insets.bottom;
        }
Tom Tromey committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    }

    /**
     * Obsolete.
     */
    protected void ArrangeGrid (Container parent)
    {
      Component[] components = parent.getComponents();

      if (components.length == 0)
        return;

      GridBagLayoutInfo info = getLayoutInfo (parent, PREFERREDSIZE);
      if (info.cols == 0 && info.rows == 0)
        return;

      // DEBUG
368 369 370 371 372 373
      //dumpLayoutInfo (info);

      // Calling setBounds on these components causes this layout to
      // be invalidated, clearing the layout information cache,
      // layoutInfo.  So we wait until after this for loop to set
      // layoutInfo.
374
      Component lastComp = null;
375 376 377

      Rectangle cell = new Rectangle();

378 379 380 381 382 383 384 385 386 387
      for (int i = 0; i < components.length; i++)
      {
        Component component = components[i];

        // If component is not visible we dont have to care about it.
        if (! component.isVisible())
          continue;

        Dimension dim = component.getPreferredSize();
        GridBagConstraints constraints = lookupInternalConstraints(component);
388

389 390
        if (lastComp != null
            && constraints.gridheight == GridBagConstraints.REMAINDER)
391
          cell.y += cell.height;
392
        else
393
          cell.y = sumIntArray(info.rowHeights, constraints.gridy);
394

395 396
        if (lastComp != null
            && constraints.gridwidth == GridBagConstraints.REMAINDER)
397
          cell.x += cell.width;
398
        else
399
          cell.x = sumIntArray(info.colWidths, constraints.gridx);
400

401 402 403 404
        cell.width = sumIntArray(info.colWidths, constraints.gridx
                                            + constraints.gridwidth) - cell.x;
        cell.height = sumIntArray(info.rowHeights, constraints.gridy
                                             + constraints.gridheight) - cell.y;
405

406
        // Adjust for insets.
407
        AdjustForGravity( constraints, cell );
408 409 410 411 412 413 414 415 416 417

        // Note: Documentation says that padding is added on both sides, but
        // visual inspection shows that the Sun implementation only adds it
        // once, so we do the same.
        dim.width += constraints.ipadx;
        dim.height += constraints.ipady;

        switch (constraints.fill)
          {
          case GridBagConstraints.HORIZONTAL:
418
            dim.width = cell.width;
419 420
            break;
          case GridBagConstraints.VERTICAL:
421
            dim.height = cell.height;
422 423
            break;
          case GridBagConstraints.BOTH:
424 425
            dim.width = cell.width;
            dim.height = cell.height;
426 427 428 429 430 431 432 433 434
            break;
          }

        int x = 0;
        int y = 0;

        switch (constraints.anchor)
          {
          case GridBagConstraints.NORTH:
435 436
            x = cell.x + (cell.width - dim.width) / 2;
            y = cell.y;
437 438
            break;
          case GridBagConstraints.SOUTH:
439 440
            x = cell.x + (cell.width - dim.width) / 2;
            y = cell.y + cell.height - dim.height;
441 442
            break;
          case GridBagConstraints.WEST:
443 444
            x = cell.x;
            y = cell.y + (cell.height - dim.height) / 2;
445 446
            break;
          case GridBagConstraints.EAST:
447 448
            x = cell.x + cell.width - dim.width;
            y = cell.y + (cell.height - dim.height) / 2;
449 450
            break;
          case GridBagConstraints.NORTHEAST:
451 452
            x = cell.x + cell.width - dim.width;
            y = cell.y;
453 454
            break;
          case GridBagConstraints.NORTHWEST:
455 456
            x = cell.x;
            y = cell.y;
457 458
            break;
          case GridBagConstraints.SOUTHEAST:
459 460
            x = cell.x + cell.width - dim.width;
            y = cell.y + cell.height - dim.height;
461 462
            break;
          case GridBagConstraints.SOUTHWEST:
463 464
            x = cell.x;
            y = cell.y + cell.height - dim.height;
465 466
            break;
          default:
467 468
            x = cell.x + (cell.width - dim.width) / 2;
            y = cell.y + (cell.height - dim.height) / 2;
469 470 471 472 473 474 475 476 477 478 479 480 481
            break;
          }
        component.setBounds(info.pos_x + x, info.pos_y + y, dim.width,
                            dim.height);
        lastComp = component;
      }

    // DEBUG
    //dumpLayoutInfo(info);

    // Cache layout information.
    layoutInfo = getLayoutInfo(parent, PREFERREDSIZE);
  }
Tom Tromey committed
482 483 484 485 486 487 488 489 490 491 492 493 494

    /**
     * Obsolete.
     */
    protected GridBagLayoutInfo GetLayoutInfo (Container parent, int sizeflag)
    {
      if (sizeflag != MINSIZE && sizeflag != PREFERREDSIZE)
        throw new IllegalArgumentException();

      Dimension parentDim = parent.getSize ();
      Insets parentInsets = parent.getInsets ();
      parentDim.width -= parentInsets.left + parentInsets.right;
      parentDim.height -= parentInsets.top + parentInsets.bottom;
495

Tom Tromey committed
496 497 498 499 500 501
      int current_y = 0;
      int max_x = 0;
      int max_y = 0;

      // Guaranteed to contain the last component added to the given row
      // or column, whose gridwidth/height is not REMAINDER.
502 503
      HashMap<Integer,Component> lastInRow = new HashMap<Integer,Component>();
      HashMap<Integer,Component> lastInCol = new HashMap<Integer,Component>();
Tom Tromey committed
504 505 506 507 508 509

      Component[] components = parent.getComponents();

      // Components sorted by gridwidths/heights,
      // smallest to largest, with REMAINDER and RELATIVE at the end.
      // 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);
Tom Tromey committed
514 515 516

      // STEP 1: first we figure out how many rows/columns
      for (int i = 0; i < components.length; i++)
517
        {
Tom Tromey committed
518 519 520 521
          Component component = components [i];
          // If component is not visible we dont have to care about it.
          if (!component.isVisible())
            continue;
522

Tom Tromey committed
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
          // When looking up the constraint for the first time, check the
          // original unmodified constraint.  After the first time, always
          // refer to the internal modified constraint.
          GridBagConstraints originalConstraints = lookupConstraints (component);
          GridBagConstraints constraints = (GridBagConstraints) originalConstraints.clone();
          internalcomptable.put(component, constraints);

          // Cases:
          //
          // 1. gridy == RELATIVE, gridx == RELATIVE
          //
          //       use y as the row number; check for the next
          //       available slot at row y
          //
          // 2. only gridx == RELATIVE
          //
          //       check for the next available slot at row gridy
          //
          // 3. only gridy == RELATIVE
          //
          //       check for the next available slot at column gridx
          //
          // 4. neither gridx or gridy == RELATIVE
          //
          //       nothing to check; just add it

          // cases 1 and 2
          if(constraints.gridx == GridBagConstraints.RELATIVE)
            {
              if (constraints.gridy == GridBagConstraints.RELATIVE)
              constraints.gridy = current_y;

              int x;

              // Check the component that occupies the right-most spot in this
              // row. We want to add this component after it.
              // If this row is empty, add to the 0 position.
560
              if (!lastInRow.containsKey(new Integer(constraints.gridy)))
Tom Tromey committed
561 562 563
                x = 0;
              else
                {
564
                  Component lastComponent = lastInRow.get(new Integer(constraints.gridy));
Tom Tromey committed
565 566 567 568 569 570 571 572 573 574
                  GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
                  x = lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth);
                }

              // Determine if this component will fit in the slot vertically.
              // If not, bump it over to where it does fit.
              for (int y = constraints.gridy + 1; y < constraints.gridy + Math.max(1, constraints.gridheight); y++)
                {
                  if (lastInRow.containsKey(new Integer(y)))
                    {
575
                      Component lastComponent = lastInRow.get(new Integer(y));
Tom Tromey committed
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
                      GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
                      x = Math.max (x,
                                    lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth));
                    }
                }

              constraints.gridx = x;
            }
          // case 3
          else if(constraints.gridy == GridBagConstraints.RELATIVE)
            {
              int y;
              // Check the component that occupies the bottom-most spot in
              // this column. We want to add this component below it.
              // If this column is empty, add to the 0 position.
591
              if (!lastInCol.containsKey(new Integer(constraints.gridx)))
592 593 594
                {
                  y = current_y;
                }
Tom Tromey committed
595 596
              else
                {
597
                  Component lastComponent = lastInCol.get(new Integer(constraints.gridx));
Tom Tromey committed
598 599 600 601 602 603 604 605 606 607
                  GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
                  y = lastConstraints.gridy + Math.max(1, lastConstraints.gridheight);
                }

              // Determine if this component will fit in the slot horizontally.
              // If not, bump it down to where it does fit.
              for (int x = constraints.gridx + 1; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++)
                {
                  if (lastInCol.containsKey(new Integer(x)))
                    {
608
                      Component lastComponent = lastInCol.get(new Integer(x));
Tom Tromey committed
609 610 611 612 613 614 615 616 617 618
                      GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
                      y = Math.max (y,
                                    lastConstraints.gridy + Math.max(1, lastConstraints.gridheight));
                    }
                }

              constraints.gridy = y;
            }
          // case 4: do nothing

619
          max_x = Math.max(max_x,
Tom Tromey committed
620 621 622 623 624 625 626 627 628
                           constraints.gridx + Math.max(1, constraints.gridwidth));
          max_y = Math.max(max_y,
                           constraints.gridy + Math.max(1, constraints.gridheight));

          sortBySpan(component, constraints.gridwidth, sortedByWidth, true);
          sortBySpan(component, constraints.gridheight, sortedByHeight, false);

          // Update our reference points for RELATIVE gridx and gridy.
          if(constraints.gridwidth == GridBagConstraints.REMAINDER)
629
            {
630
          current_y = constraints.gridy + Math.max(1, constraints.gridheight);
631
            }
Tom Tromey committed
632
          else if (constraints.gridwidth != GridBagConstraints.REMAINDER)
633
            {
Tom Tromey committed
634 635 636 637
              for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++)
                {
                  if(lastInRow.containsKey(new Integer(y)))
                    {
638
                      Component lastComponent = lastInRow.get(new Integer(y));
Tom Tromey committed
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
                      GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
                      if (constraints.gridx > lastConstraints.gridx)
                        {
                          lastInRow.put(new Integer(y), component);
                        }
                    }
                  else
                    {
                      lastInRow.put(new Integer(y), component);
                    }
                }

              for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++)
                {
                  if(lastInCol.containsKey(new Integer(x)))
                    {
655
                      Component lastComponent = lastInCol.get(new Integer(x));
Tom Tromey committed
656 657 658 659 660 661 662 663 664 665 666
                      GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
                      if (constraints.gridy > lastConstraints.gridy)
                        {
                          lastInCol.put(new Integer(x), component);
                        }
                    }
                  else
                    {
                      lastInCol.put(new Integer(x), component);
                    }
                }
667 668 669
            }
        } // end of STEP 1

Tom Tromey committed
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
      GridBagLayoutInfo info = new GridBagLayoutInfo(max_x, max_y);

      // Check if column widths and row heights are overridden.

      for (int x = 0; x < max_x; x++)
        {
          if(columnWidths != null && columnWidths.length > x)
            info.colWidths[x] = columnWidths[x];
          if(columnWeights != null && columnWeights.length > x)
            info.colWeights[x] = columnWeights[x];
        }

      for (int y = 0; y < max_y; y++)
        {
          if(rowHeights != null && rowHeights.length > y)
            info.rowHeights[y] = rowHeights[y];
          if(rowWeights != null && rowWeights.length > y)
            info.rowWeights[y] = rowWeights[y];
        }

      // STEP 2: Fix up any cells with width/height as REMAINDER/RELATIVE.
      for (int i = 0; i < components.length; i++)
        {
          Component component = components [i];
694

Tom Tromey committed
695 696 697
          // If component is not visible we dont have to care about it.
          if (!component.isVisible())
            continue;
698

Tom Tromey committed
699 700 701 702 703 704 705 706 707 708
          GridBagConstraints constraints = lookupInternalConstraints (component);

          if(constraints.gridwidth == GridBagConstraints.REMAINDER || constraints.gridwidth == GridBagConstraints.RELATIVE)
            {
              if(constraints.gridwidth == GridBagConstraints.REMAINDER)
                {
                  for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++)
                    {
                      if (lastInRow.containsKey(new Integer(y)))
                        {
709
                          Component lastComponent = lastInRow.get(new Integer(y));
Tom Tromey committed
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
                          GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);

                          if (lastConstraints.gridwidth == GridBagConstraints.RELATIVE)
                            {
                              constraints.gridx = max_x - 1;
                              break;
                            }
                          else
                            {
                              constraints.gridx = Math.max (constraints.gridx,
                                                            lastConstraints.gridx + Math.max (1, lastConstraints.gridwidth));
                            }
                        }
                    }
                  constraints.gridwidth = max_x - constraints.gridx;
                }
              else if (constraints.gridwidth == GridBagConstraints.RELATIVE)
                {
                  constraints.gridwidth = max_x - constraints.gridx - 1;
                }

              // Re-sort
              sortedByWidth.remove(sortedByWidth.indexOf(component));
              sortBySpan(component, constraints.gridwidth, sortedByWidth, true);
            }

          if(constraints.gridheight == GridBagConstraints.REMAINDER || constraints.gridheight == GridBagConstraints.RELATIVE)
            {
              if(constraints.gridheight == GridBagConstraints.REMAINDER)
                {
                  for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++)
                    {
                      if (lastInCol.containsKey(new Integer(x)))
                        {
744
                          Component lastComponent = lastInRow.get(new Integer(x));
745
                          if (lastComponent != null)
Tom Tromey committed
746
                            {
747
                              GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
748

749 750 751 752 753 754 755 756 757 758
                              if (lastConstraints.gridheight == GridBagConstraints.RELATIVE)
                                {
                                  constraints.gridy = max_y - 1;
                                  break;
                                }
                              else
                                {
                                  constraints.gridy = Math.max (constraints.gridy,
                                                                lastConstraints.gridy + Math.max (1, lastConstraints.gridheight));
                                }
Tom Tromey committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
                            }
                        }
                    }
                  constraints.gridheight = max_y - constraints.gridy;
                }
              else if (constraints.gridheight == GridBagConstraints.RELATIVE)
                {
                  constraints.gridheight = max_y - constraints.gridy - 1;
                }

              // Re-sort
              sortedByHeight.remove(sortedByHeight.indexOf(component));
              sortBySpan(component, constraints.gridheight, sortedByHeight, false);
            }
        } // end of STEP 2

      // STEP 3: Determine sizes and weights for columns.
      for (int i = 0; i < sortedByWidth.size(); i++)
        {
778
          Component component = sortedByWidth.get(i);
779

Tom Tromey committed
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
          // If component is not visible we dont have to care about it.
          if (!component.isVisible())
            continue;

          GridBagConstraints constraints = lookupInternalConstraints (component);

          int width = (sizeflag == PREFERREDSIZE) ?
                      component.getPreferredSize().width :
                      component.getMinimumSize().width;

          if(constraints.insets != null)
            width += constraints.insets.left + constraints.insets.right;

          width += constraints.ipadx;

          distributeSizeAndWeight(width,
796
                                  constraints.weightx,
Tom Tromey committed
797 798 799 800 801 802 803 804 805
                                  constraints.gridx,
                                  constraints.gridwidth,
                                  info.colWidths,
                                  info.colWeights);
        } // end of STEP 3

      // STEP 4: Determine sizes and weights for rows.
      for (int i = 0; i < sortedByHeight.size(); i++)
        {
806
          Component component = sortedByHeight.get(i);
807

Tom Tromey committed
808 809 810 811 812 813 814 815 816 817 818 819 820 821
          // If component is not visible we dont have to care about it.
          if (!component.isVisible())
            continue;

          GridBagConstraints constraints = lookupInternalConstraints (component);

          int height = (sizeflag == PREFERREDSIZE) ?
                       component.getPreferredSize().height :
                       component.getMinimumSize().height;

          if(constraints.insets != null)
            height += constraints.insets.top + constraints.insets.bottom;

          height += constraints.ipady;
822

Tom Tromey committed
823
          distributeSizeAndWeight(height,
824
                                  constraints.weighty,
Tom Tromey committed
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
                                  constraints.gridy,
                                  constraints.gridheight,
                                  info.rowHeights,
                                  info.rowWeights);
        } // end of STEP 4

      // Adjust cell sizes iff parent size not zero.
      if (parentDim.width > 0 && parentDim.height > 0)
        {
          calcCellSizes (info.colWidths, info.colWeights, parentDim.width);
          calcCellSizes (info.rowHeights, info.rowWeights, parentDim.height);
        }

      int totalWidth = sumIntArray(info.colWidths);
      int totalHeight = sumIntArray(info.rowHeights);

      // Make sure pos_x and pos_y are never negative.
      if (totalWidth >= parentDim.width)
        info.pos_x = parentInsets.left;
      else
        info.pos_x = parentInsets.left + (parentDim.width - totalWidth) / 2;

      if (totalHeight >= parentDim.height)
        info.pos_y = parentInsets.top;
      else
        info.pos_y = parentInsets.top + (parentDim.height - totalHeight) / 2;

      // DEBUG
      //dumpLayoutInfo (info);

      return info;
    }

    /**
     * Obsolete.
     */
    protected Dimension GetMinSize (Container parent, GridBagLayoutInfo info)
    {
      if (parent == null || info == null)
        return new Dimension (0, 0);

      Insets insets = parent.getInsets();
      int width = sumIntArray (info.colWidths) + insets.left + insets.right;
      int height = sumIntArray (info.rowHeights) + insets.top + insets.bottom;
      return new Dimension (width, height);
    }

    /**
     * @since 1.4
     */
    protected Dimension getMinSize (Container parent, GridBagLayoutInfo info)
    {
      return GetMinSize (parent, info);
    }

    /**
     * Helper method used by GetLayoutInfo to keep components sorted, either
     * by gridwidth or gridheight.
     *
     * @param component   Component to add to the sorted list.
     * @param span        Either the component's gridwidth or gridheight.
     * @param list        <code>ArrayList</code> of components, sorted by
     *                    their span.
     * @param sortByWidth Flag indicating sorting index. If true, sort by
     *                    width. Otherwise, sort by height.
     * FIXME: Use a better sorting algorithm.
     */
892
    private void sortBySpan (Component component, int span,
893
                             ArrayList<Component> list, boolean sortByWidth)
Tom Tromey committed
894 895 896 897 898 899 900 901 902 903 904 905
    {
      if (span == GridBagConstraints.REMAINDER
          || span == GridBagConstraints.RELATIVE)
        {
          // Put all RELATIVE and REMAINDER components at the end.
          list.add(component);
        }
      else
        {
          int i = 0;
          if (list.size() > 0)
            {
906
              GridBagConstraints gbc = lookupInternalConstraints(list.get(i));
Tom Tromey committed
907 908 909 910 911 912 913 914 915 916
              int otherspan = sortByWidth ?
                              gbc.gridwidth :
                              gbc.gridheight;
              while (otherspan != GridBagConstraints.REMAINDER
                     && otherspan != GridBagConstraints.RELATIVE
                     && span >= otherspan)
                {
                  i++;
                  if (i < list.size())
                    {
917
                      gbc = lookupInternalConstraints(list.get(i));
Tom Tromey committed
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
                      otherspan = sortByWidth ?
                                  gbc.gridwidth :
                                  gbc.gridheight;
                    }
                  else
                    break;
                }
            }
          list.add(i, component);
        }
    }

    /**
     * Helper method used by GetLayoutInfo to distribute a component's size
     * and weight.
     *
     * @param size    Preferred size of component, with inset and padding
     *                already added.
     * @param weight  Weight of component.
     * @param start   Starting position of component. Either
     *                constraints.gridx or gridy.
     * @param span    Span of component. either contraints.gridwidth or
     *                gridheight.
     * @param sizes   Sizes of rows or columns.
     * @param weights Weights of rows or columns.
     */
    private void distributeSizeAndWeight (int size, double weight,
                                          int start, int span,
                                          int[] sizes, double[] weights)
    {
      if (span == 1)
        {
          sizes[start] = Math.max(sizes[start], size);
          weights[start] = Math.max(weights[start], weight);
        }
953
      else
Tom Tromey committed
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
        {
          int numOccupied = span;
          int lastOccupied = -1;

          for(int i = start; i < start + span; i++)
            {
              if (sizes[i] == 0.0)
                numOccupied--;
              else
                {
                  size -= sizes[i];
                  lastOccupied = i;
                }
            }

          // A component needs to occupy at least one row.
          if(numOccupied == 0)
            sizes[start + span - 1] = size;
          else if (size > 0)
            sizes[lastOccupied] += size;

          calcCellWeights(weight, weights, start, span);
        }
    }

    /**
     * Helper method used by GetLayoutInfo to calculate weight distribution.
     * @param weight  Weight of component.
     * @param weights Weights of rows/columns.
     * @param start   Starting position of component in grid (gridx/gridy).
     * @param span    Span of component (gridwidth/gridheight).
     */
    private void calcCellWeights (double weight, double[] weights, int start, int span)
    {
      double totalWeight = 0.0;
      for(int k = start; k < start + span; k++)
        totalWeight += weights[k];

      if(weight > totalWeight)
        {
          if (totalWeight == 0.0)
            {
              weights[start + span - 1] += weight;
            }
          else
            {
              double diff = weight - totalWeight ;
              double remaining = diff;

              for(int k = start; k < start + span; k++)
                {
                  double extraWeight = diff * weights[k] / totalWeight;
                  weights[k] += extraWeight;
                  remaining -= extraWeight;
1008
                }
Tom Tromey committed
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046

              if (remaining > 0.0 && weights[start + span - 1] != 0.0)
                {
                  weights[start + span - 1] += remaining;
                }
            }
        }
    }

    /**
     * Helper method used by GetLayoutInfo to distribute extra space
     * based on weight distribution.
     *
     * @param sizes   Sizes of rows/columns.
     * @param weights Weights of rows/columns.
     * @param range   Dimension of container.
     */
    private void calcCellSizes (int[] sizes, double[] weights, int range)
    {
      int totalSize = sumIntArray (sizes);
      double totalWeight = sumDoubleArray (weights);

      int diff = range - totalSize;

      if (diff == 0)
        return;

      for (int i = 0; i < sizes.length; i++)
        {
          int newsize = (int) (sizes[i] + (((double) diff) * weights [i] / totalWeight ));

          if (newsize > 0)
            sizes[i] = newsize;
        }
    }

    private void dumpLayoutInfo (GridBagLayoutInfo info)
    {
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
        System.out.println ("GridBagLayoutInfo:");
        System.out.println ("cols: " + info.cols + ", rows: " + info.rows);
        System.out.print ("colWidths: ");
        dumpArray(info.colWidths);
        System.out.print ("rowHeights: ");
        dumpArray(info.rowHeights);
        System.out.print ("colWeights: ");
        dumpArray(info.colWeights);
        System.out.print ("rowWeights: ");
        dumpArray(info.rowWeights);
Tom Tromey committed
1057 1058 1059 1060
    }

    private void dumpArray(int[] array)
    {
1061 1062 1063 1064 1065 1066 1067 1068
        String sep = "";
        for(int i = 0; i < array.length; i++)
        {
            System.out.print(sep);
            System.out.print(array[i]);
            sep = ", ";
        }
        System.out.println();
Tom Tromey committed
1069 1070 1071 1072
    }

    private void dumpArray(double[] array)
    {
1073 1074 1075 1076 1077 1078 1079 1080
        String sep = "";
        for(int i = 0; i < array.length; i++)
        {
            System.out.print(sep);
            System.out.print(array[i]);
            sep = ", ";
        }
        System.out.println();
Tom Tromey committed
1081
    }
1082

Tom Tromey committed
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
    /**
     * @since 1.4
     */
    protected void arrangeGrid (Container parent)
    {
      ArrangeGrid (parent);
    }

    /**
     * @since 1.4
     */
    protected GridBagLayoutInfo getLayoutInfo (Container parent, int sizeflag)
    {
      return GetLayoutInfo (parent, sizeflag);
    }

    /**
1100 1101 1102 1103 1104 1105 1106
     * Move and resize a rectangle according to a set of grid bag
     * constraints.  The x, y, width and height fields of the
     * rectangle argument are adjusted to the new values.
     *
     * @param constraints position and size constraints
     * @param r rectangle to be moved and resized
     *
Tom Tromey committed
1107 1108
     * @since 1.4
     */
1109 1110
    protected void adjustForGravity (GridBagConstraints constraints,
                                     Rectangle r)
Tom Tromey committed
1111
    {
1112
      AdjustForGravity (constraints, r);
Tom Tromey committed
1113 1114
    }
}