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 = 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    }