| /******************************************************************************* |
| * Copyright (c) 2004, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.internal.layout; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Layout; |
| |
| /** |
| * <p>Instance of this class lay out the control children of a <code>Composite</code> |
| * in a grid, using a simple set of rules and a simple API. This class is |
| * intended to be more predictable than <code>GridLayout</code> and easier to use than |
| * <code>FormLayout</code>, while retaining most of the power of both.</p> |
| * |
| * <p>The power of a <code>CellLayout</code> lies in the ability to control |
| * the size and resizing properties of each row and column. Unlike other layout |
| * classes, complex layouts can be created without attaching any layout data to |
| * individual controls in the layout. </p> |
| * |
| * <p>The various subclasses of <code>IColumnInfo</code> |
| * can be used to create columns with fixed width, columns whose width is computed |
| * from child controls, or width that grows in proportion to the size of other |
| * columns. Layouts can be given a default <code>IColumnInfo</code> that will |
| * be used to set the size of any column whose properties have not been explicitly |
| * set. This is useful for creating layouts where most or all columns have the |
| * same properties. Similarly, the subclasses of <code>IRowInfo</code> can be used to |
| * control the height of individual rows.</p> |
| * |
| * <p>For a finer grain of control, <code>CellData</code> objects can be attached |
| * to individual controls in the layout. These objects serve a similar function as |
| * <code>GridData</code> objects serve for <code>GridLayout</code>. They allow |
| * controls to span multiple rows or columns, set the justification of the control |
| * within its cell, and allow the user to override the preferred size of the control. |
| * </p> |
| * |
| * <p>In many cases, it is not necessary to attach any layout data to controls in |
| * the layout, since the controls can be arranged based on the properties of rows |
| * and columns. However, layout data may be attached to individual controls to |
| * allow them to span multiple columns or to control their justification within |
| * their cell. |
| * </p> |
| * |
| * <p>All the <code>set</code> methods in this class return <code>this</code>, allowing |
| * a layout to be created and initialized in a single line of code. For example: </p> |
| * |
| * <code> |
| * Composite myControl = new Composite(parent, SWT.NONE); |
| * myControl.setLayout(new CellLayout(2).setMargins(10,10).setSpacing(5,5)); |
| * </code> |
| * |
| * @since 3.0 |
| */ |
| public class CellLayout extends Layout { |
| |
| /** |
| * Object used to compute the height of rows whose properties have not been |
| * explicitly set. |
| */ |
| private Row defaultRowSettings = new Row(false); |
| |
| /** |
| * Object used to compute the width of columns whose properties have not been |
| * explicitly set. |
| */ |
| private Row defaultColSettings = new Row(true); |
| |
| /** |
| * horizontalSpacing specifies the number of pixels between the right |
| * edge of one cell and the left edge of its neighbouring cell to |
| * the right. |
| * |
| * The default value is 5. |
| */ |
| int horizontalSpacing = 5; |
| |
| /** |
| * verticalSpacing specifies the number of pixels between the bottom |
| * edge of one cell and the top edge of its neighbouring cell underneath. |
| * |
| * The default value is 5. |
| */ |
| int verticalSpacing = 5; |
| |
| /** |
| * marginWidth specifies the number of pixels of horizontal margin |
| * that will be placed along the left and right edges of the layout. |
| * |
| * The default value is 0. |
| */ |
| public int marginWidth = 5; |
| |
| /** |
| * marginHeight specifies the number of pixels of vertical margin |
| * that will be placed along the top and bottom edges of the layout. |
| * |
| * The default value is 0. |
| */ |
| public int marginHeight = 5; |
| |
| /** |
| * Number of columns in this layout, or 0 indicating that the whole layout |
| * should be on a single row. |
| */ |
| private int numCols; |
| |
| /** |
| * List of IColumnInfo. The nth object is used to compute the width of the |
| * nth column, or null indicating that the default column should be used. |
| */ |
| private List cols; |
| |
| /** |
| * List of RowInfo. The nth object is used to compute the height of the |
| * nth row, or null indicating that the default row should be used. |
| */ |
| private List rows = new ArrayList(16); |
| |
| // Cached information |
| private GridInfo gridInfo = new GridInfo(); |
| |
| private int[] cachedRowMin = null; |
| |
| private int[] cachedColMin = null; |
| |
| public static int cacheMisses; |
| |
| public static int cacheHits; |
| |
| private LayoutCache cache = new LayoutCache(); |
| |
| // End of cached control sizes |
| |
| /** |
| * Creates the layout |
| * |
| * @param numCols the number of columns in this layout, |
| * or 0 indicating that the whole layout should be on one row. |
| */ |
| public CellLayout(int numCols) { |
| super(); |
| this.numCols = numCols; |
| cols = new ArrayList(numCols == 0 ? 3 : numCols); |
| } |
| |
| /** |
| * Sets the amount empty space between cells |
| * |
| * @param newSpacing a point (x,y) corresponding to the number of pixels of |
| * empty space between adjacent columns and rows respectively |
| */ |
| public CellLayout setSpacing(int horizontalSpacing, int verticalSpacing) { |
| this.horizontalSpacing = horizontalSpacing; |
| this.verticalSpacing = verticalSpacing; |
| |
| return this; |
| } |
| |
| /** |
| * Sets the amount empty space between cells |
| * |
| * @param newSpacing a point (x,y) corresponding to the number of pixels of |
| * empty space between adjacent columns and rows respectively |
| */ |
| public CellLayout setSpacing(Point newSpacing) { |
| horizontalSpacing = newSpacing.x; |
| verticalSpacing = newSpacing.y; |
| return this; |
| } |
| |
| /** |
| * Returns the amount of empty space between adjacent cells |
| * |
| * @return a point (x,y) corresponding to the number of pixels of empty |
| * space between adjacent columns and rows respectively |
| */ |
| public Point getSpacing() { |
| return new Point(horizontalSpacing, verticalSpacing); |
| } |
| |
| /** |
| * Sets the size of the margin around the outside of the layout. |
| * |
| * @param marginWidth the size of the margin around the top and |
| * bottom of the layout |
| * @param marginHeight the size of the margin on the left and right |
| * of the layout. |
| */ |
| public CellLayout setMargins(int marginWidth, int marginHeight) { |
| this.marginWidth = marginWidth; |
| this.marginHeight = marginHeight; |
| return this; |
| } |
| |
| /** |
| * Sets the size of the margin around the outside of the layout. |
| * |
| * @param newMargins point indicating the size of the horizontal and vertical |
| * margins, in pixels. |
| */ |
| public CellLayout setMargins(Point newMargins) { |
| marginWidth = newMargins.x; |
| marginHeight = newMargins.y; |
| return this; |
| } |
| |
| /** |
| * Returns the size of the margins around the outside of the layout. |
| * |
| * @return the size of the outer margins, in pixels. |
| */ |
| public Point getMargins() { |
| return new Point(marginWidth, marginHeight); |
| } |
| |
| /** |
| * Sets the default column settings. All columns will use these settings unless |
| * they have been explicitly assigned custom settings by setColumn. |
| * |
| * @param info the properties of all default columns |
| * @see setColumn |
| */ |
| public CellLayout setDefaultColumn(Row info) { |
| defaultColSettings = info; |
| return this; |
| } |
| |
| /** |
| * Sets the column info for the given column number (the leftmost column is column 0). |
| * This replaces any existing info for the column. Note that more than one column |
| * are allowed to share the same IColumnInfo instance if they have identical properties. |
| * |
| * @param colNum the column number to modify |
| * @param info the properties of the column, or null if this column should use the |
| * default properties |
| */ |
| public CellLayout setColumn(int colNum, Row info) { |
| while (cols.size() <= colNum) { |
| cols.add(null); |
| } |
| |
| cols.set(colNum, info); |
| |
| return this; |
| } |
| |
| /** |
| * Sets the default row settings for this layout. Unless this is overridden |
| * for an individual row, all rows will use the default settings. |
| * |
| * @param info the row info object that should be used to set the size |
| * of rows, by default. |
| */ |
| public CellLayout setDefaultRow(Row info) { |
| defaultRowSettings = info; |
| |
| return this; |
| } |
| |
| /** |
| * Sets the row info for the given rows. The topmost row is row 0. Multiple |
| * rows are allowed to share the same RowInfo instance. |
| * |
| * @param rowNum the row number to set |
| * @param info the row info that will control the sizing of the given row, |
| * or null if the row should use the default settings for this layout. |
| */ |
| public CellLayout setRow(int rowNum, Row info) { |
| while (rows.size() <= rowNum) { |
| rows.add(null); |
| } |
| |
| rows.set(rowNum, info); |
| |
| return this; |
| } |
| |
| /** |
| * Returns the row info that controls the size of the given row. Will return |
| * the default row settings for this layout if no custom row info has been |
| * assigned to the row. |
| * |
| * @param rowNum |
| * @return |
| */ |
| private Row getRow(int rowNum, boolean isHorizontal) { |
| if (isHorizontal) { |
| if (rowNum >= rows.size()) { |
| return defaultRowSettings; |
| } |
| |
| Row result = (Row) rows.get(rowNum); |
| |
| if (result == null) { |
| result = defaultRowSettings; |
| } |
| |
| return result; |
| } else { |
| if (rowNum >= cols.size()) { |
| return defaultColSettings; |
| } |
| |
| Row result = (Row) cols.get(rowNum); |
| |
| if (result == null) { |
| result = defaultColSettings; |
| } |
| |
| return result; |
| } |
| } |
| |
| /** |
| * Initializes the gridInfo object. |
| * |
| * @param children controls that are being layed out |
| */ |
| private void initGrid(Control[] children) { |
| cache.setControls(children); |
| gridInfo.initGrid(children, this); |
| cachedRowMin = null; |
| cachedColMin = null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite, int, int, boolean) |
| */ |
| protected Point computeSize(Composite composite, int wHint, int hHint, |
| boolean flushCache) { |
| Control[] children = composite.getChildren(); |
| initGrid(children); |
| |
| if (flushCache) { |
| cache.flush(); |
| } |
| |
| // Determine the amount of whitespace (area that cannot be used by controls) |
| Point emptySpace = totalEmptySpace(); |
| |
| int[] heightConstraints = computeConstraints(true); |
| |
| int width; |
| if (wHint == SWT.DEFAULT) { |
| width = preferredSize(heightConstraints, false); |
| } else { |
| width = wHint - emptySpace.x; |
| } |
| |
| int height = hHint; |
| if (hHint == SWT.DEFAULT) { |
| height = preferredSize( |
| computeSizes(heightConstraints, width, false), true); |
| } else { |
| height = hHint - emptySpace.y; |
| } |
| |
| Point preferredSize = new Point(width + emptySpace.x, height |
| + emptySpace.y); |
| |
| // At this point we know the layout's preferred size. Now adjust it |
| // if we're smaller than the minimum possible size for the composite. |
| |
| // If exactly one dimension of our preferred size is smaller than |
| // the minimum size of our composite, then set that dimension to |
| // the minimum size and recompute the other dimension (for example, |
| // increasing the width to match a shell's minimum width may reduce |
| // the height allocated for a wrapping text widget). There is no |
| // point in doing this if both dimensions are smaller than the |
| // composite's minimum size, since we're already smaller than |
| // we need to be. |
| Point minimumSize = CellLayoutUtil.computeMinimumSize(composite); |
| |
| boolean wider = (preferredSize.x >= minimumSize.x); |
| boolean taller = (preferredSize.y >= minimumSize.y); |
| |
| if (wider) { |
| if (taller) { |
| // If we're larger in both dimensions, don't adjust the minimum |
| // size. |
| return preferredSize; |
| } else { |
| // If our preferred height is smaller than the minimum height, |
| // recompute the preferred width using the minimum height |
| return computeSize(composite, wHint, minimumSize.y, false); |
| } |
| } else { |
| if (taller) { |
| // If our preferred width is smaller than the minimum width, |
| // recompute the preferred height using the minimum width |
| return computeSize(composite, minimumSize.x, hHint, false); |
| } else { |
| // If both dimensions are smaller than the minimum size, |
| // use the minimum size as our preferred size. |
| return minimumSize; |
| } |
| } |
| } |
| |
| int[] computeSizes(int[] constraints, int availableSpace, |
| boolean computingRows) { |
| int[] result = computeMinSizes(constraints, computingRows); |
| |
| int totalFixed = sumOfSizes(result); |
| int denominator = getResizeDenominator(computingRows); |
| int numRows = gridInfo.getNumRows(computingRows); |
| |
| if (totalFixed < availableSpace) { |
| int remaining = availableSpace - totalFixed; |
| |
| for (int idx = 0; idx < numRows && denominator > 0; idx++) { |
| Row row = getRow(idx, computingRows); |
| |
| if (row.grows) { |
| int greed = row.size; |
| int amount = remaining * greed / denominator; |
| |
| result[idx] += amount; |
| remaining -= amount; |
| denominator -= greed; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Computes one dimension of the preferred size of the layout. |
| * |
| * @param hint contains the result if already known, or SWT.DEFAULT if it needs to be computed |
| * @param constraints contains constraints along the other dimension, or SWT.DEFAULT if none. For |
| * example, if we are computing the preferred row sizes, this would be an array of known column sizes. |
| * @param computingRows if true, this method returns the height (pixels). Otherwise, it returns the |
| * width (pixels). |
| */ |
| int preferredSize(int[] constraints, boolean computingRows) { |
| int[] fixedSizes = computeMinSizes(constraints, computingRows); |
| |
| return sumOfSizes(fixedSizes) |
| + getDynamicSize(constraints, fixedSizes, computingRows); |
| } |
| |
| /** |
| * Computes the sum of all integers in the given array. If any of the entries are SWT.DEFAULT, |
| * the result is SWT.DEFAULT. |
| */ |
| static int sumOfSizes(int[] input) { |
| return sumOfSizes(input, 0, input.length); |
| } |
| |
| static int sumOfSizes(int[] input, int start, int length) { |
| int sum = 0; |
| for (int idx = start; idx < start + length; idx++) { |
| int next = input[idx]; |
| |
| if (next == SWT.DEFAULT) { |
| return SWT.DEFAULT; |
| } |
| |
| sum += next; |
| } |
| |
| return sum; |
| } |
| |
| /** |
| * Returns the preferred dynamic width of the layout |
| * |
| * @param constraints |
| * @param fixedSizes |
| * @param computingRows |
| * @return |
| */ |
| int getDynamicSize(int[] constraints, int[] fixedSizes, |
| boolean computingRows) { |
| int result = 0; |
| int numerator = getResizeDenominator(computingRows); |
| |
| // If no resizable columns, return |
| if (numerator == 0) { |
| return 0; |
| } |
| |
| int rowSpacing = computingRows ? verticalSpacing : horizontalSpacing; |
| int colSpacing = computingRows ? horizontalSpacing : verticalSpacing; |
| |
| int numControls = gridInfo.controls.length; |
| for (int idx = 0; idx < numControls; idx++) { |
| int controlRowStart = gridInfo.getStartPos(idx, computingRows); |
| int controlRowSpan = getSpan(idx, computingRows); |
| int controlColStart = gridInfo.getStartPos(idx, !computingRows); |
| int controlColSpan = getSpan(idx, !computingRows); |
| |
| int denominator = getGrowthRatio(controlRowStart, controlRowSpan, |
| computingRows); |
| |
| if (denominator > 0) { |
| |
| int widthHint = sumOfSizes(constraints, controlColStart, |
| controlColSpan); |
| if (widthHint != SWT.DEFAULT) { |
| widthHint += colSpacing * (controlColSpan - 1); |
| } |
| |
| // Compute the total control size |
| int controlSize = computeControlSize(idx, widthHint, |
| computingRows); |
| |
| // Subtract the amount that overlaps fixed-size columns |
| controlSize -= sumOfSizes(fixedSizes, controlRowStart, |
| controlRowSpan); |
| |
| // Subtract the amount that overlaps spacing between cells |
| controlSize -= (rowSpacing * (controlRowSpan - 1)); |
| |
| result = Math |
| .max(result, controlSize * numerator / denominator); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Computes one dimension of a control's size |
| * |
| * @param control the index of the control being computed |
| * @param constraint the other dimension of the control's size, or SWT.DEFAULT if unknown |
| * @param computingHeight if true, this method returns a height. Else it returns a width |
| * @return the preferred height or width of the control, in pixels |
| */ |
| int computeControlSize(int control, int constraint, boolean computingHeight) { |
| CellData data = gridInfo.getCellData(control); |
| |
| // If we're looking for the preferred size of the control (without hints) |
| if (constraint == SWT.DEFAULT) { |
| Point result = data.computeSize(cache.getCache(control), |
| SWT.DEFAULT, SWT.DEFAULT); |
| |
| // Return result |
| if (computingHeight) { |
| return result.y; |
| } |
| return result.x; |
| } |
| |
| // Compute a height |
| if (computingHeight) { |
| return data.computeSize(cache.getCache(control), constraint, |
| SWT.DEFAULT).y; |
| } |
| |
| return data.computeSize(cache.getCache(control), SWT.DEFAULT, |
| constraint).x; |
| } |
| |
| /** |
| * Returns the relative amount that a control starting on the given row and spanning |
| * the given length will contribute |
| * |
| * @param start |
| * @param length |
| * @param computingRows |
| * @return |
| */ |
| int getGrowthRatio(int start, int length, boolean computingRows) { |
| boolean willGrow = false; |
| int sum = 0; |
| |
| int end = start + length; |
| for (int idx = start; idx < end; idx++) { |
| Row row = getRow(idx, computingRows); |
| |
| if (row.largerThanChildren && row.grows) { |
| willGrow = true; |
| } |
| |
| sum += row.size; |
| } |
| |
| if (!willGrow) { |
| return 0; |
| } |
| |
| return sum; |
| } |
| |
| int[] computeMinSizes(int[] constraints, boolean computingRows) { |
| // We cache the result of this function since it might be called more than once |
| // for a single size computation |
| int[] result = computingRows ? cachedRowMin : cachedColMin; |
| |
| if (result == null) { |
| int columnSpacing; |
| int rowSpacing; |
| |
| if (computingRows) { |
| columnSpacing = horizontalSpacing; |
| rowSpacing = verticalSpacing; |
| } else { |
| columnSpacing = verticalSpacing; |
| rowSpacing = horizontalSpacing; |
| } |
| |
| int rowCount = gridInfo.getNumRows(computingRows); |
| result = new int[rowCount]; |
| int colCount = gridInfo.getNumRows(!computingRows); |
| int[] rowControls = new int[colCount]; |
| |
| int lastGrowingRow = -1; |
| |
| for (int idx = 0; idx < rowCount; idx++) { |
| Row row = getRow(idx, computingRows); |
| |
| if (row.grows) { |
| // There is no minimum size for growing rows |
| lastGrowingRow = idx; |
| result[idx] = 0; |
| } else { |
| result[idx] = row.size; |
| |
| if (row.largerThanChildren) { |
| // Determine which controls are in this row |
| gridInfo.getRow(rowControls, idx, computingRows); |
| |
| for (int colIdx = 0; colIdx < rowControls.length; colIdx++) { |
| int control = rowControls[colIdx]; |
| |
| // The getRow method will insert -1 into empty cells... skip these. |
| if (control != -1) { |
| int controlStart = gridInfo.getStartPos( |
| control, computingRows); |
| int controlSpan = getSpan(control, |
| computingRows); |
| |
| // If the control ends on this row and does not span any growing rows |
| if (controlStart + controlSpan - 1 == idx |
| && controlStart > lastGrowingRow) { |
| int controlColStart = gridInfo.getStartPos( |
| control, !computingRows); |
| int controlColSpan = getSpan(control, |
| !computingRows); |
| int controlRowSpan = getSpan(control, |
| computingRows); |
| |
| // Compute the width constraint for this control |
| int spannedWidth = sumOfSizes(constraints, |
| controlColStart, controlColSpan); |
| if (spannedWidth != SWT.DEFAULT) { |
| spannedWidth += (columnSpacing * (controlSpan - 1)); |
| } |
| |
| int controlHeight = computeControlSize( |
| control, spannedWidth, |
| computingRows); |
| |
| // Determine how much of the control spans already allocated columns |
| int allocatedHeight = sumOfSizes(result, |
| controlColStart, controlRowSpan - 1) |
| + (rowSpacing * (controlRowSpan - 1)); |
| |
| result[idx] = Math.max(result[idx], |
| controlHeight - allocatedHeight); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Cache this result |
| if (computingRows) { |
| cachedRowMin = result; |
| } else { |
| cachedColMin = result; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Returns the height constraints that should be used when computing column widths. Requires initGrid |
| * to have been called first. |
| * |
| * @param result Will contain the height constraint for row i in the ith position of the array, |
| * or SWT.DEFAULT if there is no constraint on that row. |
| */ |
| private int[] computeConstraints(boolean horizontal) { |
| // Initialize the height constraints for each row (basically, these will always be SWT.DEFAULT, |
| // except for rows of type FixedRow, which have a constant height). |
| int numRows = gridInfo.getNumRows(horizontal); |
| int[] result = new int[numRows]; |
| |
| for (int idx = 0; idx < numRows; idx++) { |
| Row row = getRow(idx, horizontal); |
| |
| if (!(row.grows || row.largerThanChildren)) { |
| result[idx] = row.size; |
| } else { |
| result[idx] = SWT.DEFAULT; |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Computes the total greediness of all rows |
| * |
| * @return the total greediness of all rows |
| */ |
| private int getResizeDenominator(boolean horizontal) { |
| int result = 0; |
| int numRows = gridInfo.getNumRows(horizontal); |
| |
| for (int idx = 0; idx < numRows; idx++) { |
| Row row = getRow(idx, horizontal); |
| |
| if (row.grows) { |
| result += row.size; |
| } |
| } |
| |
| return result; |
| } |
| |
| // /** |
| // * Computes the total fixed height of all rows |
| // * |
| // * @param widthConstraints array where the nth entry indicates the known width of the |
| // * nth column, or SWT.DEFAULT if the width is still unknown |
| // * |
| // * @return the total fixed height for all rows |
| // */ |
| // private int getMinimumSize(int[] constraints, boolean horizontal) { |
| // Control[] controls = new Control[gridInfo.getRows()]; |
| // int result = 0; |
| // int numRows = gridInfo.getRows(); |
| // |
| // for (int idx = 0; idx < numRows; idx++) { |
| // result += getRow(idx).getFixedHeight(gridInfo, widthConstraints, idx); |
| // } |
| // |
| // return result; |
| // } |
| |
| protected int getSpan(int controlId, boolean isRow) { |
| CellData data = gridInfo.getCellData(controlId); |
| |
| if (isRow) { |
| return data.verticalSpan; |
| } |
| return data.horizontalSpan; |
| } |
| |
| /** |
| * Returns the total space that will be required for margins and spacing between and |
| * around cells. initGrid(...) must have been called first. |
| * |
| * @return |
| */ |
| private Point totalEmptySpace() { |
| int numRows = gridInfo.getRows(); |
| |
| return new Point((2 * marginWidth) |
| + ((gridInfo.getCols() - 1) * horizontalSpacing), |
| (2 * marginHeight) + ((numRows - 1) * verticalSpacing)); |
| } |
| |
| /** |
| * Returns the absolute positions of each row, given the start position, row sizes, |
| * and row spacing |
| * |
| * @param startPos position of the initial row |
| * @param sizes array of row sizes (pixels) |
| * @param spacing space between each row (pixels) |
| * @return array of row positions. The result size is sizes.length + 1. The last entry is |
| * the position of the end of the layout. |
| */ |
| private static int[] computeRowPositions(int startPos, int[] sizes, |
| int spacing) { |
| int[] result = new int[sizes.length + 1]; |
| |
| result[0] = startPos; |
| for (int idx = 0; idx < sizes.length; idx++) { |
| result[idx + 1] = result[idx] + sizes[idx] + spacing; |
| } |
| |
| return result; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite, boolean) |
| */ |
| protected void layout(Composite composite, boolean flushCache) { |
| Control[] children = composite.getChildren(); |
| |
| // If there are no children then this is a NO-OP |
| if (children.length == 0) |
| return; |
| |
| initGrid(children); |
| |
| if (flushCache) { |
| cache.flush(); |
| } |
| |
| Point emptySpace = totalEmptySpace(); |
| |
| // Compute the area actually available for controls (once the margins and spacing is removed) |
| int availableWidth = composite.getClientArea().width - emptySpace.x; |
| int availableHeight = composite.getClientArea().height - emptySpace.y; |
| |
| int[] heights = computeConstraints(true); |
| int[] widths = new int[gridInfo.getCols()]; |
| |
| // Compute the actual column widths |
| widths = computeSizes(heights, availableWidth, false); |
| |
| // Compute the actual row heights (based on the actual column widths) |
| heights = computeSizes(widths, availableHeight, true); |
| |
| Rectangle currentCell = new Rectangle(0, 0, 0, 0); |
| |
| int[] starty = computeRowPositions(composite.getClientArea().y |
| + marginHeight, heights, verticalSpacing); |
| int[] startx = computeRowPositions(composite.getClientArea().x |
| + marginWidth, widths, horizontalSpacing); |
| |
| int numChildren = gridInfo.controls.length; |
| for (int controlId = 0; controlId < numChildren; controlId++) { |
| CellData data = gridInfo.getCellData(controlId); |
| |
| int row = gridInfo.controlRow[controlId]; |
| int col = gridInfo.controlCol[controlId]; |
| |
| currentCell.x = startx[col]; |
| currentCell.width = startx[col + data.horizontalSpan] |
| - currentCell.x - horizontalSpacing; |
| |
| currentCell.y = starty[row]; |
| currentCell.height = starty[row + data.verticalSpan] |
| - currentCell.y - verticalSpacing; |
| |
| data.positionControl(cache.getCache(controlId), currentCell); |
| } |
| } |
| |
| /** |
| * @return |
| */ |
| public int getColumns() { |
| return numCols; |
| } |
| |
| public boolean canGrow(Composite composite, boolean horizontally) { |
| initGrid(composite.getChildren()); |
| |
| int numRows = gridInfo.getNumRows(horizontally); |
| |
| for (int idx = 0; idx < numRows; idx++) { |
| Row row = getRow(idx, horizontally); |
| |
| if (row.grows) { |
| return true; |
| } |
| } |
| |
| return false; |
| |
| } |
| } |