/* * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.uiDesigner.core; import javax.swing.*; import java.awt.*; import java.util.Arrays; public final class GridLayoutManager extends AbstractLayout { /** * Minimum size of the cell (row/column). In design mode this constant should be greater than zero */ private int myMinCellSize = 20; /** * Rows' stretches. These are positive integer values. The default value for * any row is <code>1</code>. */ private final int[] myRowStretches; /** * Columns' stretches. These are positive integer values. The default value for * any column is <code>1</code>. */ private final int[] myColumnStretches; /** * Arrays of rows' heights. Method layoutContainer sets this member each time * it's invoked. * This is <code>getRowCount()x2</code> two dimensional array. <code>[i][0]</code> * is top <code>y</code> coordinate of row with index <code>i</code>. This <code>y</code> * coordinate is in the container coordinate system. * <code>[i][1]</code> is width of the row with index <code>i</code>. */ private final int[] myYs; private final int[] myHeights; /** * Arrays of columns' widths. Method layoutContainer sets this member each time * it's invoked. * This is <code>getColumnCount()x2</code> two dimensional array. <code>[i][0]</code> * is left <code>x</code> coordinate of row with index <code>i</code>. This <code>x</code> * coordinate is in the container coordinate system. * <code>[i][1]</code> is width of the column with index <code>i</code>. */ private final int[] myXs; private final int[] myWidths; private LayoutState myLayoutState; /** * package-private because is used in tests */ DimensionInfo myHorizontalInfo; /** * package-private because is used in tests */ DimensionInfo myVerticalInfo; private boolean mySameSizeHorizontally; private boolean mySameSizeVertically; /** * Key for accessing client property which is set on the root Swing component of the design-time component * hierarchy and specifies the value of extra insets added to all components. */ public static Object DESIGN_TIME_INSETS = new Object(); private static final int SKIP_ROW = 1; private static final int SKIP_COL = 2; public GridLayoutManager(final int rowCount, final int columnCount) { if (columnCount < 1) { throw new IllegalArgumentException("wrong columnCount: " + columnCount); } if (rowCount < 1) { throw new IllegalArgumentException("wrong rowCount: " + rowCount); } myRowStretches = new int[rowCount]; for (int i = 0; i < rowCount; i++) { myRowStretches[i] = 1; } myColumnStretches = new int[columnCount]; for (int i = 0; i < columnCount; i++) { myColumnStretches[i] = 1; } myXs = new int[columnCount]; myWidths = new int[columnCount]; myYs = new int[rowCount]; myHeights = new int[rowCount]; } /** * don't delete this constructor! don't use this constructor!!! should be used ONLY in generated code or in tests */ public GridLayoutManager(final int rowCount, final int columnCount, final Insets margin, final int hGap, final int vGap) { this(rowCount, columnCount); setMargin(margin); setHGap(hGap); setVGap(vGap); myMinCellSize = 0; } /** * don't delete this constructor! don't use this constructor!!! should be used ONLY in generated code or in tests */ public GridLayoutManager( final int rowCount, final int columnCount, final Insets margin, final int hGap, final int vGap, final boolean sameSizeHorizontally, final boolean sameSizeVertically ) { this(rowCount, columnCount, margin, hGap, vGap); mySameSizeHorizontally = sameSizeHorizontally; mySameSizeVertically = sameSizeVertically; } public void addLayoutComponent(final Component comp, final Object constraints) { final GridConstraints c = (GridConstraints)constraints; final int row = c.getRow(); final int rowSpan = c.getRowSpan(); final int rowCount = getRowCount(); if (row < 0 || row >= rowCount) { throw new IllegalArgumentException("wrong row: " + row); } if (row + rowSpan - 1 >= rowCount) { throw new IllegalArgumentException("wrong row span: " + rowSpan + "; row=" + row + " rowCount=" + rowCount); } final int column = c.getColumn(); final int colSpan = c.getColSpan(); final int columnCount = getColumnCount(); if (column < 0 || column >= columnCount) { throw new IllegalArgumentException("wrong column: " + column); } if (column + colSpan - 1 >= columnCount) { throw new IllegalArgumentException( "wrong col span: " + colSpan + "; column=" + column + " columnCount=" + columnCount); } super.addLayoutComponent(comp, constraints); } /** * @return number of rows in the grid. */ public int getRowCount() { return myRowStretches.length; } /** * @return number of columns in the grid. */ public int getColumnCount() { return myColumnStretches.length; } /** * @return vertical stretch for the row with specified index. The returned * values is in range <code>[1..Integer.MAX_VALUE]</code>. */ public int getRowStretch(final int rowIndex) { return myRowStretches[rowIndex]; } /** * @throws IllegalArgumentException if <code>stretch</code> is less * then <code>1</code>. */ public void setRowStretch(final int rowIndex, final int stretch) { if (stretch < 1) { throw new IllegalArgumentException("wrong stretch: " + stretch); } myRowStretches[rowIndex] = stretch; } /** * @return maximum horizontal stretch for the component which are located * at the specified column. */ public int getColumnStretch(final int columnIndex) { return myColumnStretches[columnIndex]; } /** * @throws IllegalArgumentException if <code>stretch</code> is less * then <code>1</code>. */ public void setColumnStretch(final int columnIndex, final int stretch) { if (stretch < 1) { throw new IllegalArgumentException("wrong stretch: " + stretch); } myColumnStretches[columnIndex] = stretch; } public Dimension maximumLayoutSize(final Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } public Dimension minimumLayoutSize(final Container container) { validateInfos(container); // IMPORTANT!!! DO NOT INLINE!!! final DimensionInfo horizontalInfo = myHorizontalInfo; final DimensionInfo verticalInfo = myVerticalInfo; final Dimension result = getTotalGap(container, horizontalInfo, verticalInfo); final int[] widths = getMinSizes(horizontalInfo); if (mySameSizeHorizontally) { makeSameSizes(widths); } result.width += sum(widths); final int[] heights = getMinSizes(verticalInfo); if (mySameSizeVertically) { makeSameSizes(heights); } result.height += sum(heights); return result; } private static void makeSameSizes(int[] widths) { int max = widths[0]; for (int i = 0; i < widths.length; i++) { int width = widths[i]; max = Math.max(width, max); } for (int i = 0; i < widths.length; i++) { widths[i] = max; } } private static int[] getSameSizes(DimensionInfo info, int totalWidth) { int[] widths = new int[info.getCellCount()]; int average = totalWidth / widths.length; int rest = totalWidth % widths.length; for (int i = 0; i < widths.length; i++) { widths[i] = average; if (rest > 0) { widths[i]++; rest--; } } return widths; } public Dimension preferredLayoutSize(final Container container) { validateInfos(container); // IMPORTANT!!! DO NOT INLINE!!! final DimensionInfo horizontalInfo = myHorizontalInfo; final DimensionInfo verticalInfo = myVerticalInfo; final Dimension result = getTotalGap(container, horizontalInfo, verticalInfo); final int[] widths = getPrefSizes(horizontalInfo); if (mySameSizeHorizontally) { makeSameSizes(widths); } result.width += sum(widths); final int[] heights = getPrefSizes(verticalInfo); if (mySameSizeVertically) { makeSameSizes(heights); } result.height += sum(heights); return result; } private static int sum(final int[] ints) { int result = 0; for (int i = ints.length - 1; i >= 0; i--) { result += ints[i]; } return result; } private Dimension getTotalGap(final Container container, final DimensionInfo hInfo, final DimensionInfo vInfo) { final Insets insets = getInsets(container); return new Dimension( insets.left + insets.right + countGap(hInfo, 0, hInfo.getCellCount()) + myMargin.left + myMargin.right, insets.top + insets.bottom + countGap(vInfo, 0, vInfo.getCellCount()) + myMargin.top + myMargin.bottom); } private static int getDesignTimeInsets(Container container) { while(container != null) { if (container instanceof JComponent) { Integer designTimeInsets = (Integer)((JComponent) container).getClientProperty(DESIGN_TIME_INSETS); if (designTimeInsets != null) { return designTimeInsets.intValue(); } } container = container.getParent(); } return 0; } private static Insets getInsets(Container container) { final Insets insets = container.getInsets(); int insetsValue = getDesignTimeInsets(container); if (insetsValue != 0) { return new Insets(insets.top+insetsValue, insets.left+insetsValue, insets.bottom+insetsValue, insets.right+insetsValue); } return insets; } private static int countGap(final DimensionInfo info, final int startCell, final int cellCount) { int counter = 0; for (int cellIndex = startCell + cellCount - 2 /*gap after last cell should not be counted*/; cellIndex >= startCell; cellIndex--) { if (shouldAddGapAfterCell(info, cellIndex)) { counter++; } } return counter * info.getGap(); } private static boolean shouldAddGapAfterCell(final DimensionInfo info, final int cellIndex) { if (cellIndex < 0 || cellIndex >= info.getCellCount()) { throw new IllegalArgumentException("wrong cellIndex: " + cellIndex + "; cellCount=" + info.getCellCount()); } boolean endsInThis = false; boolean startsInNext = false; int indexOfNextNotEmpty = -1; for (int i = cellIndex + 1; i < info.getCellCount(); i++) { if (!isCellEmpty(info, i)) { indexOfNextNotEmpty = i; break; } } for (int i = 0; i < info.getComponentCount(); i++) { final Component component = info.getComponent(i); if (component instanceof Spacer) { continue; } if (info.componentBelongsCell(i, cellIndex) && DimensionInfo.findAlignedChild(component, info.getConstraints(i)) != null) { return true; } if (info.getCell(i) == indexOfNextNotEmpty) { startsInNext = true; } if (info.getCell(i) + info.getSpan(i) - 1 == cellIndex) { endsInThis = true; } } return startsInNext && endsInThis; } private static boolean isCellEmpty(final DimensionInfo info, final int cellIndex) { if (cellIndex < 0 || cellIndex >= info.getCellCount()) { throw new IllegalArgumentException("wrong cellIndex: " + cellIndex + "; cellCount=" + info.getCellCount()); } for (int i = 0; i < info.getComponentCount(); i++) { final Component component = info.getComponent(i); if (info.getCell(i) == cellIndex && !(component instanceof Spacer)) { return false; } } return true; } public void layoutContainer(final Container container) { validateInfos(container); // IMPORTANT!!! DO NOT INLINE!!! final LayoutState layoutState = myLayoutState; final DimensionInfo horizontalInfo = myHorizontalInfo; final DimensionInfo verticalInfo = myVerticalInfo; Insets insets = getInsets(container); int skipLayout = checkSetSizesFromParent(container, insets); final Dimension gap = getTotalGap(container, horizontalInfo, verticalInfo); final Dimension size = container.getSize(); size.width -= gap.width; size.height -= gap.height; final Dimension prefSize = preferredLayoutSize(container); prefSize.width -= gap.width; prefSize.height -= gap.height; final Dimension minSize = minimumLayoutSize(container); minSize.width -= gap.width; minSize.height -= gap.height; // Calculate rows' heights if ((skipLayout & SKIP_ROW) == 0) { final int[] heights; if (mySameSizeVertically) { heights = getSameSizes(verticalInfo, Math.max(size.height, minSize.height)); } else { if (size.height < prefSize.height) { heights = getMinSizes(verticalInfo); new_doIt(heights, 0, verticalInfo.getCellCount(), size.height, verticalInfo, true); } else { heights = getPrefSizes(verticalInfo); new_doIt(heights, 0, verticalInfo.getCellCount(), size.height, verticalInfo, false); } } // Calculate rows' bounds int y = insets.top + myMargin.top; for (int i = 0; i < heights.length; i++) { myYs[i] = y; myHeights[i] = heights[i]; y += heights[i]; if (shouldAddGapAfterCell(verticalInfo, i)) { y += verticalInfo.getGap(); } } } if ((skipLayout & SKIP_COL) == 0) { // Calculate columns' widths final int[] widths; if (mySameSizeHorizontally) { widths = getSameSizes(horizontalInfo, Math.max(size.width, minSize.width)); } else { if (size.width < prefSize.width) { widths = getMinSizes(horizontalInfo); new_doIt(widths, 0, horizontalInfo.getCellCount(), size.width, horizontalInfo, true); } else { widths = getPrefSizes(horizontalInfo); new_doIt(widths, 0, horizontalInfo.getCellCount(), size.width, horizontalInfo, false); } } // Calculate columns' bounds int x = insets.left + myMargin.left; for (int i = 0; i < widths.length; i++) { myXs[i] = x; myWidths[i] = widths[i]; x += widths[i]; if (shouldAddGapAfterCell(horizontalInfo, i)) { x += horizontalInfo.getGap(); } } } // Set bounds of components for (int i = 0; i < layoutState.getComponentCount(); i++) { final GridConstraints c = layoutState.getConstraints(i); final Component component = layoutState.getComponent(i); final int column = horizontalInfo.getCell(i); final int colSpan = horizontalInfo.getSpan(i); final int row = verticalInfo.getCell(i); final int rowSpan = verticalInfo.getSpan(i); final int cellWidth = myXs[column + colSpan - 1] + myWidths[column + colSpan - 1] - myXs[column]; final int cellHeight = myYs[row + rowSpan - 1] + myHeights[row + rowSpan - 1] - myYs[row]; final Dimension componentSize = new Dimension(cellWidth, cellHeight); if ((c.getFill() & GridConstraints.FILL_HORIZONTAL) == 0) { componentSize.width = Math.min(componentSize.width, horizontalInfo.getPreferredWidth(i)); } if ((c.getFill() & GridConstraints.FILL_VERTICAL) == 0) { componentSize.height = Math.min(componentSize.height, verticalInfo.getPreferredWidth(i)); } Util.adjustSize(component, c, componentSize); int dx = 0; int dy = 0; if ((c.getAnchor() & GridConstraints.ANCHOR_EAST) != 0) { dx = cellWidth - componentSize.width; } else if ((c.getAnchor() & GridConstraints.ANCHOR_WEST) == 0) { dx = (cellWidth - componentSize.width) / 2; } if ((c.getAnchor() & GridConstraints.ANCHOR_SOUTH) != 0) { dy = cellHeight - componentSize.height; } else if ((c.getAnchor() & GridConstraints.ANCHOR_NORTH) == 0) { dy = (cellHeight - componentSize.height) / 2; } int indent = Util.DEFAULT_INDENT * c.getIndent(); componentSize.width -= indent; dx += indent; component.setBounds(myXs[column] + dx, myYs[row] + dy, componentSize.width, componentSize.height); } } private int checkSetSizesFromParent(final Container container, final Insets insets) { int skipLayout = 0; GridLayoutManager parentGridLayout = null; GridConstraints parentGridConstraints = null; // "use parent layout" also needs to work in cases where a grid is the only control in a non-grid panel // which is contained in a grid Container parent = container.getParent(); if (parent != null) { if (parent.getLayout() instanceof GridLayoutManager) { parentGridLayout = (GridLayoutManager) parent.getLayout(); parentGridConstraints = parentGridLayout.getConstraintsForComponent(container); } else { Container parent2 = parent.getParent(); if (parent2 != null && parent2.getLayout() instanceof GridLayoutManager) { parentGridLayout = (GridLayoutManager) parent2.getLayout(); parentGridConstraints = parentGridLayout.getConstraintsForComponent(parent); } } } if (parentGridLayout != null && parentGridConstraints.isUseParentLayout()) { if (myRowStretches.length == parentGridConstraints.getRowSpan()) { int row = parentGridConstraints.getRow(); myYs[0] = insets.top + myMargin.top; myHeights[0] = parentGridLayout.myHeights[row] - myYs[0]; for (int i = 1; i < myRowStretches.length; i++) { myYs[i] = parentGridLayout.myYs[i + row] - parentGridLayout.myYs[row]; myHeights[i] = parentGridLayout.myHeights[i + row]; } myHeights[myRowStretches.length - 1] -= insets.bottom + myMargin.bottom; skipLayout |= SKIP_ROW; } if (myColumnStretches.length == parentGridConstraints.getColSpan()) { int col = parentGridConstraints.getColumn(); myXs[0] = insets.left + myMargin.left; myWidths[0] = parentGridLayout.myWidths[col] - myXs[0]; for (int i = 1; i < myColumnStretches.length; i++) { myXs[i] = parentGridLayout.myXs[i + col] - parentGridLayout.myXs[col]; myWidths[i] = parentGridLayout.myWidths[i + col]; } myWidths[myColumnStretches.length - 1] -= insets.right + myMargin.right; skipLayout |= SKIP_COL; } } return skipLayout; } public void invalidateLayout(final Container container) { myLayoutState = null; myHorizontalInfo = null; myVerticalInfo = null; } void validateInfos(final Container container) { if (myLayoutState == null) { // TODO[yole]: Implement cleaner way of determining whether invisible components should be ignored myLayoutState = new LayoutState(this, getDesignTimeInsets(container) == 0); myHorizontalInfo = new HorizontalInfo(myLayoutState, getHGapImpl(container)); myVerticalInfo = new VerticalInfo(myLayoutState, getVGapImpl(container)); } } /** * for design time only */ public int[] getXs() { return myXs; } /** * for design time only */ public int[] getWidths() { return myWidths; } /** * for design time only */ public int[] getYs() { return myYs; } /** * for design time only */ public int[] getHeights() { return myHeights; } public int[] getCoords(boolean isRow) { return isRow ? myYs : myXs; } public int[] getSizes(boolean isRow) { return isRow ? myHeights : myWidths; } private int[] getMinSizes(final DimensionInfo info) { return getMinOrPrefSizes(info, true); } private int[] getPrefSizes(final DimensionInfo info) { return getMinOrPrefSizes(info, false); } private int[] getMinOrPrefSizes(final DimensionInfo info, final boolean min) { final int[] widths = new int[info.getCellCount()]; for (int i = 0; i < widths.length; i++) { widths[i] = myMinCellSize; } // single spaned components for (int i = info.getComponentCount() - 1; i >= 0; i--) { if (info.getSpan(i) != 1) { continue; } int size = min ? getMin2(info, i) : Math.max(info.getMinimumWidth(i), info.getPreferredWidth(i)); int cell = info.getCell(i); final int gap = countGap(info, cell, info.getSpan(i)); size = Math.max(size - gap, 0); widths[cell] = Math.max(widths[cell], size); } // components inheriting layout from us updateSizesFromChildren(info, min, widths); // multispanned components final boolean[] toProcess = new boolean[info.getCellCount()]; for (int i = info.getComponentCount() - 1; i >= 0; i--) { int size = min ? getMin2(info, i) : Math.max(info.getMinimumWidth(i), info.getPreferredWidth(i)); final int span = info.getSpan(i); final int cell = info.getCell(i); final int gap = countGap(info, cell, span); size = Math.max(size - gap, 0); Arrays.fill(toProcess, false); int curSize = 0; for (int j=0; j < span; j++){ curSize += widths[j + cell]; toProcess[j + cell] = true; } if (curSize >= size) { continue; } final boolean[] higherPriorityCells = new boolean[toProcess.length]; getCellsWithHigherPriorities(info, toProcess, higherPriorityCells, false, widths); distribute(higherPriorityCells, info, size - curSize, widths); } return widths; } private static void updateSizesFromChildren(final DimensionInfo info, final boolean min, final int[] widths) { for(int i=info.getComponentCount() - 1; i >= 0; i--) { Component child = info.getComponent(i); GridConstraints c = info.getConstraints(i); if (c.isUseParentLayout() && child instanceof Container) { Container container = (Container) child; if (container.getLayout() instanceof GridLayoutManager) { updateSizesFromChild(info, min, widths, container, i); } else if (container.getComponentCount() == 1 && container.getComponent(0) instanceof Container) { // "use parent layout" also needs to work in cases where a grid is the only control in a non-grid panel // which is contained in a grid Container childContainer = (Container) container.getComponent(0); if (childContainer.getLayout() instanceof GridLayoutManager) { updateSizesFromChild(info, min, widths, childContainer, i); } } } } } private static void updateSizesFromChild(final DimensionInfo info, final boolean min, final int[] widths, final Container container, final int childIndex) { GridLayoutManager childLayout = (GridLayoutManager) container.getLayout(); if (info.getSpan(childIndex) == info.getChildLayoutCellCount(childLayout)) { childLayout.validateInfos(container); DimensionInfo childInfo = (info instanceof HorizontalInfo) ? childLayout.myHorizontalInfo : childLayout.myVerticalInfo; int[] sizes = childLayout.getMinOrPrefSizes(childInfo, min); int cell = info.getCell(childIndex); for(int j=0; j<sizes.length; j++) { widths [cell+j] = Math.max(widths [cell+j], sizes [j]); } } } private static int getMin2(final DimensionInfo info, final int componentIndex) { final int s; if ((info.getSizePolicy(componentIndex) & GridConstraints.SIZEPOLICY_CAN_SHRINK) != 0) { s = info.getMinimumWidth(componentIndex); } else { // it might be possible that minSize > prefSize (for example, only min is set in constraints to 100 and // JComponent's preferred size returned is 20) s = Math.max(info.getMinimumWidth(componentIndex), info.getPreferredWidth(componentIndex)); } return s; } /** * @param widths in/out parameter */ private void new_doIt(final int[] widths, final int cell, final int span, final int minWidth, final DimensionInfo info, final boolean checkPrefs) { int toDistribute = minWidth; for (int i = cell; i < cell + span; i++) { toDistribute -= widths[i]; } if (toDistribute <= 0) { return; } final boolean[] allowedCells = new boolean[info.getCellCount()]; for (int i = cell; i < cell + span; i++) { allowedCells[i] = true; } final boolean[] higherPriorityCells = new boolean[info.getCellCount()]; getCellsWithHigherPriorities(info, allowedCells, higherPriorityCells, checkPrefs, widths); distribute(higherPriorityCells, info, toDistribute, widths); } private static void distribute(final boolean[] higherPriorityCells, final DimensionInfo info, int toDistribute, final int[] widths) { int stretches = 0; for (int i = 0; i < info.getCellCount(); i++) { if (higherPriorityCells[i]) { stretches += info.getStretch(i); } } { final int toDistributeFrozen = toDistribute; for (int i = 0; i < info.getCellCount(); i++) { if (!higherPriorityCells[i]) { continue; } final int addon = toDistributeFrozen * info.getStretch(i) / stretches; widths[i] += addon; toDistribute -= addon; } } if (toDistribute != 0) { for (int i = 0; i < info.getCellCount(); i++) { if (!higherPriorityCells[i]) { continue; } widths[i]++; toDistribute--; if (toDistribute == 0) { break; } } } if (toDistribute != 0) { throw new IllegalStateException("toDistribute = " + toDistribute); } } private void getCellsWithHigherPriorities( final DimensionInfo info, final boolean[] allowedCells, final boolean[] higherPriorityCells, final boolean checkPrefs, final int[] widths ) { Arrays.fill(higherPriorityCells, false); int foundCells = 0; if (checkPrefs) { // less that preferred size final int[] prefs = getMinOrPrefSizes(info, false); for (int cell = 0; cell < allowedCells.length; cell++) { if (!allowedCells[cell]) { continue; } if (!isCellEmpty(info, cell) && prefs[cell] > widths[cell]) { higherPriorityCells[cell] = true; foundCells++; } } if (foundCells > 0) { return; } } // want grow for (int cell = 0; cell < allowedCells.length; cell++) { if (!allowedCells[cell]) { continue; } if ((info.getCellSizePolicy(cell) & GridConstraints.SIZEPOLICY_WANT_GROW) != 0) { higherPriorityCells[cell] = true; foundCells++; } } if (foundCells > 0) { return; } // can grow for (int cell = 0; cell < allowedCells.length; cell++) { if (!allowedCells[cell]) { continue; } if ((info.getCellSizePolicy(cell) & GridConstraints.SIZEPOLICY_CAN_GROW) != 0) { higherPriorityCells[cell] = true; foundCells++; } } if (foundCells > 0) { return; } // non empty for (int cell = 0; cell < allowedCells.length; cell++) { if (!allowedCells[cell]) { continue; } if (!isCellEmpty(info, cell)) { higherPriorityCells[cell] = true; foundCells++; } } if (foundCells > 0) { return; } // any for (int cell = 0; cell < allowedCells.length; cell++) { if (!allowedCells[cell]) { continue; } higherPriorityCells[cell] = true; } } public boolean isSameSizeHorizontally() { return mySameSizeHorizontally; } public boolean isSameSizeVertically() { return mySameSizeVertically; } public void setSameSizeHorizontally(boolean sameSizeHorizontally) { mySameSizeHorizontally = sameSizeHorizontally; } public void setSameSizeVertically(boolean sameSizeVertically) { mySameSizeVertically = sameSizeVertically; } public int[] getHorizontalGridLines() { int[] result = new int [myYs.length+1]; result [0] = myYs [0]; for(int i=0; i<myYs.length-1; i++) { result [i+1] = (myYs[i] + myHeights[i] + myYs[i + 1]) / 2; } result [myYs.length] = myYs [myYs.length-1] + myHeights [myYs.length-1]; return result; } public int[] getVerticalGridLines() { int[] result = new int [myXs.length+1]; result [0] = myXs [0]; for(int i=0; i<myXs.length-1; i++) { result [i+1] = (myXs[i] + myWidths[i] + myXs[i + 1]) / 2; } result [myXs.length] = myXs [myXs.length-1] + myWidths [myXs.length-1]; return result; } public int getCellCount(final boolean isRow) { return isRow ? getRowCount() : getColumnCount(); } public int getCellSizePolicy(final boolean isRow, final int cellIndex) { DimensionInfo info = isRow ? myVerticalInfo : myHorizontalInfo; if (info == null) { // not laid out yet return 0; } return info.getCellSizePolicy(cellIndex); } }