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