001    /* GridBagLayout - Layout manager for components according to GridBagConstraints
002       Copyright (C) 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.awt;
040    
041    import java.io.Serializable;
042    import java.util.ArrayList;
043    import java.util.HashMap;
044    import 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     */
051    public 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 = (GridBagConstraints) comptable.get (component);
215    
216            if (result == null)
217            {
218                setConstraints (component, defaultConstraints);
219                result = (GridBagConstraints) comptable.get (component);
220            }
221        
222            return result;
223        }
224    
225        private GridBagConstraints lookupInternalConstraints (Component component)
226        {
227            GridBagConstraints result =
228                (GridBagConstraints) internalcomptable.get (component);
229    
230            if (result == null)
231            {
232                result = (GridBagConstraints) lookupConstraints(component).clone();
233                internalcomptable.put (component, result);
234            }
235        
236            return result;
237        }
238    
239        /**
240         * @since 1.1
241         */
242        public Point getLayoutOrigin ()
243        {
244            if (layoutInfo == null)
245                return new Point (0, 0);
246        
247            return new Point (layoutInfo.pos_x, layoutInfo.pos_y);
248        }
249    
250        /**
251         * @since 1.1
252         */
253        public int[][] getLayoutDimensions ()
254        {
255            int[][] result = new int [2][];
256            if (layoutInfo == null)
257              {
258                result[0] = new int[0];
259                result[1] = new int[0];
260    
261                return result;
262              }
263    
264            result [0] = new int [layoutInfo.cols];
265            System.arraycopy (layoutInfo.colWidths, 0, result [0], 0, layoutInfo.cols);
266            result [1] = new int [layoutInfo.rows];
267            System.arraycopy (layoutInfo.rowHeights, 0, result [1], 0, layoutInfo.rows);
268            return result;
269        }
270    
271        public double[][] getLayoutWeights ()
272        {
273            double[][] result = new double [2][];
274            if (layoutInfo == null)
275              {
276                result[0] = new double[0];
277                result[1] = new double[0];
278    
279                return result;
280              }
281    
282            result [0] = new double [layoutInfo.cols];
283            System.arraycopy (layoutInfo.colWeights, 0, result [0], 0, layoutInfo.cols);
284            result [1] = new double [layoutInfo.rows];
285            System.arraycopy (layoutInfo.rowWeights, 0, result [1], 0, layoutInfo.rows);
286            return result;
287        }
288    
289        /**
290         * @since 1.1
291         */
292        public Point location (int x, int y)
293        {
294            if (layoutInfo == null)
295                return new Point (0, 0);
296    
297            int col;
298            int row;
299            int pixel_x = layoutInfo.pos_x;
300            int pixel_y = layoutInfo.pos_y;
301    
302            for (col = 0; col < layoutInfo.cols; col++)
303            {
304                int w = layoutInfo.colWidths [col];
305                if (x < pixel_x + w)
306                    break;
307    
308                pixel_x += w;
309            }
310    
311            for (row = 0; row < layoutInfo.rows; row++)
312            {
313                int h = layoutInfo.rowHeights [row];
314                if (y < pixel_y + h)
315                    break;
316    
317                pixel_y += h;
318            }
319    
320            return new Point (col, row);
321        }
322    
323        /**
324         * Return a string representation of this GridBagLayout.
325         *
326         * @return a string representation
327         */
328        public String toString()
329        {
330          return getClass().getName();
331        }
332        
333        /**
334         * Move and resize a rectangle according to a set of grid bag
335         * constraints.  The x, y, width and height fields of the
336         * rectangle argument are adjusted to the new values.
337         *
338         * @param constraints position and size constraints
339         * @param r rectangle to be moved and resized
340         */
341        protected void AdjustForGravity (GridBagConstraints constraints,
342                                         Rectangle r)
343        {
344          Insets insets = constraints.insets;
345          if (insets != null)
346            {
347              r.x += insets.left;
348              r.y += insets.top;
349              r.width -= insets.left + insets.right;
350              r.height -= insets.top + insets.bottom;
351            }
352        }
353    
354        /**
355         * Obsolete.
356         */
357        protected void ArrangeGrid (Container parent)
358        {
359          Component[] components = parent.getComponents();
360    
361          if (components.length == 0)
362            return;
363    
364          GridBagLayoutInfo info = getLayoutInfo (parent, PREFERREDSIZE);
365          if (info.cols == 0 && info.rows == 0)
366            return;
367    
368          // DEBUG
369          //dumpLayoutInfo (info);
370    
371          // Calling setBounds on these components causes this layout to
372          // be invalidated, clearing the layout information cache,
373          // layoutInfo.  So we wait until after this for loop to set
374          // layoutInfo.
375          Component lastComp = null;
376    
377          Rectangle cell = new Rectangle();
378    
379          for (int i = 0; i < components.length; i++)
380          {
381            Component component = components[i];
382    
383            // If component is not visible we dont have to care about it.
384            if (! component.isVisible())
385              continue;
386    
387            Dimension dim = component.getPreferredSize();
388            GridBagConstraints constraints = lookupInternalConstraints(component);
389            
390            if (lastComp != null
391                && constraints.gridheight == GridBagConstraints.REMAINDER)
392              cell.y += cell.height;
393            else
394              cell.y = sumIntArray(info.rowHeights, constraints.gridy);
395            
396            if (lastComp != null
397                && constraints.gridwidth == GridBagConstraints.REMAINDER)
398              cell.x += cell.width;
399            else
400              cell.x = sumIntArray(info.colWidths, constraints.gridx);
401    
402            cell.width = sumIntArray(info.colWidths, constraints.gridx
403                                                + constraints.gridwidth) - cell.x;
404            cell.height = sumIntArray(info.rowHeights, constraints.gridy
405                                                 + constraints.gridheight) - cell.y;
406            
407            // Adjust for insets.
408            AdjustForGravity( constraints, cell );
409    
410            // Note: Documentation says that padding is added on both sides, but
411            // visual inspection shows that the Sun implementation only adds it
412            // once, so we do the same.
413            dim.width += constraints.ipadx;
414            dim.height += constraints.ipady;
415    
416            switch (constraints.fill)
417              {
418              case GridBagConstraints.HORIZONTAL:
419                dim.width = cell.width;
420                break;
421              case GridBagConstraints.VERTICAL:
422                dim.height = cell.height;
423                break;
424              case GridBagConstraints.BOTH:
425                dim.width = cell.width;
426                dim.height = cell.height;
427                break;
428              }
429    
430            int x = 0;
431            int y = 0;
432    
433            switch (constraints.anchor)
434              {
435              case GridBagConstraints.NORTH:
436                x = cell.x + (cell.width - dim.width) / 2;
437                y = cell.y;
438                break;
439              case GridBagConstraints.SOUTH:
440                x = cell.x + (cell.width - dim.width) / 2;
441                y = cell.y + cell.height - dim.height;
442                break;
443              case GridBagConstraints.WEST:
444                x = cell.x;
445                y = cell.y + (cell.height - dim.height) / 2;
446                break;
447              case GridBagConstraints.EAST:
448                x = cell.x + cell.width - dim.width;
449                y = cell.y + (cell.height - dim.height) / 2;
450                break;
451              case GridBagConstraints.NORTHEAST:
452                x = cell.x + cell.width - dim.width;
453                y = cell.y;
454                break;
455              case GridBagConstraints.NORTHWEST:
456                x = cell.x;
457                y = cell.y;
458                break;
459              case GridBagConstraints.SOUTHEAST:
460                x = cell.x + cell.width - dim.width;
461                y = cell.y + cell.height - dim.height;
462                break;
463              case GridBagConstraints.SOUTHWEST:
464                x = cell.x;
465                y = cell.y + cell.height - dim.height;
466                break;
467              default:
468                x = cell.x + (cell.width - dim.width) / 2;
469                y = cell.y + (cell.height - dim.height) / 2;
470                break;
471              }
472            component.setBounds(info.pos_x + x, info.pos_y + y, dim.width,
473                                dim.height);
474            lastComp = component;
475          }
476    
477        // DEBUG
478        //dumpLayoutInfo(info);
479    
480        // Cache layout information.
481        layoutInfo = getLayoutInfo(parent, PREFERREDSIZE);
482      }
483    
484        /**
485         * Obsolete.
486         */
487        protected GridBagLayoutInfo GetLayoutInfo (Container parent, int sizeflag)
488        {
489          if (sizeflag != MINSIZE && sizeflag != PREFERREDSIZE)
490            throw new IllegalArgumentException();
491    
492          Dimension parentDim = parent.getSize ();
493          Insets parentInsets = parent.getInsets ();
494          parentDim.width -= parentInsets.left + parentInsets.right;
495          parentDim.height -= parentInsets.top + parentInsets.bottom;
496       
497          int current_y = 0;
498          int max_x = 0;
499          int max_y = 0;
500    
501          // Guaranteed to contain the last component added to the given row
502          // or column, whose gridwidth/height is not REMAINDER.
503          HashMap<Integer,Component> lastInRow = new HashMap<Integer,Component>();
504          HashMap<Integer,Component> lastInCol = new HashMap<Integer,Component>();
505    
506          Component[] components = parent.getComponents();
507    
508          // Components sorted by gridwidths/heights,
509          // smallest to largest, with REMAINDER and RELATIVE at the end.
510          // These are useful when determining sizes and weights.
511          ArrayList<Component> sortedByWidth =
512            new ArrayList<Component>(components.length);
513          ArrayList<Component> sortedByHeight =
514            new ArrayList<Component>(components.length);
515    
516          // STEP 1: first we figure out how many rows/columns
517          for (int i = 0; i < components.length; i++)
518            {
519              Component component = components [i];
520              // If component is not visible we dont have to care about it.
521              if (!component.isVisible())
522                continue;
523    
524              // When looking up the constraint for the first time, check the
525              // original unmodified constraint.  After the first time, always
526              // refer to the internal modified constraint.
527              GridBagConstraints originalConstraints = lookupConstraints (component);
528              GridBagConstraints constraints = (GridBagConstraints) originalConstraints.clone();
529              internalcomptable.put(component, constraints);
530    
531              // Cases:
532              //
533              // 1. gridy == RELATIVE, gridx == RELATIVE
534              //
535              //       use y as the row number; check for the next
536              //       available slot at row y
537              //
538              // 2. only gridx == RELATIVE
539              //
540              //       check for the next available slot at row gridy
541              //
542              // 3. only gridy == RELATIVE
543              //
544              //       check for the next available slot at column gridx
545              //
546              // 4. neither gridx or gridy == RELATIVE
547              //
548              //       nothing to check; just add it
549    
550              // cases 1 and 2
551              if(constraints.gridx == GridBagConstraints.RELATIVE)
552                {
553                  if (constraints.gridy == GridBagConstraints.RELATIVE)
554                  constraints.gridy = current_y;
555    
556                  int x;
557    
558                  // Check the component that occupies the right-most spot in this
559                  // row. We want to add this component after it.
560                  // If this row is empty, add to the 0 position.
561                  if (!lastInRow.containsKey(new Integer(constraints.gridy))) 
562                    x = 0;
563                  else
564                    {
565                      Component lastComponent = (Component) lastInRow.get(new Integer(constraints.gridy));
566                      GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
567                      x = lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth);
568                    }
569    
570                  // Determine if this component will fit in the slot vertically.
571                  // If not, bump it over to where it does fit.
572                  for (int y = constraints.gridy + 1; y < constraints.gridy + Math.max(1, constraints.gridheight); y++)
573                    {
574                      if (lastInRow.containsKey(new Integer(y)))
575                        {
576                          Component lastComponent = (Component) lastInRow.get(new Integer(y));
577                          GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
578                          x = Math.max (x,
579                                        lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth));
580                        }
581                    }
582    
583                  constraints.gridx = x;
584                }
585              // case 3
586              else if(constraints.gridy == GridBagConstraints.RELATIVE)
587                {
588                  int y;
589                  // Check the component that occupies the bottom-most spot in
590                  // this column. We want to add this component below it.
591                  // If this column is empty, add to the 0 position.
592                  if (!lastInCol.containsKey(new Integer(constraints.gridx))) 
593                    {
594                      y = current_y;
595                    }
596                  else
597                    {
598                      Component lastComponent = (Component)lastInCol.get(new Integer(constraints.gridx));
599                      GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
600                      y = lastConstraints.gridy + Math.max(1, lastConstraints.gridheight);
601                    }
602    
603                  // Determine if this component will fit in the slot horizontally.
604                  // If not, bump it down to where it does fit.
605                  for (int x = constraints.gridx + 1; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++)
606                    {
607                      if (lastInCol.containsKey(new Integer(x)))
608                        {
609                          Component lastComponent = (Component) lastInCol.get(new Integer(x));
610                          GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
611                          y = Math.max (y,
612                                        lastConstraints.gridy + Math.max(1, lastConstraints.gridheight));
613                        }
614                    }
615    
616                  constraints.gridy = y;
617                }
618              // case 4: do nothing
619    
620              max_x = Math.max(max_x, 
621                               constraints.gridx + Math.max(1, constraints.gridwidth));
622              max_y = Math.max(max_y,
623                               constraints.gridy + Math.max(1, constraints.gridheight));
624    
625              sortBySpan(component, constraints.gridwidth, sortedByWidth, true);
626              sortBySpan(component, constraints.gridheight, sortedByHeight, false);
627    
628              // Update our reference points for RELATIVE gridx and gridy.
629              if(constraints.gridwidth == GridBagConstraints.REMAINDER)
630                {
631              current_y = constraints.gridy + Math.max(1, constraints.gridheight);
632                }
633              else if (constraints.gridwidth != GridBagConstraints.REMAINDER)
634                {
635                  for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++)
636                    {
637                      if(lastInRow.containsKey(new Integer(y)))
638                        {
639                          Component lastComponent = (Component) lastInRow.get(new Integer(y));
640                          GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
641                          if (constraints.gridx > lastConstraints.gridx)
642                            {
643                              lastInRow.put(new Integer(y), component);
644                            }
645                        }
646                      else
647                        {
648                          lastInRow.put(new Integer(y), component);
649                        }
650                    }
651    
652                  for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++)
653                    {
654                      if(lastInCol.containsKey(new Integer(x)))
655                        {
656                          Component lastComponent = (Component) lastInCol.get(new Integer(x));
657                          GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
658                          if (constraints.gridy > lastConstraints.gridy)
659                            {
660                              lastInCol.put(new Integer(x), component);
661                            }
662                        }
663                      else
664                        {
665                          lastInCol.put(new Integer(x), component);
666                        }
667                    }
668                }
669            } // end of STEP 1
670            
671          GridBagLayoutInfo info = new GridBagLayoutInfo(max_x, max_y);
672    
673          // Check if column widths and row heights are overridden.
674    
675          for (int x = 0; x < max_x; x++)
676            {
677              if(columnWidths != null && columnWidths.length > x)
678                info.colWidths[x] = columnWidths[x];
679              if(columnWeights != null && columnWeights.length > x)
680                info.colWeights[x] = columnWeights[x];
681            }
682    
683          for (int y = 0; y < max_y; y++)
684            {
685              if(rowHeights != null && rowHeights.length > y)
686                info.rowHeights[y] = rowHeights[y];
687              if(rowWeights != null && rowWeights.length > y)
688                info.rowWeights[y] = rowWeights[y];
689            }
690    
691          // STEP 2: Fix up any cells with width/height as REMAINDER/RELATIVE.
692          for (int i = 0; i < components.length; i++)
693            {
694              Component component = components [i];
695                            
696              // If component is not visible we dont have to care about it.
697              if (!component.isVisible())
698                continue;
699                            
700              GridBagConstraints constraints = lookupInternalConstraints (component);
701    
702              if(constraints.gridwidth == GridBagConstraints.REMAINDER || constraints.gridwidth == GridBagConstraints.RELATIVE)
703                {
704                  if(constraints.gridwidth == GridBagConstraints.REMAINDER)
705                    {
706                      for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++)
707                        {
708                          if (lastInRow.containsKey(new Integer(y)))
709                            {
710                              Component lastComponent = (Component) lastInRow.get(new Integer(y));
711                              GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
712    
713                              if (lastConstraints.gridwidth == GridBagConstraints.RELATIVE)
714                                {
715                                  constraints.gridx = max_x - 1;
716                                  break;
717                                }
718                              else
719                                {
720                                  constraints.gridx = Math.max (constraints.gridx,
721                                                                lastConstraints.gridx + Math.max (1, lastConstraints.gridwidth));
722                                }
723                            }
724                        }
725                      constraints.gridwidth = max_x - constraints.gridx;
726                    }
727                  else if (constraints.gridwidth == GridBagConstraints.RELATIVE)
728                    {
729                      constraints.gridwidth = max_x - constraints.gridx - 1;
730                    }
731    
732                  // Re-sort
733                  sortedByWidth.remove(sortedByWidth.indexOf(component));
734                  sortBySpan(component, constraints.gridwidth, sortedByWidth, true);
735                }
736    
737              if(constraints.gridheight == GridBagConstraints.REMAINDER || constraints.gridheight == GridBagConstraints.RELATIVE)
738                {
739                  if(constraints.gridheight == GridBagConstraints.REMAINDER)
740                    {
741                      for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++)
742                        {
743                          if (lastInCol.containsKey(new Integer(x)))
744                            {
745                              Component lastComponent = (Component) lastInRow.get(new Integer(x));
746                              if (lastComponent != null)
747                                {
748                                  GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent);
749        
750                                  if (lastConstraints.gridheight == GridBagConstraints.RELATIVE)
751                                    {
752                                      constraints.gridy = max_y - 1;
753                                      break;
754                                    }
755                                  else
756                                    {
757                                      constraints.gridy = Math.max (constraints.gridy,
758                                                                    lastConstraints.gridy + Math.max (1, lastConstraints.gridheight));
759                                    }
760                                }
761                            }
762                        }
763                      constraints.gridheight = max_y - constraints.gridy;
764                    }
765                  else if (constraints.gridheight == GridBagConstraints.RELATIVE)
766                    {
767                      constraints.gridheight = max_y - constraints.gridy - 1;
768                    }
769    
770                  // Re-sort
771                  sortedByHeight.remove(sortedByHeight.indexOf(component));
772                  sortBySpan(component, constraints.gridheight, sortedByHeight, false);
773                }
774            } // end of STEP 2
775    
776          // STEP 3: Determine sizes and weights for columns.
777          for (int i = 0; i < sortedByWidth.size(); i++)
778            {
779              Component component = sortedByWidth.get(i);
780                            
781              // If component is not visible we dont have to care about it.
782              if (!component.isVisible())
783                continue;
784    
785              GridBagConstraints constraints = lookupInternalConstraints (component);
786    
787              int width = (sizeflag == PREFERREDSIZE) ?
788                          component.getPreferredSize().width :
789                          component.getMinimumSize().width;
790    
791              if(constraints.insets != null)
792                width += constraints.insets.left + constraints.insets.right;
793    
794              width += constraints.ipadx;
795    
796              distributeSizeAndWeight(width,
797                                      constraints.weightx, 
798                                      constraints.gridx,
799                                      constraints.gridwidth,
800                                      info.colWidths,
801                                      info.colWeights);
802            } // end of STEP 3
803    
804          // STEP 4: Determine sizes and weights for rows.
805          for (int i = 0; i < sortedByHeight.size(); i++)
806            {
807              Component component = (Component) sortedByHeight.get(i);
808                            
809              // If component is not visible we dont have to care about it.
810              if (!component.isVisible())
811                continue;
812    
813              GridBagConstraints constraints = lookupInternalConstraints (component);
814    
815              int height = (sizeflag == PREFERREDSIZE) ?
816                           component.getPreferredSize().height :
817                           component.getMinimumSize().height;
818    
819              if(constraints.insets != null)
820                height += constraints.insets.top + constraints.insets.bottom;
821    
822              height += constraints.ipady;
823              
824              distributeSizeAndWeight(height,
825                                      constraints.weighty, 
826                                      constraints.gridy,
827                                      constraints.gridheight,
828                                      info.rowHeights,
829                                      info.rowWeights);
830            } // end of STEP 4
831    
832          // Adjust cell sizes iff parent size not zero.
833          if (parentDim.width > 0 && parentDim.height > 0)
834            {
835              calcCellSizes (info.colWidths, info.colWeights, parentDim.width);
836              calcCellSizes (info.rowHeights, info.rowWeights, parentDim.height);
837            }
838    
839          int totalWidth = sumIntArray(info.colWidths);
840          int totalHeight = sumIntArray(info.rowHeights);
841    
842          // Make sure pos_x and pos_y are never negative.
843          if (totalWidth >= parentDim.width)
844            info.pos_x = parentInsets.left;
845          else
846            info.pos_x = parentInsets.left + (parentDim.width - totalWidth) / 2;
847    
848          if (totalHeight >= parentDim.height)
849            info.pos_y = parentInsets.top;
850          else
851            info.pos_y = parentInsets.top + (parentDim.height - totalHeight) / 2;
852    
853          // DEBUG
854          //dumpLayoutInfo (info);
855    
856          return info;
857        }
858    
859        /**
860         * Obsolete.
861         */
862        protected Dimension GetMinSize (Container parent, GridBagLayoutInfo info)
863        {
864          if (parent == null || info == null)
865            return new Dimension (0, 0);
866    
867          Insets insets = parent.getInsets();
868          int width = sumIntArray (info.colWidths) + insets.left + insets.right;
869          int height = sumIntArray (info.rowHeights) + insets.top + insets.bottom;
870          return new Dimension (width, height);
871        }
872    
873        /**
874         * @since 1.4
875         */
876        protected Dimension getMinSize (Container parent, GridBagLayoutInfo info)
877        {
878          return GetMinSize (parent, info);
879        }
880    
881        /**
882         * Helper method used by GetLayoutInfo to keep components sorted, either
883         * by gridwidth or gridheight.
884         *
885         * @param component   Component to add to the sorted list.
886         * @param span        Either the component's gridwidth or gridheight.
887         * @param list        <code>ArrayList</code> of components, sorted by
888         *                    their span.
889         * @param sortByWidth Flag indicating sorting index. If true, sort by
890         *                    width. Otherwise, sort by height.
891         * FIXME: Use a better sorting algorithm.
892         */
893        private void sortBySpan (Component component, int span,
894                                 ArrayList<Component> list, boolean sortByWidth)
895        {
896          if (span == GridBagConstraints.REMAINDER
897              || span == GridBagConstraints.RELATIVE)
898            {
899              // Put all RELATIVE and REMAINDER components at the end.
900              list.add(component);
901            }
902          else
903            {
904              int i = 0;
905              if (list.size() > 0)
906                {
907                  GridBagConstraints gbc = lookupInternalConstraints((Component) list.get(i));
908                  int otherspan = sortByWidth ?
909                                  gbc.gridwidth :
910                                  gbc.gridheight;
911                  while (otherspan != GridBagConstraints.REMAINDER
912                         && otherspan != GridBagConstraints.RELATIVE
913                         && span >= otherspan)
914                    {
915                      i++;
916                      if (i < list.size())
917                        {
918                          gbc = lookupInternalConstraints((Component) list.get(i));
919                          otherspan = sortByWidth ?
920                                      gbc.gridwidth :
921                                      gbc.gridheight;
922                        }
923                      else
924                        break;
925                    }
926                }
927              list.add(i, component);
928            }
929        }
930    
931        /**
932         * Helper method used by GetLayoutInfo to distribute a component's size
933         * and weight.
934         *
935         * @param size    Preferred size of component, with inset and padding
936         *                already added.
937         * @param weight  Weight of component.
938         * @param start   Starting position of component. Either
939         *                constraints.gridx or gridy.
940         * @param span    Span of component. either contraints.gridwidth or
941         *                gridheight.
942         * @param sizes   Sizes of rows or columns.
943         * @param weights Weights of rows or columns.
944         */
945        private void distributeSizeAndWeight (int size, double weight,
946                                              int start, int span,
947                                              int[] sizes, double[] weights)
948        {
949          if (span == 1)
950            {
951              sizes[start] = Math.max(sizes[start], size);
952              weights[start] = Math.max(weights[start], weight);
953            }
954          else
955            {
956              int numOccupied = span;
957              int lastOccupied = -1;
958    
959              for(int i = start; i < start + span; i++)
960                {
961                  if (sizes[i] == 0.0)
962                    numOccupied--;
963                  else
964                    {
965                      size -= sizes[i];
966                      lastOccupied = i;
967                    }
968                }
969    
970              // A component needs to occupy at least one row.
971              if(numOccupied == 0)
972                sizes[start + span - 1] = size;
973              else if (size > 0)
974                sizes[lastOccupied] += size;
975    
976              calcCellWeights(weight, weights, start, span);
977            }
978        }
979    
980        /**
981         * Helper method used by GetLayoutInfo to calculate weight distribution.
982         * @param weight  Weight of component.
983         * @param weights Weights of rows/columns.
984         * @param start   Starting position of component in grid (gridx/gridy).
985         * @param span    Span of component (gridwidth/gridheight).
986         */
987        private void calcCellWeights (double weight, double[] weights, int start, int span)
988        {
989          double totalWeight = 0.0;
990          for(int k = start; k < start + span; k++)
991            totalWeight += weights[k];
992    
993          if(weight > totalWeight)
994            {
995              if (totalWeight == 0.0)
996                {
997                  weights[start + span - 1] += weight;
998                }
999              else
1000                {
1001                  double diff = weight - totalWeight ;
1002                  double remaining = diff;
1003    
1004                  for(int k = start; k < start + span; k++)
1005                    {
1006                      double extraWeight = diff * weights[k] / totalWeight;
1007                      weights[k] += extraWeight;
1008                      remaining -= extraWeight;
1009                    } 
1010    
1011                  if (remaining > 0.0 && weights[start + span - 1] != 0.0)
1012                    {
1013                      weights[start + span - 1] += remaining;
1014                    }
1015                }
1016            }
1017        }
1018    
1019        /**
1020         * Helper method used by GetLayoutInfo to distribute extra space
1021         * based on weight distribution.
1022         *
1023         * @param sizes   Sizes of rows/columns.
1024         * @param weights Weights of rows/columns.
1025         * @param range   Dimension of container.
1026         */
1027        private void calcCellSizes (int[] sizes, double[] weights, int range)
1028        {
1029          int totalSize = sumIntArray (sizes);
1030          double totalWeight = sumDoubleArray (weights);
1031    
1032          int diff = range - totalSize;
1033    
1034          if (diff == 0)
1035            return;
1036    
1037          for (int i = 0; i < sizes.length; i++)
1038            {
1039              int newsize = (int) (sizes[i] + (((double) diff) * weights [i] / totalWeight ));
1040    
1041              if (newsize > 0)
1042                sizes[i] = newsize;
1043            }
1044        }
1045    
1046        private void dumpLayoutInfo (GridBagLayoutInfo info)
1047        {
1048            System.out.println ("GridBagLayoutInfo:");
1049            System.out.println ("cols: " + info.cols + ", rows: " + info.rows);
1050            System.out.print ("colWidths: ");
1051            dumpArray(info.colWidths);
1052            System.out.print ("rowHeights: ");
1053            dumpArray(info.rowHeights);
1054            System.out.print ("colWeights: ");
1055            dumpArray(info.colWeights);
1056            System.out.print ("rowWeights: ");
1057            dumpArray(info.rowWeights);
1058        }
1059    
1060        private void dumpArray(int[] array)
1061        {
1062            String sep = "";
1063            for(int i = 0; i < array.length; i++)
1064            {
1065                System.out.print(sep);
1066                System.out.print(array[i]);
1067                sep = ", ";
1068            }
1069            System.out.println();
1070        }
1071    
1072        private void dumpArray(double[] array)
1073        {
1074            String sep = "";
1075            for(int i = 0; i < array.length; i++)
1076            {
1077                System.out.print(sep);
1078                System.out.print(array[i]);
1079                sep = ", ";
1080            }
1081            System.out.println();
1082        }
1083      
1084        /**
1085         * @since 1.4
1086         */
1087        protected void arrangeGrid (Container parent)
1088        {
1089          ArrangeGrid (parent);
1090        }
1091    
1092        /**
1093         * @since 1.4
1094         */
1095        protected GridBagLayoutInfo getLayoutInfo (Container parent, int sizeflag)
1096        {
1097          return GetLayoutInfo (parent, sizeflag);
1098        }
1099    
1100        /**
1101         * Move and resize a rectangle according to a set of grid bag
1102         * constraints.  The x, y, width and height fields of the
1103         * rectangle argument are adjusted to the new values.
1104         *
1105         * @param constraints position and size constraints
1106         * @param r rectangle to be moved and resized
1107         *
1108         * @since 1.4
1109         */
1110        protected void adjustForGravity (GridBagConstraints constraints,
1111                                         Rectangle r)
1112        {
1113          AdjustForGravity (constraints, r);
1114        }
1115    }