2 * Copyright 2000-2005 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.uiDesigner
.core
;
20 import java
.util
.Arrays
;
22 public final class GridLayoutManager
extends AbstractLayout
{
24 * Minimum size of the cell (row/column). In design mode this constant should be greater than zero
26 private int myMinCellSize
= 20;
28 * Rows' stretches. These are positive integer values. The default value for
29 * any row is <code>1</code>.
31 private final int[] myRowStretches
;
33 * Columns' stretches. These are positive integer values. The default value for
34 * any column is <code>1</code>.
36 private final int[] myColumnStretches
;
38 * Arrays of rows' heights. Method layoutContainer sets this member each time
40 * This is <code>getRowCount()x2</code> two dimensional array. <code>[i][0]</code>
41 * is top <code>y</code> coordinate of row with index <code>i</code>. This <code>y</code>
42 * coordinate is in the container coordinate system.
43 * <code>[i][1]</code> is width of the row with index <code>i</code>.
45 private final int[] myYs
;
46 private final int[] myHeights
;
48 * Arrays of columns' widths. Method layoutContainer sets this member each time
50 * This is <code>getColumnCount()x2</code> two dimensional array. <code>[i][0]</code>
51 * is left <code>x</code> coordinate of row with index <code>i</code>. This <code>x</code>
52 * coordinate is in the container coordinate system.
53 * <code>[i][1]</code> is width of the column with index <code>i</code>.
55 private final int[] myXs
;
56 private final int[] myWidths
;
58 private LayoutState myLayoutState
;
60 * package local because is used in tests
62 DimensionInfo myHorizontalInfo
;
64 * package local because is used in tests
66 DimensionInfo myVerticalInfo
;
68 private boolean mySameSizeHorizontally
;
69 private boolean mySameSizeVertically
;
72 * Key for accessing client property which is set on the root Swing component of the design-time component
73 * hierarchy and specifies the value of extra insets added to all components.
75 public static Object DESIGN_TIME_INSETS
= new Object();
77 private static final int SKIP_ROW
= 1;
78 private static final int SKIP_COL
= 2;
80 public GridLayoutManager(final int rowCount
, final int columnCount
) {
81 if (columnCount
< 1) {
82 throw new IllegalArgumentException("wrong columnCount: " + columnCount
);
85 throw new IllegalArgumentException("wrong rowCount: " + rowCount
);
88 myRowStretches
= new int[rowCount
];
89 for (int i
= 0; i
< rowCount
; i
++) {
90 myRowStretches
[i
] = 1;
92 myColumnStretches
= new int[columnCount
];
93 for (int i
= 0; i
< columnCount
; i
++) {
94 myColumnStretches
[i
] = 1;
97 myXs
= new int[columnCount
];
98 myWidths
= new int[columnCount
];
100 myYs
= new int[rowCount
];
101 myHeights
= new int[rowCount
];
105 * don't delete this constructor! don't use this constructor!!! should be used ONLY in generated code or in tests
107 public GridLayoutManager(final int rowCount
, final int columnCount
, final Insets margin
, final int hGap
, final int vGap
) {
108 this(rowCount
, columnCount
);
116 * don't delete this constructor! don't use this constructor!!! should be used ONLY in generated code or in tests
118 public GridLayoutManager(
120 final int columnCount
,
124 final boolean sameSizeHorizontally
,
125 final boolean sameSizeVertically
127 this(rowCount
, columnCount
, margin
, hGap
, vGap
);
128 mySameSizeHorizontally
= sameSizeHorizontally
;
129 mySameSizeVertically
= sameSizeVertically
;
132 public void addLayoutComponent(final Component comp
, final Object constraints
) {
133 final GridConstraints c
= (GridConstraints
)constraints
;
134 final int row
= c
.getRow();
135 final int rowSpan
= c
.getRowSpan();
136 final int rowCount
= getRowCount();
137 if (row
< 0 || row
>= rowCount
) {
138 throw new IllegalArgumentException("wrong row: " + row
);
140 if (row
+ rowSpan
- 1 >= rowCount
) {
141 throw new IllegalArgumentException("wrong row span: " + rowSpan
+ "; row=" + row
+ " rowCount=" + rowCount
);
143 final int column
= c
.getColumn();
144 final int colSpan
= c
.getColSpan();
145 final int columnCount
= getColumnCount();
146 if (column
< 0 || column
>= columnCount
) {
147 throw new IllegalArgumentException("wrong column: " + column
);
149 if (column
+ colSpan
- 1 >= columnCount
) {
150 throw new IllegalArgumentException(
151 "wrong col span: " + colSpan
+ "; column=" + column
+ " columnCount=" + columnCount
);
153 super.addLayoutComponent(comp
, constraints
);
157 * @return number of rows in the grid.
159 public int getRowCount() {
160 return myRowStretches
.length
;
164 * @return number of columns in the grid.
166 public int getColumnCount() {
167 return myColumnStretches
.length
;
171 * @return vertical stretch for the row with specified index. The returned
172 * values is in range <code>[1..Integer.MAX_VALUE]</code>.
174 public int getRowStretch(final int rowIndex
) {
175 return myRowStretches
[rowIndex
];
179 * @throws IllegalArgumentException if <code>stretch</code> is less
180 * then <code>1</code>.
182 public void setRowStretch(final int rowIndex
, final int stretch
) {
184 throw new IllegalArgumentException("wrong stretch: " + stretch
);
186 myRowStretches
[rowIndex
] = stretch
;
190 * @return maximum horizontal stretch for the component which are located
191 * at the specified column.
193 public int getColumnStretch(final int columnIndex
) {
194 return myColumnStretches
[columnIndex
];
198 * @throws IllegalArgumentException if <code>stretch</code> is less
199 * then <code>1</code>.
201 public void setColumnStretch(final int columnIndex
, final int stretch
) {
203 throw new IllegalArgumentException("wrong stretch: " + stretch
);
205 myColumnStretches
[columnIndex
] = stretch
;
208 public Dimension
maximumLayoutSize(final Container target
) {
209 return new Dimension(Integer
.MAX_VALUE
, Integer
.MAX_VALUE
);
212 public Dimension
minimumLayoutSize(final Container container
) {
213 validateInfos(container
);
215 // IMPORTANT!!! DO NOT INLINE!!!
216 final DimensionInfo horizontalInfo
= myHorizontalInfo
;
217 final DimensionInfo verticalInfo
= myVerticalInfo
;
219 final Dimension result
= getTotalGap(container
, horizontalInfo
, verticalInfo
);
221 final int[] widths
= getMinSizes(horizontalInfo
);
222 if (mySameSizeHorizontally
) {
223 makeSameSizes(widths
);
225 result
.width
+= sum(widths
);
227 final int[] heights
= getMinSizes(verticalInfo
);
228 if (mySameSizeVertically
) {
229 makeSameSizes(heights
);
231 result
.height
+= sum(heights
);
236 private static void makeSameSizes(int[] widths
) {
238 for (int i
= 0; i
< widths
.length
; i
++) {
239 int width
= widths
[i
];
240 max
= Math
.max(width
, max
);
243 for (int i
= 0; i
< widths
.length
; i
++) {
248 private static int[] getSameSizes(DimensionInfo info
, int totalWidth
) {
249 int[] widths
= new int[info
.getCellCount()];
251 int average
= totalWidth
/ widths
.length
;
252 int rest
= totalWidth
% widths
.length
;
254 for (int i
= 0; i
< widths
.length
; i
++) {
265 public Dimension
preferredLayoutSize(final Container container
) {
266 validateInfos(container
);
268 // IMPORTANT!!! DO NOT INLINE!!!
269 final DimensionInfo horizontalInfo
= myHorizontalInfo
;
270 final DimensionInfo verticalInfo
= myVerticalInfo
;
272 final Dimension result
= getTotalGap(container
, horizontalInfo
, verticalInfo
);
274 final int[] widths
= getPrefSizes(horizontalInfo
);
275 if (mySameSizeHorizontally
) {
276 makeSameSizes(widths
);
278 result
.width
+= sum(widths
);
280 final int[] heights
= getPrefSizes(verticalInfo
);
281 if (mySameSizeVertically
) {
282 makeSameSizes(heights
);
284 result
.height
+= sum(heights
);
289 private static int sum(final int[] ints
) {
291 for (int i
= ints
.length
- 1; i
>= 0; i
--) {
297 private Dimension
getTotalGap(final Container container
, final DimensionInfo hInfo
, final DimensionInfo vInfo
) {
298 final Insets insets
= getInsets(container
);
299 return new Dimension(
300 insets
.left
+ insets
.right
+ countGap(hInfo
, 0, hInfo
.getCellCount()) + myMargin
.left
+ myMargin
.right
,
301 insets
.top
+ insets
.bottom
+ countGap(vInfo
, 0, vInfo
.getCellCount()) + myMargin
.top
+ myMargin
.bottom
);
304 private static int getDesignTimeInsets(Container container
) {
305 while(container
!= null) {
306 if (container
instanceof JComponent
) {
307 Integer designTimeInsets
= (Integer
)((JComponent
) container
).getClientProperty(DESIGN_TIME_INSETS
);
308 if (designTimeInsets
!= null) {
309 return designTimeInsets
.intValue();
312 container
= container
.getParent();
317 private static Insets
getInsets(Container container
) {
318 final Insets insets
= container
.getInsets();
319 int insetsValue
= getDesignTimeInsets(container
);
320 if (insetsValue
!= 0) {
321 return new Insets(insets
.top
+insetsValue
, insets
.left
+insetsValue
,
322 insets
.bottom
+insetsValue
, insets
.right
+insetsValue
);
327 private static int countGap(final DimensionInfo info
, final int startCell
, final int cellCount
) {
329 for (int cellIndex
= startCell
+ cellCount
- 2 /*gap after last cell should not be counted*/;
330 cellIndex
>= startCell
;
332 if (shouldAddGapAfterCell(info
, cellIndex
)) {
336 return counter
* info
.getGap();
339 private static boolean shouldAddGapAfterCell(final DimensionInfo info
, final int cellIndex
) {
340 if (cellIndex
< 0 || cellIndex
>= info
.getCellCount()) {
341 throw new IllegalArgumentException("wrong cellIndex: " + cellIndex
+ "; cellCount=" + info
.getCellCount());
344 boolean endsInThis
= false;
345 boolean startsInNext
= false;
347 int indexOfNextNotEmpty
= -1;
348 for (int i
= cellIndex
+ 1; i
< info
.getCellCount(); i
++) {
349 if (!isCellEmpty(info
, i
)) {
350 indexOfNextNotEmpty
= i
;
355 for (int i
= 0; i
< info
.getComponentCount(); i
++) {
356 final Component component
= info
.getComponent(i
);
357 if (component
instanceof Spacer
) {
361 if (info
.getCell(i
) == indexOfNextNotEmpty
) {
365 if (info
.getCell(i
) + info
.getSpan(i
) - 1 == cellIndex
) {
370 return startsInNext
&& endsInThis
;
373 private static boolean isCellEmpty(final DimensionInfo info
, final int cellIndex
) {
374 if (cellIndex
< 0 || cellIndex
>= info
.getCellCount()) {
375 throw new IllegalArgumentException("wrong cellIndex: " + cellIndex
+ "; cellCount=" + info
.getCellCount());
377 for (int i
= 0; i
< info
.getComponentCount(); i
++) {
378 final Component component
= info
.getComponent(i
);
379 if (info
.getCell(i
) == cellIndex
&& !(component
instanceof Spacer
)) {
386 public void layoutContainer(final Container container
) {
387 validateInfos(container
);
389 // IMPORTANT!!! DO NOT INLINE!!!
390 final LayoutState layoutState
= myLayoutState
;
391 final DimensionInfo horizontalInfo
= myHorizontalInfo
;
392 final DimensionInfo verticalInfo
= myVerticalInfo
;
394 Insets insets
= getInsets(container
);
396 int skipLayout
= checkSetSizesFromParent(container
, insets
);
398 final Dimension gap
= getTotalGap(container
, horizontalInfo
, verticalInfo
);
400 final Dimension size
= container
.getSize();
401 size
.width
-= gap
.width
;
402 size
.height
-= gap
.height
;
404 final Dimension prefSize
= preferredLayoutSize(container
);
405 prefSize
.width
-= gap
.width
;
406 prefSize
.height
-= gap
.height
;
408 final Dimension minSize
= minimumLayoutSize(container
);
409 minSize
.width
-= gap
.width
;
410 minSize
.height
-= gap
.height
;
412 // Calculate rows' heights
413 if ((skipLayout
& SKIP_ROW
) == 0) {
415 if (mySameSizeVertically
) {
416 heights
= getSameSizes(verticalInfo
, Math
.max(size
.height
, minSize
.height
));
419 if (size
.height
< prefSize
.height
) {
420 heights
= getMinSizes(verticalInfo
);
421 new_doIt(heights
, 0, verticalInfo
.getCellCount(), size
.height
, verticalInfo
, true);
424 heights
= getPrefSizes(verticalInfo
);
425 new_doIt(heights
, 0, verticalInfo
.getCellCount(), size
.height
, verticalInfo
, false);
429 // Calculate rows' bounds
430 int y
= insets
.top
+ myMargin
.top
;
431 for (int i
= 0; i
< heights
.length
; i
++) {
433 myHeights
[i
] = heights
[i
];
435 if (shouldAddGapAfterCell(verticalInfo
, i
)) {
436 y
+= verticalInfo
.getGap();
441 if ((skipLayout
& SKIP_COL
) == 0) {
442 // Calculate columns' widths
444 if (mySameSizeHorizontally
) {
445 widths
= getSameSizes(horizontalInfo
, Math
.max(size
.width
, minSize
.width
));
448 if (size
.width
< prefSize
.width
) {
449 widths
= getMinSizes(horizontalInfo
);
450 new_doIt(widths
, 0, horizontalInfo
.getCellCount(), size
.width
, horizontalInfo
, true);
453 widths
= getPrefSizes(horizontalInfo
);
454 new_doIt(widths
, 0, horizontalInfo
.getCellCount(), size
.width
, horizontalInfo
, false);
458 // Calculate columns' bounds
459 int x
= insets
.left
+ myMargin
.left
;
460 for (int i
= 0; i
< widths
.length
; i
++) {
462 myWidths
[i
] = widths
[i
];
464 if (shouldAddGapAfterCell(horizontalInfo
, i
)) {
465 x
+= horizontalInfo
.getGap();
470 // Set bounds of components
471 for (int i
= 0; i
< layoutState
.getComponentCount(); i
++) {
472 final GridConstraints c
= layoutState
.getConstraints(i
);
473 final Component component
= layoutState
.getComponent(i
);
475 final int column
= horizontalInfo
.getCell(i
);
476 final int colSpan
= horizontalInfo
.getSpan(i
);
477 final int row
= verticalInfo
.getCell(i
);
478 final int rowSpan
= verticalInfo
.getSpan(i
);
480 final int cellWidth
= myXs
[column
+ colSpan
- 1] + myWidths
[column
+ colSpan
- 1] - myXs
[column
];
481 final int cellHeight
= myYs
[row
+ rowSpan
- 1] + myHeights
[row
+ rowSpan
- 1] - myYs
[row
];
483 final Dimension componentSize
= new Dimension(cellWidth
, cellHeight
);
485 if ((c
.getFill() & GridConstraints
.FILL_HORIZONTAL
) == 0) {
486 componentSize
.width
= Math
.min(componentSize
.width
, horizontalInfo
.getPreferredWidth(i
));
489 if ((c
.getFill() & GridConstraints
.FILL_VERTICAL
) == 0) {
490 componentSize
.height
= Math
.min(componentSize
.height
, verticalInfo
.getPreferredWidth(i
));
493 Util
.adjustSize(component
, c
, componentSize
);
498 if ((c
.getAnchor() & GridConstraints
.ANCHOR_EAST
) != 0) {
499 dx
= cellWidth
- componentSize
.width
;
501 else if ((c
.getAnchor() & GridConstraints
.ANCHOR_WEST
) == 0) {
502 dx
= (cellWidth
- componentSize
.width
) / 2;
505 if ((c
.getAnchor() & GridConstraints
.ANCHOR_SOUTH
) != 0) {
506 dy
= cellHeight
- componentSize
.height
;
508 else if ((c
.getAnchor() & GridConstraints
.ANCHOR_NORTH
) == 0) {
509 dy
= (cellHeight
- componentSize
.height
) / 2;
512 int indent
= Util
.DEFAULT_INDENT
* c
.getIndent();
513 componentSize
.width
-= indent
;
516 component
.setBounds(myXs
[column
] + dx
, myYs
[row
] + dy
, componentSize
.width
, componentSize
.height
);
520 private int checkSetSizesFromParent(final Container container
, final Insets insets
) {
523 GridLayoutManager parentGridLayout
= null;
524 GridConstraints parentGridConstraints
= null;
525 // "use parent layout" also needs to work in cases where a grid is the only control in a non-grid panel
526 // which is contained in a grid
527 Container parent
= container
.getParent();
528 if (parent
!= null) {
529 if (parent
.getLayout() instanceof GridLayoutManager
) {
530 parentGridLayout
= (GridLayoutManager
) parent
.getLayout();
531 parentGridConstraints
= parentGridLayout
.getConstraintsForComponent(container
);
534 Container parent2
= parent
.getParent();
535 if (parent2
!= null && parent2
.getLayout() instanceof GridLayoutManager
) {
536 parentGridLayout
= (GridLayoutManager
) parent2
.getLayout();
537 parentGridConstraints
= parentGridLayout
.getConstraintsForComponent(parent
);
542 if (parentGridLayout
!= null && parentGridConstraints
.isUseParentLayout()) {
543 if (myRowStretches
.length
== parentGridConstraints
.getRowSpan()) {
544 int row
= parentGridConstraints
.getRow();
545 myYs
[0] = insets
.top
+ myMargin
.top
;
546 myHeights
[0] = parentGridLayout
.myHeights
[row
] - myYs
[0];
547 for (int i
= 1; i
< myRowStretches
.length
; i
++) {
548 myYs
[i
] = parentGridLayout
.myYs
[i
+ row
] - parentGridLayout
.myYs
[row
];
549 myHeights
[i
] = parentGridLayout
.myHeights
[i
+ row
];
551 myHeights
[myRowStretches
.length
- 1] -= insets
.bottom
+ myMargin
.bottom
;
552 skipLayout
|= SKIP_ROW
;
554 if (myColumnStretches
.length
== parentGridConstraints
.getColSpan()) {
555 int col
= parentGridConstraints
.getColumn();
556 myXs
[0] = insets
.left
+ myMargin
.left
;
557 myWidths
[0] = parentGridLayout
.myWidths
[col
] - myXs
[0];
558 for (int i
= 1; i
< myColumnStretches
.length
; i
++) {
559 myXs
[i
] = parentGridLayout
.myXs
[i
+ col
] - parentGridLayout
.myXs
[col
];
560 myWidths
[i
] = parentGridLayout
.myWidths
[i
+ col
];
562 myWidths
[myColumnStretches
.length
- 1] -= insets
.right
+ myMargin
.right
;
563 skipLayout
|= SKIP_COL
;
569 public void invalidateLayout(final Container container
) {
570 myLayoutState
= null;
571 myHorizontalInfo
= null;
572 myVerticalInfo
= null;
575 void validateInfos(final Container container
) {
576 if (myLayoutState
== null) {
577 // TODO[yole]: Implement cleaner way of determining whether invisible components should be ignored
578 myLayoutState
= new LayoutState(this, getDesignTimeInsets(container
) == 0);
579 myHorizontalInfo
= new HorizontalInfo(myLayoutState
, getHGapImpl(container
));
580 myVerticalInfo
= new VerticalInfo(myLayoutState
, getVGapImpl(container
));
585 * for design time only
587 public int[] getXs() {
592 * for design time only
594 public int[] getWidths() {
599 * for design time only
601 public int[] getYs() {
606 * for design time only
608 public int[] getHeights() {
612 public int[] getCoords(boolean isRow
) {
613 return isRow ? myYs
: myXs
;
616 public int[] getSizes(boolean isRow
) {
617 return isRow ? myHeights
: myWidths
;
621 * @return index of the row that contains point with <code>y</code> coordinate.
622 * If <code>y</code> doesn't belong to any row then the method returns <code>-1</code>.
623 * Note, that <code>y</code> is in <code>group</code> coordinate system.
625 public int getRowAt(final int y
) {
626 for (int i
= 0; i
< myYs
.length
; i
++) {
627 if (myYs
[i
] <= y
&& y
<= myYs
[i
] + myHeights
[i
]) {
635 * @return index of the column that contains point with <code>x</code> coordinate.
636 * Note, that <code>x</code> is in <code>group</code> coordinate system.
638 public int getColumnAt(final int x
) {
639 for (int i
= 0; i
< myXs
.length
; i
++) {
640 if (myXs
[i
] <= x
&& x
<= myXs
[i
] + myWidths
[i
]) {
647 private int[] getMinSizes(final DimensionInfo info
) {
648 return getMinOrPrefSizes(info
, true);
651 private int[] getPrefSizes(final DimensionInfo info
) {
652 return getMinOrPrefSizes(info
, false);
655 private int[] getMinOrPrefSizes(final DimensionInfo info
, final boolean min
) {
656 final int[] widths
= new int[info
.getCellCount()];
657 for (int i
= 0; i
< widths
.length
; i
++) {
658 widths
[i
] = myMinCellSize
;
661 // single spaned components
662 for (int i
= info
.getComponentCount() - 1; i
>= 0; i
--) {
663 if (info
.getSpan(i
) != 1) {
667 int size
= min ?
getMin2(info
, i
) : Math
.max(info
.getMinimumWidth(i
), info
.getPreferredWidth(i
));
668 final int gap
= countGap(info
, info
.getCell(i
), info
.getSpan(i
));
669 size
= Math
.max(size
- gap
, 0);
671 widths
[info
.getCell(i
)] = Math
.max(widths
[info
.getCell(i
)], size
);
674 // components inheriting layout from us
675 updateSizesFromChildren(info
, min
, widths
);
677 // multispanned components
679 final boolean[] toProcess
= new boolean[info
.getCellCount()];
681 for (int i
= info
.getComponentCount() - 1; i
>= 0; i
--) {
682 int size
= min ?
getMin2(info
, i
) : Math
.max(info
.getMinimumWidth(i
), info
.getPreferredWidth(i
));
684 final int span
= info
.getSpan(i
);
685 final int cell
= info
.getCell(i
);
687 final int gap
= countGap(info
, cell
, span
);
688 size
= Math
.max(size
- gap
, 0);
690 Arrays
.fill(toProcess
, false);
693 for (int j
=0; j
< span
; j
++){
694 curSize
+= widths
[j
+ cell
];
695 toProcess
[j
+ cell
] = true;
698 if (curSize
>= size
) {
702 final boolean[] higherPriorityCells
= new boolean[toProcess
.length
];
703 getCellsWithHigherPriorities(info
, toProcess
, higherPriorityCells
, false, widths
);
705 distribute(higherPriorityCells
, info
, size
- curSize
, widths
);
711 private static void updateSizesFromChildren(final DimensionInfo info
, final boolean min
, final int[] widths
) {
712 for(int i
=info
.getComponentCount() - 1; i
>= 0; i
--) {
713 Component child
= info
.getComponent(i
);
714 GridConstraints c
= info
.getConstraints(i
);
715 if (c
.isUseParentLayout() && child
instanceof Container
) {
716 Container container
= (Container
) child
;
717 if (container
.getLayout() instanceof GridLayoutManager
) {
718 updateSizesFromChild(info
, min
, widths
, container
, i
);
720 else if (container
.getComponentCount() == 1 && container
.getComponent(0) instanceof Container
) {
721 // "use parent layout" also needs to work in cases where a grid is the only control in a non-grid panel
722 // which is contained in a grid
723 Container childContainer
= (Container
) container
.getComponent(0);
724 if (childContainer
.getLayout() instanceof GridLayoutManager
) {
725 updateSizesFromChild(info
, min
, widths
, childContainer
, i
);
732 private static void updateSizesFromChild(final DimensionInfo info
,
735 final Container container
,
736 final int childIndex
) {
737 GridLayoutManager childLayout
= (GridLayoutManager
) container
.getLayout();
738 if (info
.getSpan(childIndex
) == info
.getChildLayoutCellCount(childLayout
)) {
739 childLayout
.validateInfos(container
);
740 DimensionInfo childInfo
= (info
instanceof HorizontalInfo
)
741 ? childLayout
.myHorizontalInfo
742 : childLayout
.myVerticalInfo
;
743 int[] sizes
= childLayout
.getMinOrPrefSizes(childInfo
, min
);
744 int cell
= info
.getCell(childIndex
);
745 for(int j
=0; j
<sizes
.length
; j
++) {
746 widths
[cell
+j
] = Math
.max(widths
[cell
+j
], sizes
[j
]);
752 private static int getMin2(final DimensionInfo info
, final int componentIndex
) {
754 if ((info
.getSizePolicy(componentIndex
) & GridConstraints
.SIZEPOLICY_CAN_SHRINK
) != 0) {
755 s
= info
.getMinimumWidth(componentIndex
);
758 // it might be possible that minSize > prefSize (for example, only min is set in constraints to 100 and
759 // JComponent's preferred size returned is 20)
760 s
= Math
.max(info
.getMinimumWidth(componentIndex
), info
.getPreferredWidth(componentIndex
));
766 * @param widths in/out parameter
768 private void new_doIt(final int[] widths
, final int cell
, final int span
, final int minWidth
, final DimensionInfo info
, final boolean checkPrefs
) {
769 int toDistribute
= minWidth
;
771 for (int i
= cell
; i
< cell
+ span
; i
++) {
772 toDistribute
-= widths
[i
];
774 if (toDistribute
<= 0) {
778 final boolean[] allowedCells
= new boolean[info
.getCellCount()];
779 for (int i
= cell
; i
< cell
+ span
; i
++) {
780 allowedCells
[i
] = true;
783 final boolean[] higherPriorityCells
= new boolean[info
.getCellCount()];
784 getCellsWithHigherPriorities(info
, allowedCells
, higherPriorityCells
, checkPrefs
, widths
);
786 distribute(higherPriorityCells
, info
, toDistribute
, widths
);
789 private static void distribute(final boolean[] higherPriorityCells
, final DimensionInfo info
, int toDistribute
, final int[] widths
) {
791 for (int i
= 0; i
< info
.getCellCount(); i
++) {
792 if (higherPriorityCells
[i
]) {
793 stretches
+= info
.getStretch(i
);
798 final int toDistributeFrozen
= toDistribute
;
799 for (int i
= 0; i
< info
.getCellCount(); i
++) {
800 if (!higherPriorityCells
[i
]) {
804 final int addon
= toDistributeFrozen
* info
.getStretch(i
) / stretches
;
807 toDistribute
-= addon
;
811 if (toDistribute
!= 0) {
812 for (int i
= 0; i
< info
.getCellCount(); i
++) {
813 if (!higherPriorityCells
[i
]) {
820 if (toDistribute
== 0) {
826 if (toDistribute
!= 0) {
827 throw new IllegalStateException("toDistribute = " + toDistribute
);
831 private void getCellsWithHigherPriorities(
832 final DimensionInfo info
,
833 final boolean[] allowedCells
,
834 final boolean[] higherPriorityCells
,
835 final boolean checkPrefs
,
838 Arrays
.fill(higherPriorityCells
, false);
843 // less that preferred size
844 final int[] prefs
= getMinOrPrefSizes(info
, false);
845 for (int cell
= 0; cell
< allowedCells
.length
; cell
++) {
846 if (!allowedCells
[cell
]) {
849 if (!isCellEmpty(info
, cell
) && prefs
[cell
] > widths
[cell
]) {
850 higherPriorityCells
[cell
] = true;
855 if (foundCells
> 0) {
861 for (int cell
= 0; cell
< allowedCells
.length
; cell
++) {
862 if (!allowedCells
[cell
]) {
865 if ((info
.getCellSizePolicy(cell
) & GridConstraints
.SIZEPOLICY_WANT_GROW
) != 0) {
866 higherPriorityCells
[cell
] = true;
871 if (foundCells
> 0) {
876 for (int cell
= 0; cell
< allowedCells
.length
; cell
++) {
877 if (!allowedCells
[cell
]) {
880 if ((info
.getCellSizePolicy(cell
) & GridConstraints
.SIZEPOLICY_CAN_GROW
) != 0) {
881 higherPriorityCells
[cell
] = true;
886 if (foundCells
> 0) {
891 for (int cell
= 0; cell
< allowedCells
.length
; cell
++) {
892 if (!allowedCells
[cell
]) {
895 if (!isCellEmpty(info
, cell
)) {
896 higherPriorityCells
[cell
] = true;
901 if (foundCells
> 0) {
906 for (int cell
= 0; cell
< allowedCells
.length
; cell
++) {
907 if (!allowedCells
[cell
]) {
910 higherPriorityCells
[cell
] = true;
914 public boolean isSameSizeHorizontally() {
915 return mySameSizeHorizontally
;
918 public boolean isSameSizeVertically() {
919 return mySameSizeVertically
;
922 public void setSameSizeHorizontally(boolean sameSizeHorizontally
) {
923 mySameSizeHorizontally
= sameSizeHorizontally
;
926 public void setSameSizeVertically(boolean sameSizeVertically
) {
927 mySameSizeVertically
= sameSizeVertically
;
930 public int[] getHorizontalGridLines() {
931 int[] result
= new int [myYs
.length
+1];
932 result
[0] = myYs
[0];
933 for(int i
=0; i
<myYs
.length
-1; i
++) {
934 result
[i
+1] = (myYs
[i
] + myHeights
[i
] + myYs
[i
+ 1]) / 2;
936 result
[myYs
.length
] = myYs
[myYs
.length
-1] + myHeights
[myYs
.length
-1];
940 public int[] getVerticalGridLines() {
941 int[] result
= new int [myXs
.length
+1];
942 result
[0] = myXs
[0];
943 for(int i
=0; i
<myXs
.length
-1; i
++) {
944 result
[i
+1] = (myXs
[i
] + myWidths
[i
] + myXs
[i
+ 1]) / 2;
946 result
[myXs
.length
] = myXs
[myXs
.length
-1] + myWidths
[myXs
.length
-1];
950 public int getHorizontalGridLineNear(int y
, int epsilon
) {
951 return getGridLineNear(y
, epsilon
, getHorizontalGridLines());
954 public int getVerticalGridLineNear(int x
, int epsilon
) {
955 return getGridLineNear(x
, epsilon
, getVerticalGridLines());
958 private static int getGridLineNear(final int coord
, final int epsilon
, final int[] gridLines
) {
959 for(int col
= 1; col
<gridLines
.length
; col
++) {
960 if (coord
< gridLines
[col
]) {
961 if (coord
- gridLines
[col
-1] < epsilon
) {
964 if (gridLines
[col
] - coord
< epsilon
) {
970 if (coord
- gridLines
[gridLines
.length
-1] < epsilon
) {
971 return gridLines
.length
-1;
976 public Rectangle
getCellRangeRect(final int startRow
, final int startCol
, final int endRow
, final int endCol
) {
977 return new Rectangle(myXs
[startCol
],
979 myXs
[endCol
] + myWidths
[endCol
] - myXs
[startCol
],
980 myYs
[endRow
] + myHeights
[endRow
] - myYs
[startRow
]);
984 public Rectangle
getCellRangeGridlineRect(final int startRow
, final int startCol
, final int endRow
, final int endCol
) {
985 int[] horzGridLines
= getHorizontalGridLines();
986 int[] vertGridLines
= getVerticalGridLines();
988 return new Rectangle(vertGridLines
[startCol
],
989 horzGridLines
[startRow
],
990 vertGridLines
[endCol
+1] - vertGridLines
[startCol
],
991 horzGridLines
[endRow
+1] - horzGridLines
[startRow
]);
994 public int getCellCount(final boolean isRow
) {
995 return isRow ?
getRowCount() : getColumnCount();