001/* GridBagLayout - Layout manager for components according to GridBagConstraints
002   Copyright (C) 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt;
040
041import java.io.Serializable;
042import java.util.ArrayList;
043import java.util.HashMap;
044import java.util.Hashtable;
045
046/**
047 * @author Michael Koch (konqueror@gmx.de)
048 * @author Jeroen Frijters (jeroen@frijters.net)
049 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
050 */
051public class GridBagLayout
052    implements Serializable, LayoutManager2
053{
054    private static final long serialVersionUID = 8838754796412211005L;
055
056    protected static final int MINSIZE = 1;
057    protected static final int PREFERREDSIZE = 2;
058    protected static final int MAXGRIDSIZE = 512;
059
060    // comptable remembers the original contraints given to us.
061    // internalcomptable is used to keep track of modified constraint values
062    // that we calculate, particularly when we are given RELATIVE and
063    // REMAINDER constraints.
064    // Constraints kept in comptable are never modified, and constraints
065    // kept in internalcomptable can be modified internally only.
066    protected Hashtable<Component,GridBagConstraints> comptable;
067    private Hashtable<Component,GridBagConstraints> internalcomptable;
068    protected GridBagLayoutInfo layoutInfo;
069    protected GridBagConstraints defaultConstraints;
070
071    public double[] columnWeights;
072    public int[] columnWidths;
073    public double[] rowWeights;
074    public int[] rowHeights;
075
076    public GridBagLayout ()
077    {
078        this.comptable = new Hashtable<Component,GridBagConstraints>();
079        this.internalcomptable = new Hashtable<Component,GridBagConstraints>();
080        this.defaultConstraints= new GridBagConstraints();
081    }
082
083    /**
084     * Helper method to calc the sum of a range of elements in an int array.
085     */
086    private int sumIntArray (int[] array, int upto)
087    {
088        int result = 0;
089
090        for (int i = 0; i < upto; i++)
091            result += array [i];
092
093        return result;
094    }
095
096    /**
097     * Helper method to calc the sum of all elements in an int array.
098     */
099    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     */
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
117    public void addLayoutComponent (String name, Component component)
118    {
119        // do nothing here.
120    }
121
122    public void removeLayoutComponent (Component component)
123    {
124        // do nothing here
125    }
126
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
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
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
158    public Dimension maximumLayoutSize (Container target)
159    {
160        return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE);
161    }
162
163    public void layoutContainer (Container parent)
164    {
165      arrangeGrid (parent);
166    }
167
168    public float getLayoutAlignmentX (Container target)
169    {
170        return Component.CENTER_ALIGNMENT;
171    }
172
173    public float getLayoutAlignmentY (Container target)
174    {
175        return Component.CENTER_ALIGNMENT;
176    }
177
178    public void invalidateLayout (Container target)
179    {
180        this.layoutInfo = null;
181    }
182
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
207    public GridBagConstraints getConstraints (Component component)
208    {
209        return (GridBagConstraints) (lookupConstraints (component).clone());
210    }
211
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
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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
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     */
1086    protected void arrangeGrid (Container parent)
1087    {
1088      ArrangeGrid (parent);
1089    }
1090
1091    /**
1092     * @since 1.4
1093     */
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     */
1109    protected void adjustForGravity (GridBagConstraints constraints,
1110                                     Rectangle r)
1111    {
1112      AdjustForGravity (constraints, r);
1113    }
1114}