[yole] correct size policy for columns which contain only components with child layout
[fedora-idea.git] / forms_rt / src / com / intellij / uiDesigner / core / GridLayoutManager.java
blobeb8b6bb7d26aef658a63ebf616d6447b56b53eae
1 /*
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;
18 import javax.swing.*;
19 import java.awt.*;
20 import java.util.Arrays;
22 public final class GridLayoutManager extends AbstractLayout {
23 /**
24 * Minimum size of the cell (row/column). In design mode this constant should be greater than zero
26 private int myMinCellSize = 20;
27 /**
28 * Rows' stretches. These are positive integer values. The default value for
29 * any row is <code>1</code>.
31 private final int[] myRowStretches;
32 /**
33 * Columns' stretches. These are positive integer values. The default value for
34 * any column is <code>1</code>.
36 private final int[] myColumnStretches;
37 /**
38 * Arrays of rows' heights. Method layoutContainer sets this member each time
39 * it's invoked.
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;
47 /**
48 * Arrays of columns' widths. Method layoutContainer sets this member each time
49 * it's invoked.
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;
59 /**
60 * package local because is used in tests
62 DimensionInfo myHorizontalInfo;
63 /**
64 * package local because is used in tests
66 DimensionInfo myVerticalInfo;
68 private boolean mySameSizeHorizontally;
69 private boolean mySameSizeVertically;
71 /**
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);
84 if (rowCount < 1) {
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);
109 setMargin(margin);
110 setHGap(hGap);
111 setVGap(vGap);
112 myMinCellSize = 0;
116 * don't delete this constructor! don't use this constructor!!! should be used ONLY in generated code or in tests
118 public GridLayoutManager(
119 final int rowCount,
120 final int columnCount,
121 final Insets margin,
122 final int hGap,
123 final int vGap,
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) {
183 if (stretch < 1) {
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) {
202 if (stretch < 1) {
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);
233 return result;
236 private static void makeSameSizes(int[] widths) {
237 int max = widths[0];
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++) {
244 widths[i] = max;
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++) {
255 widths[i] = average;
256 if (rest > 0) {
257 widths[i]++;
258 rest--;
262 return widths;
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);
286 return result;
289 private static int sum(final int[] ints) {
290 int result = 0;
291 for (int i = ints.length - 1; i >= 0; i--) {
292 result += ints[i];
294 return result;
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();
314 return 0;
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);
324 return insets;
327 private static int countGap(final DimensionInfo info, final int startCell, final int cellCount) {
328 int counter = 0;
329 for (int cellIndex = startCell + cellCount - 2 /*gap after last cell should not be counted*/;
330 cellIndex >= startCell;
331 cellIndex--) {
332 if (shouldAddGapAfterCell(info, cellIndex)) {
333 counter++;
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;
351 break;
355 for (int i = 0; i < info.getComponentCount(); i++) {
356 final Component component = info.getComponent(i);
357 if (component instanceof Spacer) {
358 continue;
361 if (info.getCell(i) == indexOfNextNotEmpty) {
362 startsInNext = true;
365 if (info.getCell(i) + info.getSpan(i) - 1 == cellIndex) {
366 endsInThis = true;
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)) {
380 return false;
383 return true;
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) {
414 final int[] heights;
415 if (mySameSizeVertically) {
416 heights = getSameSizes(verticalInfo, Math.max(size.height, minSize.height));
418 else {
419 if (size.height < prefSize.height) {
420 heights = getMinSizes(verticalInfo);
421 new_doIt(heights, 0, verticalInfo.getCellCount(), size.height, verticalInfo, true);
423 else {
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++) {
432 myYs[i] = y;
433 myHeights[i] = heights[i];
434 y += heights[i];
435 if (shouldAddGapAfterCell(verticalInfo, i)) {
436 y += verticalInfo.getGap();
441 if ((skipLayout & SKIP_COL) == 0) {
442 // Calculate columns' widths
443 final int[] widths;
444 if (mySameSizeHorizontally) {
445 widths = getSameSizes(horizontalInfo, Math.max(size.width, minSize.width));
447 else {
448 if (size.width < prefSize.width) {
449 widths = getMinSizes(horizontalInfo);
450 new_doIt(widths, 0, horizontalInfo.getCellCount(), size.width, horizontalInfo, true);
452 else {
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++) {
461 myXs[i] = x;
462 myWidths[i] = widths[i];
463 x += 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);
495 int dx = 0;
496 int dy = 0;
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;
514 dx += indent;
516 component.setBounds(myXs[column] + dx, myYs[row] + dy, componentSize.width, componentSize.height);
520 private int checkSetSizesFromParent(final Container container, final Insets insets) {
521 int skipLayout = 0;
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);
533 else {
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;
566 return skipLayout;
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() {
588 return myXs;
592 * for design time only
594 public int[] getWidths() {
595 return myWidths;
599 * for design time only
601 public int[] getYs() {
602 return myYs;
606 * for design time only
608 public int[] getHeights() {
609 return myHeights;
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]) {
628 return i;
631 return -1;
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]) {
641 return i;
644 return -1;
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) {
664 continue;
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);
692 int curSize = 0;
693 for (int j=0; j < span; j++){
694 curSize += widths[j + cell];
695 toProcess[j + cell] = true;
698 if (curSize >= size) {
699 continue;
702 final boolean[] higherPriorityCells = new boolean[toProcess.length];
703 getCellsWithHigherPriorities(info, toProcess, higherPriorityCells, false, widths);
705 distribute(higherPriorityCells, info, size - curSize, widths);
708 return 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,
733 final boolean min,
734 final int[] widths,
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) {
753 final int s;
754 if ((info.getSizePolicy(componentIndex) & GridConstraints.SIZEPOLICY_CAN_SHRINK) != 0) {
755 s = info.getMinimumWidth(componentIndex);
757 else {
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));
762 return s;
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) {
775 return;
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) {
790 int stretches = 0;
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]) {
801 continue;
804 final int addon = toDistributeFrozen * info.getStretch(i) / stretches;
805 widths[i] += addon;
807 toDistribute -= addon;
811 if (toDistribute != 0) {
812 for (int i = 0; i < info.getCellCount(); i++) {
813 if (!higherPriorityCells[i]) {
814 continue;
817 widths[i]++;
818 toDistribute--;
820 if (toDistribute == 0) {
821 break;
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,
836 final int[] widths
838 Arrays.fill(higherPriorityCells, false);
840 int foundCells = 0;
842 if (checkPrefs) {
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]) {
847 continue;
849 if (!isCellEmpty(info, cell) && prefs[cell] > widths[cell]) {
850 higherPriorityCells[cell] = true;
851 foundCells++;
855 if (foundCells > 0) {
856 return;
860 // want grow
861 for (int cell = 0; cell < allowedCells.length; cell++) {
862 if (!allowedCells[cell]) {
863 continue;
865 if ((info.getCellSizePolicy(cell) & GridConstraints.SIZEPOLICY_WANT_GROW) != 0) {
866 higherPriorityCells[cell] = true;
867 foundCells++;
871 if (foundCells > 0) {
872 return;
875 // can grow
876 for (int cell = 0; cell < allowedCells.length; cell++) {
877 if (!allowedCells[cell]) {
878 continue;
880 if ((info.getCellSizePolicy(cell) & GridConstraints.SIZEPOLICY_CAN_GROW) != 0) {
881 higherPriorityCells[cell] = true;
882 foundCells++;
886 if (foundCells > 0) {
887 return;
890 // non empty
891 for (int cell = 0; cell < allowedCells.length; cell++) {
892 if (!allowedCells[cell]) {
893 continue;
895 if (!isCellEmpty(info, cell)) {
896 higherPriorityCells[cell] = true;
897 foundCells++;
901 if (foundCells > 0) {
902 return;
905 // any
906 for (int cell = 0; cell < allowedCells.length; cell++) {
907 if (!allowedCells[cell]) {
908 continue;
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];
937 return result;
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];
947 return result;
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) {
962 return col-1;
964 if (gridLines [col] - coord < epsilon) {
965 return col;
967 return -1;
970 if (coord - gridLines [gridLines.length-1] < epsilon) {
971 return gridLines.length-1;
973 return -1;
976 public Rectangle getCellRangeRect(final int startRow, final int startCol, final int endRow, final int endCol) {
977 return new Rectangle(myXs[startCol],
978 myYs[startRow],
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();