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 }