org.eclipse.ui.forms.widgets.TableWrapLayout.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ui.forms.widgets.TableWrapLayout.java

Source

/*******************************************************************************
 *  Copyright (c) 2000, 2015 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.forms.widgets;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

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;

/**
 * This implementation of the layout algorithm attempts to position controls in
 * the composite using a two-pass autolayout HTML table algorithm recommended by
 * HTML 4.01 W3C specification (see
 * http://www.w3.org/TR/html4/appendix/notes.html#h-B.5.2.2). The main
 * differences with GridLayout is that it has two passes and that width and
 * height are not calculated in the same pass.
 * <p>
 * The advantage of the algorithm over GridLayout is that it is capable of
 * flowing text controls capable of line wrap. These controls do not have
 * natural 'preferred size'. Instead, they are capable of providing the required
 * height if the width is set. Consequently, this algorithm first calculates the
 * widths that will be assigned to columns, and then passes those widths to the
 * controls to calculate the height. When a composite with this layout is a
 * child of the scrolling composite, they should interact in such a way that
 * reduction in the scrolling composite width results in the reflow and increase
 * of the overall height.
 * <p>
 * If none of the columns contain expandable and wrappable controls, the
 * end-result will be similar to the one provided by GridLayout. The difference
 * will show up for layouts that contain controls whose minimum and maximum
 * widths are not the same.
 *
 * @see TableWrapData
 * @since 3.0
 */
public final class TableWrapLayout extends Layout implements ILayoutExtension {
    /**
     * Number of columns to use when positioning children (default is 1).
     */
    public int numColumns = 1;

    /**
     * Left margin variable (default is 5).
     */
    public int leftMargin = 5;

    /**
     * Right margin variable (default is 5).
     */
    public int rightMargin = 5;

    /**
     * Top margin variable (default is 5).
     */
    public int topMargin = 5;

    /**
     * Botom margin variable (default is 5).
     */
    public int bottomMargin = 5;

    /**
     * Horizontal spacing (default is 5).
     */
    public int horizontalSpacing = 5;

    /**
     * Vertical spacing (default is 5).
     */
    public int verticalSpacing = 5;

    /**
     * If set to <code>true</code>, all the columns will have the same width.
     * Otherwise, column widths will be computed based on controls in them and
     * their layout data (default is <code>false</code>).
     */
    public boolean makeColumnsEqualWidth = false;

    private boolean initialLayout = true;

    private Vector<TableWrapData[]> grid;

    private Hashtable<Control, RowSpan> rowspans;

    private int[] minColumnWidths, maxColumnWidths;

    private int widestColumnWidth;

    private int[] growingColumns;

    private int[] growingRows;

    private LayoutCache cache = new LayoutCache();

    private class RowSpan {
        Control child;

        int row;

        int height;

        int totalHeight;

        public RowSpan(Control child, int column, int row) {
            this.child = child;
            this.row = row;
        }

        /*
         * Updates this row span's height with the given one if it is within
         * this span.
         */
        public void update(int currentRow, int rowHeight) {
            TableWrapData td = (TableWrapData) child.getLayoutData();
            // is currentRow within this span?
            if (currentRow >= row && currentRow < row + td.rowspan) {
                totalHeight += rowHeight;
                if (currentRow > row)
                    totalHeight += verticalSpacing;
            }
        }

        public int getRequiredHeightIncrease() {
            if (totalHeight < height)
                return height - totalHeight;
            return 0;
        }
    }

    /**
     * Implements ILayoutExtension. Should not be called directly.
     *
     * @see ILayoutExtension
     */
    @Override
    public int computeMinimumWidth(Composite parent, boolean changed) {

        Control[] children = parent.getChildren();
        if (changed) {
            cache.flush();
        }

        cache.setControls(children);

        changed = true;
        initializeIfNeeded(parent, changed);
        if (initialLayout) {
            changed = true;
            initialLayout = false;
        }
        if (grid == null || changed) {
            changed = true;
            grid = new Vector<>();
            createGrid(parent);
        }
        if (minColumnWidths == null)
            minColumnWidths = new int[numColumns];
        for (int i = 0; i < numColumns; i++) {
            minColumnWidths[i] = 0;
        }
        return internalGetMinimumWidth(parent, changed);
    }

    /**
     * Implements ILayoutExtension. Should not be called directly.
     *
     * @see ILayoutExtension
     */
    @Override
    public int computeMaximumWidth(Composite parent, boolean changed) {
        Control[] children = parent.getChildren();
        if (changed) {
            cache.flush();
        }

        cache.setControls(children);

        changed = true;
        initializeIfNeeded(parent, changed);
        if (initialLayout) {
            changed = true;
            initialLayout = false;
        }
        if (grid == null || changed) {
            changed = true;
            grid = new Vector<>();
            createGrid(parent);
        }
        if (maxColumnWidths == null)
            maxColumnWidths = new int[numColumns];
        for (int i = 0; i < numColumns; i++) {
            maxColumnWidths[i] = 0;
        }
        return internalGetMaximumWidth(parent, changed);
    }

    /**
     * @see Layout#layout(Composite, boolean)
     */
    @Override
    protected void layout(Composite parent, boolean changed) {

        Rectangle clientArea = parent.getClientArea();
        Control[] children = parent.getChildren();
        if (changed) {
            cache.flush();
        }

        if (children.length == 0)
            return;

        cache.setControls(children);

        int parentWidth = clientArea.width;
        changed = true;
        initializeIfNeeded(parent, changed);
        if (initialLayout) {
            changed = true;
            initialLayout = false;
        }
        if (grid == null || changed) {
            changed = true;
            grid = new Vector<>();
            createGrid(parent);
        }
        resetColumnWidths();
        int minWidth = internalGetMinimumWidth(parent, changed);
        int maxWidth = internalGetMaximumWidth(parent, changed);
        int tableWidth = parentWidth;
        int[] columnWidths;
        if (parentWidth <= minWidth) {
            tableWidth = minWidth;
            if (makeColumnsEqualWidth) {
                columnWidths = new int[numColumns];
                for (int i = 0; i < numColumns; i++) {
                    columnWidths[i] = widestColumnWidth;
                }
            } else
                columnWidths = minColumnWidths;
        } else if (parentWidth > maxWidth) {
            if (growingColumns.length == 0) {
                tableWidth = maxWidth;
                columnWidths = maxColumnWidths;
            } else {
                columnWidths = new int[numColumns];
                int extra = parentWidth - maxWidth;
                int colExtra = extra / growingColumns.length;
                for (int i = 0; i < numColumns; i++) {
                    columnWidths[i] = maxColumnWidths[i];
                    if (isGrowingColumn(i)) {
                        columnWidths[i] += colExtra;
                    }
                }
            }
        } else {
            columnWidths = new int[numColumns];
            if (makeColumnsEqualWidth) {
                int colSpace = tableWidth - leftMargin - rightMargin;
                colSpace -= (numColumns - 1) * horizontalSpacing;
                int col = colSpace / numColumns;
                for (int i = 0; i < numColumns; i++) {
                    columnWidths[i] = col;
                }
            } else {
                columnWidths = assignExtraSpace(tableWidth, maxWidth, minWidth);
            }
        }
        int y = topMargin + clientArea.y;
        int[] rowHeights = computeRowHeights(children, columnWidths, changed);
        for (int i = 0; i < grid.size(); i++) {
            int rowHeight = rowHeights[i];
            int x = leftMargin + clientArea.x;
            TableWrapData[] row = grid.elementAt(i);
            for (int j = 0; j < numColumns; j++) {
                TableWrapData td = row[j];
                if (td.isItemData) {
                    Control child = children[td.childIndex];
                    placeControl(child, td, x, y, rowHeights, i);
                }
                x += columnWidths[j];
                if (j < numColumns - 1)
                    x += horizontalSpacing;
            }
            y += rowHeight + verticalSpacing;
        }
    }

    int[] computeRowHeights(Control[] children, int[] columnWidths, boolean changed) {
        int[] rowHeights = new int[grid.size()];
        for (int i = 0; i < grid.size(); i++) {
            TableWrapData[] row = grid.elementAt(i);
            rowHeights[i] = 0;
            for (int j = 0; j < numColumns; j++) {
                TableWrapData td = row[j];
                if (td.isItemData == false) {
                    continue;
                }
                Control child = children[td.childIndex];
                int span = td.colspan;
                int cwidth = 0;
                for (int k = j; k < j + span; k++) {
                    cwidth += columnWidths[k];
                    if (k < j + span - 1)
                        cwidth += horizontalSpacing;
                }
                Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight);
                td.compWidth = cwidth;
                if (td.heightHint != SWT.DEFAULT) {
                    size = new Point(size.x, td.heightHint);
                }
                td.compSize = size;
                RowSpan rowspan = rowspans.get(child);
                if (rowspan == null) {
                    rowHeights[i] = Math.max(rowHeights[i], size.y);
                } else
                    rowspan.height = size.y;
            }
            updateRowSpans(i, rowHeights[i]);
        }
        for (Enumeration<RowSpan> enm = rowspans.elements(); enm.hasMoreElements();) {
            RowSpan rowspan = enm.nextElement();
            int increase = rowspan.getRequiredHeightIncrease();
            if (increase == 0)
                continue;
            TableWrapData td = (TableWrapData) rowspan.child.getLayoutData();
            int ngrowing = 0;
            int[] affectedRows = new int[grid.size()];
            for (int growingRow : growingRows) {
                if (growingRow >= rowspan.row && growingRow < rowspan.row + td.rowspan) {
                    affectedRows[ngrowing++] = growingRow;
                }
            }
            if (ngrowing == 0) {
                ngrowing = 1;
                affectedRows[0] = rowspan.row + td.rowspan - 1;
            }
            increase += increase % ngrowing;
            int perRowIncrease = increase / ngrowing;
            for (int i = 0; i < ngrowing; i++) {
                int growingRow = affectedRows[i];
                rowHeights[growingRow] += perRowIncrease;
            }
        }
        return rowHeights;
    }

    boolean isGrowingColumn(int col) {
        if (growingColumns == null)
            return false;
        for (int growingColumn : growingColumns) {
            if (col == growingColumn)
                return true;
        }
        return false;
    }

    int[] assignExtraSpace(int tableWidth, int maxWidth, int minWidth) {
        int fixedPart = leftMargin + rightMargin + (numColumns - 1) * horizontalSpacing;
        int D = maxWidth - minWidth;
        int W = tableWidth - minWidth;
        int widths[] = new int[numColumns];
        int rem = 0;
        for (int i = 0; i < numColumns; i++) {
            int cmin = minColumnWidths[i];
            int cmax = maxColumnWidths[i];
            int d = cmax - cmin;
            int extra = D != 0 ? (d * W) / D : 0;
            if (i < numColumns - 1) {
                widths[i] = cmin + extra;
                rem += widths[i];
            } else {
                widths[i] = tableWidth - fixedPart - rem;
            }
        }
        return widths;
    }

    Point computeSize(int childIndex, int width, int indent, int maxWidth, int maxHeight) {
        int widthArg = width - indent;
        SizeCache controlCache = cache.getCache(childIndex);
        if (!isWrap(controlCache.getControl()))
            widthArg = SWT.DEFAULT;
        Point size = controlCache.computeSize(widthArg, SWT.DEFAULT);
        if (maxWidth != SWT.DEFAULT)
            size.x = Math.min(size.x, maxWidth);
        if (maxHeight != SWT.DEFAULT)
            size.y = Math.min(size.y, maxHeight);
        size.x += indent;
        return size;
    }

    void placeControl(Control control, TableWrapData td, int x, int y, int[] rowHeights, int row) {
        int xloc = x + td.indent;
        int yloc = y;
        int height = td.compSize.y;
        int colWidth = td.compWidth - td.indent;
        int width = td.compSize.x - td.indent;
        width = Math.min(width, colWidth);
        int slotHeight = rowHeights[row];
        RowSpan rowspan = rowspans.get(control);
        if (rowspan != null) {
            slotHeight = 0;
            for (int i = row; i < row + td.rowspan; i++) {
                if (i > row)
                    slotHeight += verticalSpacing;
                slotHeight += rowHeights[i];
            }
        }
        // align horizontally
        if (td.align == TableWrapData.CENTER) {
            xloc = x + colWidth / 2 - width / 2;
        } else if (td.align == TableWrapData.RIGHT) {
            xloc = x + colWidth - width;
        } else if (td.align == TableWrapData.FILL) {
            width = colWidth;
        }
        // align vertically
        if (td.valign == TableWrapData.MIDDLE) {
            yloc = y + slotHeight / 2 - height / 2;
        } else if (td.valign == TableWrapData.BOTTOM) {
            yloc = y + slotHeight - height;
        } else if (td.valign == TableWrapData.FILL) {
            height = slotHeight;
        }
        control.setBounds(xloc, yloc, width, height);
    }

    void createGrid(Composite composite) {
        int row, column, rowFill, columnFill;
        Control[] children;
        TableWrapData spacerSpec;
        Vector<Integer> growingCols = new Vector<>();
        Vector<Integer> growingRows = new Vector<>();
        rowspans = new Hashtable<>();
        //
        children = composite.getChildren();
        if (children.length == 0)
            return;
        //
        grid.addElement(createEmptyRow());
        row = 0;
        column = 0;
        // Loop through the children and place their associated layout specs in
        // the
        // grid. Placement occurs left to right, top to bottom (i.e., by row).
        for (int i = 0; i < children.length; i++) {
            // Find the first available spot in the grid.
            Control child = children[i];
            TableWrapData spec = (TableWrapData) child.getLayoutData();
            while (grid.elementAt(row)[column] != null) {
                column = column + 1;
                if (column >= numColumns) {
                    row = row + 1;
                    column = 0;
                    if (row >= grid.size()) {
                        grid.addElement(createEmptyRow());
                    }
                }
            }
            // See if the place will support the widget's horizontal span. If
            // not, go to the
            // next row.
            if (column + spec.colspan - 1 >= numColumns) {
                grid.addElement(createEmptyRow());
                row = row + 1;
                column = 0;
            }
            // The vertical span for the item will be at least 1. If it is > 1,
            // add other rows to the grid.
            if (spec.rowspan > 1) {
                rowspans.put(child, new RowSpan(child, column, row));
            }
            for (int j = 2; j <= spec.rowspan; j++) {
                if (row + j > grid.size()) {
                    grid.addElement(createEmptyRow());
                }
            }
            // Store the layout spec. Also cache the childIndex. NOTE: That we
            // assume the children of a
            // composite are maintained in the order in which they are created
            // and added to the composite.
            grid.elementAt(row)[column] = spec;
            spec.childIndex = i;
            if (spec.grabHorizontal) {
                updateGrowingColumns(growingCols, spec, column);
            }
            if (spec.grabVertical) {
                updateGrowingRows(growingRows, spec, row);
            }
            // Put spacers in the grid to account for the item's vertical and
            // horizontal
            // span.
            rowFill = spec.rowspan - 1;
            columnFill = spec.colspan - 1;
            for (int r = 1; r <= rowFill; r++) {
                for (int c = 0; c < spec.colspan; c++) {
                    spacerSpec = new TableWrapData();
                    spacerSpec.isItemData = false;
                    grid.elementAt(row + r)[column + c] = spacerSpec;
                }
            }
            for (int c = 1; c <= columnFill; c++) {
                for (int r = 0; r < spec.rowspan; r++) {
                    spacerSpec = new TableWrapData();
                    spacerSpec.isItemData = false;
                    grid.elementAt(row + r)[column + c] = spacerSpec;
                }
            }
            column = column + spec.colspan - 1;
        }
        // Fill out empty grid cells with spacers.
        for (int k = column + 1; k < numColumns; k++) {
            spacerSpec = new TableWrapData();
            spacerSpec.isItemData = false;
            grid.elementAt(row)[k] = spacerSpec;
        }
        for (int k = row + 1; k < grid.size(); k++) {
            spacerSpec = new TableWrapData();
            spacerSpec.isItemData = false;
            grid.elementAt(k)[column] = spacerSpec;
        }
        growingColumns = new int[growingCols.size()];
        for (int i = 0; i < growingCols.size(); i++) {
            growingColumns[i] = growingCols.get(i).intValue();
        }
        this.growingRows = new int[growingRows.size()];
        for (int i = 0; i < growingRows.size(); i++) {
            this.growingRows[i] = growingRows.get(i).intValue();
        }
    }

    private void updateGrowingColumns(Vector<Integer> growingColumns, TableWrapData spec, int column) {
        int affectedColumn = column + spec.colspan - 1;
        for (int i = 0; i < growingColumns.size(); i++) {
            Integer col = growingColumns.get(i);
            if (col.intValue() == affectedColumn)
                return;
        }
        growingColumns.add(Integer.valueOf(affectedColumn));
    }

    private void updateGrowingRows(Vector<Integer> growingRows, TableWrapData spec, int row) {
        int affectedRow = row + spec.rowspan - 1;
        for (int i = 0; i < growingRows.size(); i++) {
            Integer irow = growingRows.get(i);
            if (irow.intValue() == affectedRow)
                return;
        }
        growingRows.add(Integer.valueOf(affectedRow));
    }

    private TableWrapData[] createEmptyRow() {
        TableWrapData[] row = new TableWrapData[numColumns];
        for (int i = 0; i < numColumns; i++)
            row[i] = null;
        return row;
    }

    /**
     * @see Layout#computeSize(Composite, int, int, boolean)
     */
    @Override
    protected Point computeSize(Composite parent, int wHint, int hHint, boolean changed) {
        Control[] children = parent.getChildren();
        if (changed) {
            cache.flush();
        }
        if (children.length == 0) {
            return new Point(0, 0);
        }
        cache.setControls(children);

        int parentWidth = wHint;
        changed = true;
        initializeIfNeeded(parent, changed);
        if (initialLayout) {
            changed = true;
            initialLayout = false;
        }
        if (grid == null || changed) {
            changed = true;
            grid = new Vector<>();
            createGrid(parent);
        }
        resetColumnWidths();
        int minWidth = internalGetMinimumWidth(parent, changed);
        int maxWidth = internalGetMaximumWidth(parent, changed);

        if (wHint == SWT.DEFAULT)
            parentWidth = maxWidth;

        int tableWidth = parentWidth;
        int[] columnWidths;
        if (parentWidth <= minWidth) {
            tableWidth = minWidth;
            if (makeColumnsEqualWidth) {
                columnWidths = new int[numColumns];
                for (int i = 0; i < numColumns; i++) {
                    columnWidths[i] = widestColumnWidth;
                }
            } else
                columnWidths = minColumnWidths;
        } else if (parentWidth >= maxWidth) {
            if (makeColumnsEqualWidth) {
                columnWidths = new int[numColumns];
                int colSpace = parentWidth - leftMargin - rightMargin;
                colSpace -= (numColumns - 1) * horizontalSpacing;
                int col = colSpace / numColumns;
                for (int i = 0; i < numColumns; i++) {
                    columnWidths[i] = col;
                }
            } else {
                tableWidth = maxWidth;
                columnWidths = maxColumnWidths;
            }
        } else {
            columnWidths = new int[numColumns];
            if (makeColumnsEqualWidth) {
                int colSpace = tableWidth - leftMargin - rightMargin;
                colSpace -= (numColumns - 1) * horizontalSpacing;
                int col = colSpace / numColumns;
                for (int i = 0; i < numColumns; i++) {
                    columnWidths[i] = col;
                }
            } else {
                columnWidths = assignExtraSpace(tableWidth, maxWidth, minWidth);
            }
        }
        int totalHeight = 0;
        int innerHeight = 0;
        // compute widths
        for (int i = 0; i < grid.size(); i++) {
            TableWrapData[] row = grid.elementAt(i);
            // assign widths, calculate heights
            int rowHeight = 0;
            for (int j = 0; j < numColumns; j++) {
                TableWrapData td = row[j];
                if (td.isItemData == false) {
                    continue;
                }
                Control child = children[td.childIndex];
                int span = td.colspan;
                int cwidth = 0;
                for (int k = j; k < j + span; k++) {
                    if (k > j)
                        cwidth += horizontalSpacing;
                    cwidth += columnWidths[k];
                }
                int cy = td.heightHint;
                if (cy == SWT.DEFAULT) {
                    Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight);
                    cy = size.y;
                }
                RowSpan rowspan = rowspans.get(child);
                if (rowspan != null) {
                    // don't take the height of this child into acount
                    // because it spans multiple rows
                    rowspan.height = cy;
                } else {
                    rowHeight = Math.max(rowHeight, cy);
                }
            }
            updateRowSpans(i, rowHeight);
            if (i > 0)
                innerHeight += verticalSpacing;
            innerHeight += rowHeight;
        }
        if (!rowspans.isEmpty())
            innerHeight = compensateForRowSpans(innerHeight);
        totalHeight = topMargin + innerHeight + bottomMargin;
        return new Point(tableWidth, totalHeight);
    }

    private void updateRowSpans(int row, int rowHeight) {
        if (rowspans == null || rowspans.size() == 0)
            return;
        for (Enumeration<RowSpan> enm = rowspans.elements(); enm.hasMoreElements();) {
            RowSpan rowspan = enm.nextElement();
            rowspan.update(row, rowHeight);
        }
    }

    private int compensateForRowSpans(int totalHeight) {
        for (Enumeration<RowSpan> enm = rowspans.elements(); enm.hasMoreElements();) {
            RowSpan rowspan = enm.nextElement();
            totalHeight += rowspan.getRequiredHeightIncrease();
        }
        return totalHeight;
    }

    int internalGetMinimumWidth(Composite parent, boolean changed) {
        if (changed)
            //calculateMinimumColumnWidths(parent, true);
            calculateColumnWidths(parent, minColumnWidths, false, true, makeColumnsEqualWidth);
        int minimumWidth = 0;
        widestColumnWidth = 0;
        if (makeColumnsEqualWidth) {
            for (int i = 0; i < numColumns; i++) {
                widestColumnWidth = Math.max(widestColumnWidth, minColumnWidths[i]);
            }
        }
        for (int i = 0; i < numColumns; i++) {
            if (i > 0)
                minimumWidth += horizontalSpacing;
            if (makeColumnsEqualWidth)
                minimumWidth += widestColumnWidth;
            else
                minimumWidth += minColumnWidths[i];
        }
        // add margins
        minimumWidth += leftMargin + rightMargin;
        return minimumWidth;
    }

    int internalGetMaximumWidth(Composite parent, boolean changed) {
        if (changed)
            //calculateMaximumColumnWidths(parent, true);
            calculateColumnWidths(parent, maxColumnWidths, true, true, makeColumnsEqualWidth);
        int maximumWidth = 0;
        for (int i = 0; i < numColumns; i++) {
            if (i > 0)
                maximumWidth += horizontalSpacing;
            maximumWidth += maxColumnWidths[i];
        }
        // add margins
        maximumWidth += leftMargin + rightMargin;
        return maximumWidth;
    }

    void resetColumnWidths() {
        if (minColumnWidths == null)
            minColumnWidths = new int[numColumns];
        if (maxColumnWidths == null)
            maxColumnWidths = new int[numColumns];
        for (int i = 0; i < numColumns; i++) {
            minColumnWidths[i] = 0;
        }
        for (int i = 0; i < numColumns; i++) {
            maxColumnWidths[i] = 0;
        }
    }

    void calculateColumnWidths(Composite parent, int[] columnWidths, boolean max, boolean changed,
            boolean makeColumnsEqualWidth2) {
        boolean secondPassNeeded = false;
        int widestColumnWidth = 0;
        for (int i = 0; i < grid.size(); i++) {
            TableWrapData[] row = grid.elementAt(i);
            for (int j = 0; j < numColumns; j++) {
                TableWrapData td = row[j];
                if (td.isItemData == false)
                    continue;

                if (td.colspan > 1) {
                    // we will not do controls with multiple column span
                    // here - increment and continue
                    secondPassNeeded = true;
                    j += td.colspan - 1;
                    continue;
                }

                SizeCache childCache = cache.getCache(td.childIndex);
                // !!
                int width = max ? childCache.computeMaximumWidth() : childCache.computeMinimumWidth();
                if (td.maxWidth != SWT.DEFAULT)
                    width = Math.min(width, td.maxWidth);

                width += td.indent;
                columnWidths[j] = Math.max(columnWidths[j], width);
                widestColumnWidth = Math.max(widestColumnWidth, columnWidths[j]);
            }
        }
        if (makeColumnsEqualWidth) {
            for (int i = 0; i < numColumns; i++) {
                columnWidths[i] = widestColumnWidth;
            }
        }
        if (!secondPassNeeded)
            return;

        // Second pass for controls with multi-column horizontal span
        for (int i = 0; i < grid.size(); i++) {
            TableWrapData[] row = grid.elementAt(i);
            for (int j = 0; j < numColumns; j++) {
                TableWrapData td = row[j];
                if (td.isItemData == false || td.colspan == 1)
                    continue;

                SizeCache childCache = cache.getCache(td.childIndex);
                int width = max ? childCache.computeMaximumWidth() : childCache.computeMinimumWidth();
                if (td.maxWidth != SWT.DEFAULT)
                    width = Math.min(width, td.maxWidth);

                width += td.indent;
                // check if the current width is enough to
                // support the control; if not, add the delta to
                // the last column or to all the growing columns, if present
                int current = 0;
                for (int k = j; k < j + td.colspan; k++) {
                    if (k > j)
                        current += horizontalSpacing;
                    current += columnWidths[k];
                }
                if (width <= current) {
                    // we are ok - nothing to do here
                } else {
                    int ndiv = 0;
                    if (growingColumns != null) {
                        for (int k = j; k < j + td.colspan; k++) {
                            if (isGrowingColumn(k)) {
                                ndiv++;
                            }
                        }
                    }
                    if (ndiv == 0) {
                        // add the delta to the last column
                        columnWidths[j + td.colspan - 1] += width - current;
                    } else {
                        // distribute the delta to the growing
                        // columns
                        int percolumn = (width - current) / ndiv;
                        if ((width - current) % ndiv > 0)
                            percolumn++;
                        for (int k = j; k < j + td.colspan; k++) {
                            if (isGrowingColumn(k))
                                columnWidths[k] += percolumn;
                        }
                    }
                }
            }
        }
    }

    boolean isWrap(Control control) {
        if (control instanceof Composite && ((Composite) control).getLayout() instanceof ILayoutExtension)
            return true;
        return (control.getStyle() & SWT.WRAP) != 0;
    }

    private void initializeIfNeeded(Composite parent, boolean changed) {
        if (changed)
            initialLayout = true;
        if (initialLayout) {
            initializeLayoutData(parent);
            initialLayout = false;
        }
    }

    void initializeLayoutData(Composite composite) {
        Control[] children = composite.getChildren();
        for (Control child : children) {
            if (child.getLayoutData() == null) {
                child.setLayoutData(new TableWrapData());
            }
        }
    }
}