1 /* GridBagLayout - Layout manager for components according to GridBagConstraints
2    Copyright (C) 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.awt;
40 
41 import java.io.Serializable;
42 import java.util.ArrayList;
43 import java.util.HashMap;
44 import java.util.Hashtable;
45 
46 /**
47  * @author Michael Koch (konqueror@gmx.de)
48  * @author Jeroen Frijters (jeroen@frijters.net)
49  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
50  */
51 public class GridBagLayout
52     implements Serializable, LayoutManager2
53 {
54     private static final long serialVersionUID = 8838754796412211005L;
55 
56     protected static final int MINSIZE = 1;
57     protected static final int PREFERREDSIZE = 2;
58     protected static final int MAXGRIDSIZE = 512;
59 
60     // comptable remembers the original contraints given to us.
61     // internalcomptable is used to keep track of modified constraint values
62     // that we calculate, particularly when we are given RELATIVE and
63     // REMAINDER constraints.
64     // Constraints kept in comptable are never modified, and constraints
65     // kept in internalcomptable can be modified internally only.
66     protected Hashtable<Component,GridBagConstraints> comptable;
67     private Hashtable<Component,GridBagConstraints> internalcomptable;
68     protected GridBagLayoutInfo layoutInfo;
69     protected GridBagConstraints defaultConstraints;
70 
71     public double[] columnWeights;
72     public int[] columnWidths;
73     public double[] rowWeights;
74     public int[] rowHeights;
75 
GridBagLayout()76     public GridBagLayout ()
77     {
78         this.comptable = new Hashtable<Component,GridBagConstraints>();
79         this.internalcomptable = new Hashtable<Component,GridBagConstraints>();
80         this.defaultConstraints= new GridBagConstraints();
81     }
82 
83     /**
84      * Helper method to calc the sum of a range of elements in an int array.
85      */
sumIntArray(int[] array, int upto)86     private int sumIntArray (int[] array, int upto)
87     {
88         int result = 0;
89 
90         for (int i = 0; i < upto; i++)
91             result += array [i];
92 
93         return result;
94     }
95 
96     /**
97      * Helper method to calc the sum of all elements in an int array.
98      */
sumIntArray(int[] array)99     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      */
sumDoubleArray(double[] array)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 
addLayoutComponent(String name, Component component)117     public void addLayoutComponent (String name, Component component)
118     {
119         // do nothing here.
120     }
121 
removeLayoutComponent(Component component)122     public void removeLayoutComponent (Component component)
123     {
124         // do nothing here
125     }
126 
addLayoutComponent(Component component, Object constraints)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 
preferredLayoutSize(Container parent)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 
minimumLayoutSize(Container parent)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 
maximumLayoutSize(Container target)158     public Dimension maximumLayoutSize (Container target)
159     {
160         return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE);
161     }
162 
layoutContainer(Container parent)163     public void layoutContainer (Container parent)
164     {
165       arrangeGrid (parent);
166     }
167 
getLayoutAlignmentX(Container target)168     public float getLayoutAlignmentX (Container target)
169     {
170         return Component.CENTER_ALIGNMENT;
171     }
172 
getLayoutAlignmentY(Container target)173     public float getLayoutAlignmentY (Container target)
174     {
175         return Component.CENTER_ALIGNMENT;
176     }
177 
invalidateLayout(Container target)178     public void invalidateLayout (Container target)
179     {
180         this.layoutInfo = null;
181     }
182 
setConstraints(Component component, GridBagConstraints constraints)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 
getConstraints(Component component)207     public GridBagConstraints getConstraints (Component component)
208     {
209         return (GridBagConstraints) (lookupConstraints (component).clone());
210     }
211 
lookupConstraints(Component component)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 
lookupInternalConstraints(Component component)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      */
getLayoutOrigin()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      */
getLayoutDimensions()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 
getLayoutWeights()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      */
location(int x, int y)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      */
toString()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      */
AdjustForGravity(GridBagConstraints constraints, Rectangle r)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      */
ArrangeGrid(Container parent)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      */
GetLayoutInfo(Container parent, int sizeflag)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      */
GetMinSize(Container parent, GridBagLayoutInfo info)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      */
getMinSize(Container parent, GridBagLayoutInfo info)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      */
sortBySpan(Component component, int span, ArrayList<Component> list, boolean sortByWidth)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      */
distributeSizeAndWeight(int size, double weight, int start, int span, int[] sizes, double[] weights)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      */
calcCellWeights(double weight, double[] weights, int start, int span)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      */
calcCellSizes(int[] sizes, double[] weights, int range)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 
dumpLayoutInfo(GridBagLayoutInfo info)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 
dumpArray(int[] array)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 
dumpArray(double[] array)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      */
arrangeGrid(Container parent)1086     protected void arrangeGrid (Container parent)
1087     {
1088       ArrangeGrid (parent);
1089     }
1090 
1091     /**
1092      * @since 1.4
1093      */
getLayoutInfo(Container parent, int sizeflag)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      */
adjustForGravity(GridBagConstraints constraints, Rectangle r)1109     protected void adjustForGravity (GridBagConstraints constraints,
1110                                      Rectangle r)
1111     {
1112       AdjustForGravity (constraints, r);
1113     }
1114 }
1115