diff org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children dbfb303e8fb0
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.ui.forms/src/org/eclipse/ui/forms/widgets/TableWrapLayout.d	Sat Mar 14 18:23:29 2009 +0100
@@ -0,0 +1,888 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.ILayoutExtension;
+import org.eclipse.ui.forms.widgets.LayoutCache;
+import org.eclipse.ui.forms.widgets.SizeCache;
+
+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;
+
+import java.lang.all;
+import java.util.Enumeration;
+import java.util.Set;
+
+/**
+ * This implementation of the layout algorithm attempts to position controls in
+ * the composite using a two-pass autolayout HTML table altorithm recommeded 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 : Layout, ILayoutExtension {
+
+    public alias Layout.computeSize computeSize;
+    /**
+     * 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 bool makeColumnsEqualWidth = false;
+
+    private bool initialLayout = true;
+
+    private Vector grid = null;
+
+    private Hashtable rowspans;
+
+    private int[] minColumnWidths, maxColumnWidths;
+
+    private int widestColumnWidth;
+
+    private int[] growingColumns;
+
+    private int[] growingRows;
+
+    private LayoutCache cache;
+
+    private class RowSpan {
+        Control child;
+
+        int row;
+
+        int column;
+
+        int height;
+
+        int totalHeight;
+
+        public this(Control child, int column, int row) {
+            this.child = child;
+            this.column = column;
+            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 = cast(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;
+        }
+    }
+
+    this(){
+        cache = new LayoutCache();
+    }
+
+    /**
+     * Implements ILayoutExtension. Should not be called directly.
+     *
+     * @see ILayoutExtension
+     */
+    public int computeMinimumWidth(Composite parent, bool 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 is null || changed) {
+            changed = true;
+            grid = new Vector();
+            createGrid(parent);
+        }
+        if (minColumnWidths is 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
+     */
+    public int computeMaximumWidth(Composite parent, bool 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 is null || changed) {
+            changed = true;
+            grid = new Vector();
+            createGrid(parent);
+        }
+        if (maxColumnWidths is null)
+            maxColumnWidths = new int[numColumns];
+        for (int i = 0; i < numColumns; i++) {
+            maxColumnWidths[i] = 0;
+        }
+        return internalGetMaximumWidth(parent, changed);
+    }
+
+    /**
+     * @see Layout#layout(Composite, bool)
+     */
+    protected void layout(Composite parent, bool changed) {
+
+        Rectangle clientArea = parent.getClientArea();
+        Control[] children = parent.getChildren();
+        if (changed) {
+            cache.flush();
+        }
+
+        if (children.length is 0)
+            return;
+
+        cache.setControls(children);
+
+        int parentWidth = clientArea.width;
+        changed = true;
+        initializeIfNeeded(parent, changed);
+        if (initialLayout) {
+            changed = true;
+            initialLayout = false;
+        }
+        if (grid is 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 is 0) {
+                tableWidth = maxWidth;
+                columnWidths = maxColumnWidths;
+            } else {
+                columnWidths = new int[numColumns];
+                int colSpace = tableWidth - leftMargin - rightMargin;
+                colSpace -= (numColumns - 1) * horizontalSpacing;
+                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 = arrayFromObject!(TableWrapData)( 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,
+            bool changed) {
+        int[] rowHeights = new int[grid.size()];
+        for (int i = 0; i < grid.size(); i++) {
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            rowHeights[i] = 0;
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is 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 !is SWT.DEFAULT) {
+                    size = new Point(size.x, td.heightHint);
+                }
+                td.compSize = size;
+                RowSpan rowspan = cast(RowSpan) rowspans.get(child);
+                if (rowspan is null) {
+                    rowHeights[i] = Math.max(rowHeights[i], size.y);
+                } else
+                    rowspan.height = size.y;
+            }
+            updateRowSpans(i, rowHeights[i]);
+        }
+        for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
+            RowSpan rowspan = cast(RowSpan) enm.nextElement();
+            int increase = rowspan.getRequiredHeightIncrease();
+            if (increase is 0)
+                continue;
+            TableWrapData td = cast(TableWrapData) rowspan.child.getLayoutData();
+            int ngrowing = 0;
+            int[] affectedRows = new int[grid.size()];
+            for (int i = 0; i < growingRows.length; i++) {
+                int growingRow = growingRows[i];
+                if (growingRow >= rowspan.row
+                        && growingRow < rowspan.row + td.rowspan) {
+                    affectedRows[ngrowing++] = growingRow;
+                }
+            }
+            if (ngrowing is 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;
+    }
+
+    bool isGrowingColumn(int col) {
+        if (growingColumns is null)
+            return false;
+        for (int i = 0; i < growingColumns.length; i++) {
+            if (col is growingColumns[i])
+                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 - fixedPart - 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 !is 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 !is SWT.DEFAULT)
+            size.x = Math.min(size.x, maxWidth);
+        if (maxHeight !is 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 = cast(RowSpan) rowspans.get(control);
+        if (rowspan !is 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_ is TableWrapData.CENTER) {
+            xloc = x + colWidth / 2 - width / 2;
+        } else if (td.align_ is TableWrapData.RIGHT) {
+            xloc = x + colWidth - width;
+        } else if (td.align_ is TableWrapData.FILL) {
+            width = colWidth;
+        }
+        // align vertically
+        if (td.valign is TableWrapData.MIDDLE) {
+            yloc = y + slotHeight / 2 - height / 2;
+        } else if (td.valign is TableWrapData.BOTTOM) {
+            yloc = y + slotHeight - height;
+        } else if (td.valign is TableWrapData.FILL) {
+            height = slotHeight;
+        }
+        control.setBounds(xloc, yloc, width, height);
+    }
+
+    void createGrid(Composite composite) {
+        int row, column, rowFill, columnFill;
+        Control[] children;
+        TableWrapData spacerSpec;
+        Vector growingCols = new Vector();
+        Vector growingRows = new Vector();
+        rowspans = new Hashtable();
+        //
+        children = composite.getChildren();
+        if (children.length is 0)
+            return;
+        //
+        grid.addElement( new ArrayWrapperObject(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 = cast(TableWrapData) child.getLayoutData();
+            while (arrayFromObject!(TableWrapData)( grid.elementAt(row))[column] !is null) {
+                column = column + 1;
+                if (column >= numColumns) {
+                    row = row + 1;
+                    column = 0;
+                    if (row >= grid.size()) {
+                        grid.addElement(new ArrayWrapperObject(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(new ArrayWrapperObject(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(new ArrayWrapperObject(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.
+            (cast(ArrayWrapperObject) grid.elementAt(row)).array[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;
+                    (cast(ArrayWrapperObject) grid.elementAt(row + r)).array[column + c] = spacerSpec;
+                }
+            }
+            for (int c = 1; c <= columnFill; c++) {
+                for (int r = 0; r < spec.rowspan; r++) {
+                    spacerSpec = new TableWrapData();
+                    spacerSpec.isItemData = false;
+                    (cast(ArrayWrapperObject) grid.elementAt(row + r)).array[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;
+            (cast(ArrayWrapperObject) grid.elementAt(row)).array[k] = spacerSpec;
+        }
+        for (int k = row + 1; k < grid.size(); k++) {
+            spacerSpec = new TableWrapData();
+            spacerSpec.isItemData = false;
+            (cast(ArrayWrapperObject) grid.elementAt(k)).array[column] = spacerSpec;
+        }
+        growingColumns = new int[growingCols.size()];
+        for (int i = 0; i < growingCols.size(); i++) {
+            growingColumns[i] = (cast(Integer) growingCols.get(i)).intValue();
+        }
+        this.growingRows = new int[growingRows.size()];
+        for (int i = 0; i < growingRows.size(); i++) {
+            this.growingRows[i] = (cast(Integer) growingRows.get(i)).intValue();
+        }
+    }
+
+    private void updateGrowingColumns(Vector growingColumns,
+            TableWrapData spec, int column) {
+        int affectedColumn = column + spec.colspan - 1;
+        for (int i = 0; i < growingColumns.size(); i++) {
+            Integer col = cast(Integer) growingColumns.get(i);
+            if (col.intValue() is affectedColumn)
+                return;
+        }
+        growingColumns.add(new Integer(affectedColumn));
+    }
+
+    private void updateGrowingRows(Vector growingRows, TableWrapData spec,
+            int row) {
+        int affectedRow = row + spec.rowspan - 1;
+        for (int i = 0; i < growingRows.size(); i++) {
+            Integer irow = cast(Integer) growingRows.get(i);
+            if (irow.intValue() is affectedRow)
+                return;
+        }
+        growingRows.add(new Integer(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, bool)
+     */
+    /+protected+/ override Point computeSize(Composite parent, int wHint, int hHint,
+            bool changed) {
+        Control[] children = parent.getChildren();
+        if (changed) {
+            cache.flush();
+        }
+        if (children.length is 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 is null || changed) {
+            changed = true;
+            grid = new Vector();
+            createGrid(parent);
+        }
+        resetColumnWidths();
+        int minWidth = internalGetMinimumWidth(parent, changed);
+        int maxWidth = internalGetMaximumWidth(parent, changed);
+
+        if (wHint is 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 = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            // assign widths, calculate heights
+            int rowHeight = 0;
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is 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 is SWT.DEFAULT) {
+                    Point size = computeSize(td.childIndex, cwidth, td.indent, td.maxWidth, td.maxHeight);
+                    cy = size.y;
+                }
+                RowSpan rowspan = cast(RowSpan) rowspans.get(child);
+                if (rowspan !is 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 is null || rowspans.size() is 0)
+            return;
+        for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
+            RowSpan rowspan = cast(RowSpan) enm.nextElement();
+            rowspan.update(row, rowHeight);
+        }
+    }
+
+    private int compensateForRowSpans(int totalHeight) {
+         for (Enumeration enm = rowspans.elements(); enm.hasMoreElements();) {
+            RowSpan rowspan = cast(RowSpan) enm.nextElement();
+            totalHeight += rowspan.getRequiredHeightIncrease();
+        }
+        return totalHeight;
+    }
+
+    int internalGetMinimumWidth(Composite parent, bool changed) {
+        if (changed)
+            //calculateMinimumColumnWidths(parent, true);
+            calculateColumnWidths(parent, minColumnWidths, false, true);
+        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, bool changed) {
+        if (changed)
+            //calculateMaximumColumnWidths(parent, true);
+            calculateColumnWidths(parent, maxColumnWidths, true, true);
+        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 is null)
+            minColumnWidths = new int[numColumns];
+        if (maxColumnWidths is 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, bool max, bool changed) {
+        bool secondPassNeeded=false;
+        for (int i = 0; i < grid.size(); i++) {
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is 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 !is SWT.DEFAULT)
+                    width = Math.min(width, td.maxWidth);
+
+                width += td.indent;
+                columnWidths[j] = Math.max(columnWidths[j], width);
+            }
+        }
+        if (!secondPassNeeded) return;
+
+        // Second pass for controls with multi-column horizontal span
+        for (int i = 0; i < grid.size(); i++) {
+            TableWrapData[] row = arrayFromObject!(TableWrapData)( grid.elementAt(i));
+            for (int j = 0; j < numColumns; j++) {
+                TableWrapData td = row[j];
+                if (td.isItemData is false || td.colspan is 1)
+                    continue;
+
+                SizeCache childCache = cache.getCache(td.childIndex);
+                int width = max?childCache.computeMaximumWidth():childCache.computeMinimumWidth();
+                if (td.maxWidth !is 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 !is null) {
+                        for (int k = j; k < j + td.colspan; k++) {
+                            if (isGrowingColumn(k)) {
+                                ndiv++;
+                            }
+                        }
+                    }
+                    if (ndiv is 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;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    bool isWrap(Control control) {
+        if (null !is cast(Composite)control
+                && null !is cast(ILayoutExtension)((cast(Composite) control).getLayout()) )
+            return true;
+        return (control.getStyle() & SWT.WRAP) !is 0;
+    }
+
+    private void initializeIfNeeded(Composite parent, bool changed) {
+        if (changed)
+            initialLayout = true;
+        if (initialLayout) {
+            initializeLayoutData(parent);
+            initialLayout = false;
+        }
+    }
+
+    void initializeLayoutData(Composite composite) {
+        Control[] children = composite.getChildren();
+        for (int i = 0; i < children.length; i++) {
+            Control child = children[i];
+            if (child.getLayoutData() is null) {
+                child.setLayoutData(new TableWrapData());
+            }
+        }
+    }
+}