Version 2.0.0: update sources
[jvcard.git] / src / com / googlecode / lanterna / gui2 / GridLayout.java
diff --git a/src/com/googlecode/lanterna/gui2/GridLayout.java b/src/com/googlecode/lanterna/gui2/GridLayout.java
deleted file mode 100644 (file)
index 3f2a4d4..0000000
+++ /dev/null
@@ -1,833 +0,0 @@
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- * 
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- * 
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.*;
-
-/**
- * This emulates the behaviour of the GridLayout in SWT (as opposed to the one in AWT/Swing). I originally ported the
- * SWT class itself but due to licensing concerns (the eclipse license is not compatible with LGPL) I was advised not to
- * do that. This is a partial implementation and some of the semantics have changed, but in general it works the same
- * way so the SWT documentation will generally match.
- * <p>
- * You use the {@code GridLayout} by specifying a number of columns you want your grid to have and then when you add
- * components, you assign {@code LayoutData} to these components using the different static methods in this class
- * ({@code createLayoutData(..)}). You can set components to span both rows and columns, as well as defining how to
- * distribute the available space.
- */
-public class GridLayout implements LayoutManager {
-    /**
-     * The enum is used to specify where in a grid cell a component should be placed, in the case that the preferred
-     * size of the component is smaller than the space in the cell. This class will generally use two alignments, one
-     * for horizontal and one for vertical.
-     */
-    public enum Alignment {
-        /**
-         * Place the component at the start of the cell (horizontally or vertically) and leave whatever space is left
-         * after the preferred size empty.
-         */
-        BEGINNING,
-        /**
-         * Place the component at the middle of the cell (horizontally or vertically) and leave the space before and
-         * after empty.
-         */
-        CENTER,
-        /**
-         * Place the component at the end of the cell (horizontally or vertically) and leave whatever space is left
-         * before the preferred size empty.
-         */
-        END,
-        /**
-         * Force the component to be the same size as the table cell
-         */
-        FILL,
-        ;
-    }
-
-    static class GridLayoutData implements LayoutData {
-        final Alignment horizontalAlignment;
-        final Alignment verticalAlignment;
-        final boolean grabExtraHorizontalSpace;
-        final boolean grabExtraVerticalSpace;
-        final int horizontalSpan;
-        final int verticalSpan;
-
-        private GridLayoutData(
-                Alignment horizontalAlignment,
-                Alignment verticalAlignment,
-                boolean grabExtraHorizontalSpace,
-                boolean grabExtraVerticalSpace,
-                int horizontalSpan,
-                int verticalSpan) {
-
-            if(horizontalSpan < 1 || verticalSpan < 1) {
-                throw new IllegalArgumentException("Horizontal/Vertical span must be 1 or greater");
-            }
-
-            this.horizontalAlignment = horizontalAlignment;
-            this.verticalAlignment = verticalAlignment;
-            this.grabExtraHorizontalSpace = grabExtraHorizontalSpace;
-            this.grabExtraVerticalSpace = grabExtraVerticalSpace;
-            this.horizontalSpan = horizontalSpan;
-            this.verticalSpan = verticalSpan;
-        }
-    }
-
-    private static GridLayoutData DEFAULT = new GridLayoutData(
-            Alignment.BEGINNING,
-            Alignment.BEGINNING,
-            false,
-            false,
-            1,
-            1);
-
-    /**
-     * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
-     * component in case the cell space is larger than the preferred size of the component
-     * @param horizontalAlignment Horizontal alignment strategy
-     * @param verticalAlignment Vertical alignment strategy
-     * @return The layout data object containing the specified alignments
-     */
-    public static LayoutData createLayoutData(Alignment horizontalAlignment, Alignment verticalAlignment) {
-        return createLayoutData(horizontalAlignment, verticalAlignment, false, false);
-    }
-
-    /**
-     * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
-     * component in case the cell space is larger than the preferred size of the component. This method also has fields
-     * for indicating that the component would like to take more space if available to the container. For example, if
-     * the container is assigned is assigned an area of 50x15, but all the child components in the grid together only
-     * asks for 40x10, the remaining 10 columns and 5 rows will be empty. If just a single component asks for extra
-     * space horizontally and/or vertically, the grid will expand out to fill the entire area and the text space will be
-     * assigned to the component that asked for it.
-     *
-     * @param horizontalAlignment Horizontal alignment strategy
-     * @param verticalAlignment Vertical alignment strategy
-     * @param grabExtraHorizontalSpace If set to {@code true}, this component will ask to be assigned extra horizontal
-     *                                 space if there is any to assign
-     * @param grabExtraVerticalSpace If set to {@code true}, this component will ask to be assigned extra vertical
-     *                                 space if there is any to assign
-     * @return The layout data object containing the specified alignments and size requirements
-     */
-    public static LayoutData createLayoutData(
-            Alignment horizontalAlignment,
-            Alignment verticalAlignment,
-            boolean grabExtraHorizontalSpace,
-            boolean grabExtraVerticalSpace) {
-
-        return createLayoutData(horizontalAlignment, verticalAlignment, grabExtraHorizontalSpace, grabExtraVerticalSpace, 1, 1);
-    }
-
-    /**
-     * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
-     * component in case the cell space is larger than the preferred size of the component. This method also has fields
-     * for indicating that the component would like to take more space if available to the container. For example, if
-     * the container is assigned is assigned an area of 50x15, but all the child components in the grid together only
-     * asks for 40x10, the remaining 10 columns and 5 rows will be empty. If just a single component asks for extra
-     * space horizontally and/or vertically, the grid will expand out to fill the entire area and the text space will be
-     * assigned to the component that asked for it. It also puts in data on how many rows and/or columns the component
-     * should span.
-     *
-     * @param horizontalAlignment Horizontal alignment strategy
-     * @param verticalAlignment Vertical alignment strategy
-     * @param grabExtraHorizontalSpace If set to {@code true}, this component will ask to be assigned extra horizontal
-     *                                 space if there is any to assign
-     * @param grabExtraVerticalSpace If set to {@code true}, this component will ask to be assigned extra vertical
-     *                                 space if there is any to assign
-     * @param horizontalSpan How many "cells" this component wants to span horizontally
-     * @param verticalSpan How many "cells" this component wants to span vertically
-     * @return The layout data object containing the specified alignments, size requirements and cell spanning
-     */
-    public static LayoutData createLayoutData(
-            Alignment horizontalAlignment,
-            Alignment verticalAlignment,
-            boolean grabExtraHorizontalSpace,
-            boolean grabExtraVerticalSpace,
-            int horizontalSpan,
-            int verticalSpan) {
-
-        return new GridLayoutData(
-                horizontalAlignment,
-                verticalAlignment,
-                grabExtraHorizontalSpace,
-                grabExtraVerticalSpace,
-                horizontalSpan,
-                verticalSpan);
-    }
-
-    /**
-     * This is a shortcut method that will create a grid layout data object that will expand its cell as much as is can
-     * horizontally and make the component occupy the whole area horizontally and center it vertically
-     * @param horizontalSpan How many cells to span horizontally
-     * @return Layout data object with the specified span and horizontally expanding as much as it can
-     */
-    public static LayoutData createHorizontallyFilledLayoutData(int horizontalSpan) {
-        return createLayoutData(
-                Alignment.FILL,
-                Alignment.CENTER,
-                true,
-                false,
-                horizontalSpan,
-                1);
-    }
-
-    /**
-     * This is a shortcut method that will create a grid layout data object that will expand its cell as much as is can
-     * vertically and make the component occupy the whole area vertically and center it horizontally
-     * @param horizontalSpan How many cells to span vertically
-     * @return Layout data object with the specified span and vertically expanding as much as it can
-     */
-    public static LayoutData createHorizontallyEndAlignedLayoutData(int horizontalSpan) {
-        return createLayoutData(
-                Alignment.END,
-                Alignment.CENTER,
-                true,
-                false,
-                horizontalSpan,
-                1);
-    }
-
-    private final int numberOfColumns;
-    private int horizontalSpacing;
-    private int verticalSpacing;
-    private int topMarginSize;
-    private int bottomMarginSize;
-    private int leftMarginSize;
-    private int rightMarginSize;
-
-    private boolean changed;
-
-    /**
-     * Creates a new {@code GridLayout} with the specified number of columns. Initially, this layout will have a
-     * horizontal spacing of 1 and vertical spacing of 0, with a left and right margin of 1.
-     * @param numberOfColumns Number of columns in this grid
-     */
-    public GridLayout(int numberOfColumns) {
-        this.numberOfColumns = numberOfColumns;
-        this.horizontalSpacing = 1;
-        this.verticalSpacing = 0;
-        this.topMarginSize = 0;
-        this.bottomMarginSize = 0;
-        this.leftMarginSize = 1;
-        this.rightMarginSize = 1;
-        this.changed = true;
-    }
-
-    /**
-     * Returns the horizontal spacing, i.e. the number of empty columns between each cell
-     * @return Horizontal spacing
-     */
-    public int getHorizontalSpacing() {
-        return horizontalSpacing;
-    }
-
-    /**
-     * Sets the horizontal spacing, i.e. the number of empty columns between each cell
-     * @param horizontalSpacing New horizontal spacing
-     * @return Itself
-     */
-    public GridLayout setHorizontalSpacing(int horizontalSpacing) {
-        if(horizontalSpacing < 0) {
-            throw new IllegalArgumentException("Horizontal spacing cannot be less than 0");
-        }
-        this.horizontalSpacing = horizontalSpacing;
-        this.changed = true;
-        return this;
-    }
-
-    /**
-     * Returns the vertical spacing, i.e. the number of empty columns between each row
-     * @return Vertical spacing
-     */
-    public int getVerticalSpacing() {
-        return verticalSpacing;
-    }
-
-    /**
-     * Sets the vertical spacing, i.e. the number of empty columns between each row
-     * @param verticalSpacing New vertical spacing
-     * @return Itself
-     */
-    public GridLayout setVerticalSpacing(int verticalSpacing) {
-        if(verticalSpacing < 0) {
-            throw new IllegalArgumentException("Vertical spacing cannot be less than 0");
-        }
-        this.verticalSpacing = verticalSpacing;
-        this.changed = true;
-        return this;
-    }
-
-    /**
-     * Returns the top margin, i.e. number of empty rows above the first row in the grid
-     * @return Top margin, in number of rows
-     */
-    public int getTopMarginSize() {
-        return topMarginSize;
-    }
-
-    /**
-     * Sets the top margin, i.e. number of empty rows above the first row in the grid
-     * @param topMarginSize Top margin, in number of rows
-     * @return Itself
-     */
-    public GridLayout setTopMarginSize(int topMarginSize) {
-        if(topMarginSize < 0) {
-            throw new IllegalArgumentException("Top margin size cannot be less than 0");
-        }
-        this.topMarginSize = topMarginSize;
-        this.changed = true;
-        return this;
-    }
-
-    /**
-     * Returns the bottom margin, i.e. number of empty rows below the last row in the grid
-     * @return Bottom margin, in number of rows
-     */
-    public int getBottomMarginSize() {
-        return bottomMarginSize;
-    }
-
-    /**
-     * Sets the bottom margin, i.e. number of empty rows below the last row in the grid
-     * @param bottomMarginSize Bottom margin, in number of rows
-     * @return Itself
-     */
-    public GridLayout setBottomMarginSize(int bottomMarginSize) {
-        if(bottomMarginSize < 0) {
-            throw new IllegalArgumentException("Bottom margin size cannot be less than 0");
-        }
-        this.bottomMarginSize = bottomMarginSize;
-        this.changed = true;
-        return this;
-    }
-
-    /**
-     * Returns the left margin, i.e. number of empty columns left of the first column in the grid
-     * @return Left margin, in number of columns
-     */
-    public int getLeftMarginSize() {
-        return leftMarginSize;
-    }
-
-    /**
-     * Sets the left margin, i.e. number of empty columns left of the first column in the grid
-     * @param leftMarginSize Left margin, in number of columns
-     * @return Itself
-     */
-    public GridLayout setLeftMarginSize(int leftMarginSize) {
-        if(leftMarginSize < 0) {
-            throw new IllegalArgumentException("Left margin size cannot be less than 0");
-        }
-        this.leftMarginSize = leftMarginSize;
-        this.changed = true;
-        return this;
-    }
-
-    /**
-     * Returns the right margin, i.e. number of empty columns right of the last column in the grid
-     * @return Right margin, in number of columns
-     */
-    public int getRightMarginSize() {
-        return rightMarginSize;
-    }
-
-    /**
-     * Sets the right margin, i.e. number of empty columns right of the last column in the grid
-     * @param rightMarginSize Right margin, in number of columns
-     * @return Itself
-     */
-    public GridLayout setRightMarginSize(int rightMarginSize) {
-        if(rightMarginSize < 0) {
-            throw new IllegalArgumentException("Right margin size cannot be less than 0");
-        }
-        this.rightMarginSize = rightMarginSize;
-        this.changed = true;
-        return this;
-    }
-
-    @Override
-    public boolean hasChanged() {
-        return this.changed;
-    }
-
-    @Override
-    public TerminalSize getPreferredSize(List<Component> components) {
-        TerminalSize preferredSize = TerminalSize.ZERO;
-        if(components.isEmpty()) {
-            return preferredSize.withRelative(
-                    leftMarginSize + rightMarginSize,
-                    topMarginSize + bottomMarginSize);
-        }
-
-        Component[][] table = buildTable(components);
-        table = eliminateUnusedRowsAndColumns(table);
-
-        //Figure out each column first, this can be done independently of the row heights
-        int preferredWidth = 0;
-        int preferredHeight = 0;
-        for(int width: getPreferredColumnWidths(table)) {
-            preferredWidth += width;
-        }
-        for(int height: getPreferredRowHeights(table)) {
-            preferredHeight += height;
-        }
-        preferredSize = preferredSize.withRelative(preferredWidth, preferredHeight);
-        preferredSize = preferredSize.withRelativeColumns(leftMarginSize + rightMarginSize + (table[0].length - 1) * horizontalSpacing);
-        preferredSize = preferredSize.withRelativeRows(topMarginSize + bottomMarginSize + (table.length - 1) * verticalSpacing);
-        return preferredSize;
-    }
-
-    @Override
-    public void doLayout(TerminalSize area, List<Component> components) {
-        //Sanity check, if the area is way too small, just return
-        Component[][] table = buildTable(components);
-        table = eliminateUnusedRowsAndColumns(table);
-
-        if(area.equals(TerminalSize.ZERO) ||
-                table.length == 0 ||
-                area.getColumns() <= leftMarginSize + rightMarginSize + ((table[0].length - 1) * horizontalSpacing) ||
-                area.getRows() <= bottomMarginSize + topMarginSize + ((table.length - 1) * verticalSpacing)) {
-            return;
-        }
-
-        //Adjust area to the margins
-        area = area.withRelative(-leftMarginSize - rightMarginSize, -topMarginSize - bottomMarginSize);
-
-        Map<Component, TerminalSize> sizeMap = new IdentityHashMap<Component, TerminalSize>();
-        Map<Component, TerminalPosition> positionMap = new IdentityHashMap<Component, TerminalPosition>();
-
-        //Figure out each column first, this can be done independently of the row heights
-        int[] columnWidths = getPreferredColumnWidths(table);
-
-        //Take notes of which columns we can expand if the usable area is larger than what the components want
-        Set<Integer> expandableColumns = getExpandableColumns(table);
-
-        //Next, start shrinking to make sure it fits the size of the area we are trying to lay out on.
-        //Notice we subtract the horizontalSpacing to take the space between components into account
-        TerminalSize areaWithoutHorizontalSpacing = area.withRelativeColumns(-horizontalSpacing * (table[0].length - 1));
-        int totalWidth = shrinkWidthToFitArea(areaWithoutHorizontalSpacing, columnWidths);
-
-        //Finally, if there is extra space, make the expandable columns larger
-        while(areaWithoutHorizontalSpacing.getColumns() > totalWidth && !expandableColumns.isEmpty()) {
-            totalWidth = grabExtraHorizontalSpace(areaWithoutHorizontalSpacing, columnWidths, expandableColumns, totalWidth);
-        }
-
-        //Now repeat for rows
-        int[] rowHeights = getPreferredRowHeights(table);
-        Set<Integer> expandableRows = getExpandableRows(table);
-        TerminalSize areaWithoutVerticalSpacing = area.withRelativeRows(-verticalSpacing * (table.length - 1));
-        int totalHeight = shrinkHeightToFitArea(areaWithoutVerticalSpacing, rowHeights);
-        while(areaWithoutVerticalSpacing.getRows() > totalHeight && !expandableRows.isEmpty()) {
-            totalHeight = grabExtraVerticalSpace(areaWithoutVerticalSpacing, rowHeights, expandableRows, totalHeight);
-        }
-
-        //Ok, all constraints are in place, we can start placing out components. To simplify, do it horizontally first
-        //and vertically after
-        TerminalPosition tableCellTopLeft = TerminalPosition.TOP_LEFT_CORNER;
-        for(int y = 0; y < table.length; y++) {
-            tableCellTopLeft = tableCellTopLeft.withColumn(0);
-            for(int x = 0; x < table[y].length; x++) {
-                Component component = table[y][x];
-                if(component != null && !positionMap.containsKey(component)) {
-                    GridLayoutData layoutData = getLayoutData(component);
-                    TerminalSize size = component.getPreferredSize();
-                    TerminalPosition position = tableCellTopLeft;
-
-                    int availableHorizontalSpace = 0;
-                    int availableVerticalSpace = 0;
-                    for (int i = 0; i < layoutData.horizontalSpan; i++) {
-                        availableHorizontalSpace += columnWidths[x + i] + (i > 0 ? horizontalSpacing : 0);
-                    }
-                    for (int i = 0; i < layoutData.verticalSpan; i++) {
-                        availableVerticalSpace += rowHeights[y + i]  + (i > 0 ? verticalSpacing : 0);
-                    }
-
-                    //Make sure to obey the size restrictions
-                    size = size.withColumns(Math.min(size.getColumns(), availableHorizontalSpace));
-                    size = size.withRows(Math.min(size.getRows(), availableVerticalSpace));
-
-                    switch (layoutData.horizontalAlignment) {
-                        case CENTER:
-                            position = position.withRelativeColumn((availableHorizontalSpace - size.getColumns()) / 2);
-                            break;
-                        case END:
-                            position = position.withRelativeColumn(availableHorizontalSpace - size.getColumns());
-                            break;
-                        case FILL:
-                            size = size.withColumns(availableHorizontalSpace);
-                            break;
-                        default:
-                            break;
-                    }
-                    switch (layoutData.verticalAlignment) {
-                        case CENTER:
-                            position = position.withRelativeRow((availableVerticalSpace - size.getRows()) / 2);
-                            break;
-                        case END:
-                            position = position.withRelativeRow(availableVerticalSpace - size.getRows());
-                            break;
-                        case FILL:
-                            size = size.withRows(availableVerticalSpace);
-                            break;
-                        default:
-                            break;
-                    }
-
-                    sizeMap.put(component, size);
-                    positionMap.put(component, position);
-                }
-                tableCellTopLeft = tableCellTopLeft.withRelativeColumn(columnWidths[x] + horizontalSpacing);
-            }
-            tableCellTopLeft = tableCellTopLeft.withRelativeRow(rowHeights[y] + verticalSpacing);
-        }
-
-        //Apply the margins here
-        for(Component component: components) {
-            component.setPosition(positionMap.get(component).withRelative(leftMarginSize, topMarginSize));
-            component.setSize(sizeMap.get(component));
-        }
-        this.changed = false;
-    }
-
-    private int[] getPreferredColumnWidths(Component[][] table) {
-        //actualNumberOfColumns may be different from this.numberOfColumns since some columns may have been eliminated
-        int actualNumberOfColumns = table[0].length;
-        int columnWidths[] = new int[actualNumberOfColumns];
-
-        //Start by letting all span = 1 columns take what they need
-        for(Component[] row: table) {
-            for(int i = 0; i < actualNumberOfColumns; i++) {
-                Component component = row[i];
-                if(component == null) {
-                    continue;
-                }
-                GridLayoutData layoutData = getLayoutData(component);
-                if (layoutData.horizontalSpan == 1) {
-                    columnWidths[i] = Math.max(columnWidths[i], component.getPreferredSize().getColumns());
-                }
-            }
-        }
-
-        //Next, do span > 1 and enlarge if necessary
-        for(Component[] row: table) {
-            for(int i = 0; i < actualNumberOfColumns; ) {
-                Component component = row[i];
-                if(component == null) {
-                    i++;
-                    continue;
-                }
-                GridLayoutData layoutData = getLayoutData(component);
-                if(layoutData.horizontalSpan > 1) {
-                    int accumWidth = 0;
-                    for(int j = i; j < i + layoutData.horizontalSpan; j++) {
-                        accumWidth += columnWidths[j];
-                    }
-
-                    int preferredWidth = component.getPreferredSize().getColumns();
-                    if(preferredWidth > accumWidth) {
-                        int columnOffset = 0;
-                        do {
-                            columnWidths[i + columnOffset++]++;
-                            accumWidth++;
-                            if(columnOffset == layoutData.horizontalSpan) {
-                                columnOffset = 0;
-                            }
-                        }
-                        while(preferredWidth > accumWidth);
-                    }
-                }
-                i += layoutData.horizontalSpan;
-            }
-        }
-        return columnWidths;
-    }
-
-    private int[] getPreferredRowHeights(Component[][] table) {
-        int numberOfRows = table.length;
-        int rowHeights[] = new int[numberOfRows];
-
-        //Start by letting all span = 1 rows take what they need
-        int rowIndex = 0;
-        for(Component[] row: table) {
-            for(int i = 0; i < row.length; i++) {
-                Component component = row[i];
-                if(component == null) {
-                    continue;
-                }
-                GridLayoutData layoutData = getLayoutData(component);
-                if(layoutData.verticalSpan == 1) {
-                    rowHeights[rowIndex] = Math.max(rowHeights[rowIndex], component.getPreferredSize().getRows());
-                }
-            }
-            rowIndex++;
-        }
-
-        //Next, do span > 1 and enlarge if necessary
-        for(int x = 0; x < numberOfColumns; x++) {
-            for(int y = 0; y < numberOfRows && y < table.length; ) {
-                if(x >= table[y].length) {
-                    y++;
-                    continue;
-                }
-                Component component = table[y][x];
-                if(component == null) {
-                    y++;
-                    continue;
-                }
-                GridLayoutData layoutData = getLayoutData(component);
-                if(layoutData.verticalSpan > 1) {
-                    int accumulatedHeight = 0;
-                    for(int i = y; i < y + layoutData.verticalSpan; i++) {
-                        accumulatedHeight += rowHeights[i];
-                    }
-
-                    int preferredHeight = component.getPreferredSize().getRows();
-                    if(preferredHeight > accumulatedHeight) {
-                        int rowOffset = 0;
-                        do {
-                            rowHeights[y + rowOffset++]++;
-                            accumulatedHeight++;
-                            if(rowOffset == layoutData.verticalSpan) {
-                                rowOffset = 0;
-                            }
-                        }
-                        while(preferredHeight > accumulatedHeight);
-                    }
-                }
-                y += layoutData.verticalSpan;
-            }
-        }
-        return rowHeights;
-    }
-
-    private Set<Integer> getExpandableColumns(Component[][] table) {
-        Set<Integer> expandableColumns = new TreeSet<Integer>();
-        for(Component[] row: table) {
-            for (int i = 0; i < row.length; i++) {
-                if(row[i] == null) {
-                    continue;
-                }
-                GridLayoutData layoutData = getLayoutData(row[i]);
-                if(layoutData.grabExtraHorizontalSpace) {
-                    expandableColumns.add(i);
-                }
-            }
-        }
-        return expandableColumns;
-    }
-
-    private Set<Integer> getExpandableRows(Component[][] table) {
-        Set<Integer> expandableRows = new TreeSet<Integer>();
-        for(int rowIndex = 0; rowIndex < table.length; rowIndex++) {
-            Component[] row = table[rowIndex];
-            for (int columnIndex = 0; columnIndex < row.length; columnIndex++) {
-                if(row[columnIndex] == null) {
-                    continue;
-                }
-                GridLayoutData layoutData = getLayoutData(row[columnIndex]);
-                if(layoutData.grabExtraVerticalSpace) {
-                    expandableRows.add(rowIndex);
-                }
-            }
-        }
-        return expandableRows;
-    }
-
-    private int shrinkWidthToFitArea(TerminalSize area, int[] columnWidths) {
-        int totalWidth = 0;
-        for(int width: columnWidths) {
-            totalWidth += width;
-        }
-        if(totalWidth > area.getColumns()) {
-            int columnOffset = 0;
-            do {
-                if(columnWidths[columnOffset] > 0) {
-                    columnWidths[columnOffset]--;
-                    totalWidth--;
-                }
-                if(++columnOffset == numberOfColumns) {
-                    columnOffset = 0;
-                }
-            }
-            while(totalWidth > area.getColumns());
-        }
-        return totalWidth;
-    }
-
-    private int shrinkHeightToFitArea(TerminalSize area, int[] rowHeights) {
-        int totalHeight = 0;
-        for(int height: rowHeights) {
-            totalHeight += height;
-        }
-        if(totalHeight > area.getRows()) {
-            int rowOffset = 0;
-            do {
-                if(rowHeights[rowOffset] > 0) {
-                    rowHeights[rowOffset]--;
-                    totalHeight--;
-                }
-                if(++rowOffset == rowHeights.length) {
-                    rowOffset = 0;
-                }
-            }
-            while(totalHeight > area.getRows());
-        }
-        return totalHeight;
-    }
-
-    private int grabExtraHorizontalSpace(TerminalSize area, int[] columnWidths, Set<Integer> expandableColumns, int totalWidth) {
-        for(int columnIndex: expandableColumns) {
-            columnWidths[columnIndex]++;
-            totalWidth++;
-            if(area.getColumns() == totalWidth) {
-                break;
-            }
-        }
-        return totalWidth;
-    }
-
-    private int grabExtraVerticalSpace(TerminalSize area, int[] rowHeights, Set<Integer> expandableRows, int totalHeight) {
-        for(int rowIndex: expandableRows) {
-            rowHeights[rowIndex]++;
-            totalHeight++;
-            if(area.getColumns() == totalHeight) {
-                break;
-            }
-        }
-        return totalHeight;
-    }
-
-    private Component[][] buildTable(List<Component> components) {
-        List<Component[]> rows = new ArrayList<Component[]>();
-        List<int[]> hspans = new ArrayList<int[]>();
-        List<int[]> vspans = new ArrayList<int[]>();
-
-        int rowCount = 0;
-        int rowsExtent = 1;
-        Queue<Component> toBePlaced = new LinkedList<Component>(components);
-        while(!toBePlaced.isEmpty() || rowCount < rowsExtent) {
-            //Start new row
-            Component[] row = new Component[numberOfColumns];
-            int[] hspan = new int[numberOfColumns];
-            int[] vspan = new int[numberOfColumns];
-
-            for(int i = 0; i < numberOfColumns; i++) {
-                if(i > 0 && hspan[i - 1] > 1) {
-                    row[i] = row[i-1];
-                    hspan[i] = hspan[i - 1] - 1;
-                    vspan[i] = vspan[i - 1];
-                }
-                else if(rowCount > 0 && vspans.get(rowCount - 1)[i] > 1) {
-                    row[i] = rows.get(rowCount - 1)[i];
-                    hspan[i] = hspans.get(rowCount - 1)[i];
-                    vspan[i] = vspans.get(rowCount - 1)[i] - 1;
-                }
-                else if(!toBePlaced.isEmpty()) {
-                    Component component = toBePlaced.poll();
-                    GridLayoutData gridLayoutData = getLayoutData(component);
-
-                    row[i] = component;
-                    hspan[i] = gridLayoutData.horizontalSpan;
-                    vspan[i] = gridLayoutData.verticalSpan;
-                    rowsExtent = Math.max(rowsExtent, rowCount + gridLayoutData.verticalSpan);
-                }
-                else {
-                    row[i] = null;
-                    hspan[i] = 1;
-                    vspan[i] = 1;
-                }
-            }
-
-            rows.add(row);
-            hspans.add(hspan);
-            vspans.add(vspan);
-            rowCount++;
-        }
-        return rows.toArray(new Component[rows.size()][]);
-    }
-
-    private Component[][] eliminateUnusedRowsAndColumns(Component[][] table) {
-        if(table.length == 0) {
-            return table;
-        }
-        //Could make this into a Set, but I doubt there will be any real gain in performance as these are probably going
-        //to be very small.
-        List<Integer> rowsToRemove = new ArrayList<Integer>();
-        List<Integer> columnsToRemove = new ArrayList<Integer>();
-
-        final int tableRows = table.length;
-        final int tableColumns = table[0].length;
-
-        //Scan for unnecessary columns
-        columnLoop:
-        for(int column = tableColumns - 1; column > 0; column--) {
-            for(int row = 0; row < tableRows; row++) {
-                if(table[row][column] != table[row][column - 1]) {
-                   continue columnLoop;
-                }
-            }
-            columnsToRemove.add(column);
-        }
-
-        //Scan for unnecessary rows
-        rowLoop:
-        for(int row = tableRows - 1; row > 0; row--) {
-            for(int column = 0; column < tableColumns; column++) {
-                if(table[row][column] != table[row - 1][column]) {
-                    continue rowLoop;
-                }
-            }
-            rowsToRemove.add(row);
-        }
-
-        //If there's nothing to remove, just return the same
-        if(rowsToRemove.isEmpty() && columnsToRemove.isEmpty()) {
-            return table;
-        }
-
-        //Build a new table with rows & columns eliminated
-        Component[][] newTable = new Component[tableRows - rowsToRemove.size()][];
-        int insertedRowCounter = 0;
-        for(int row = 0; row < tableRows; row++) {
-            Component[] newColumn = new Component[tableColumns - columnsToRemove.size()];
-            int insertedColumnCounter = 0;
-            for(int column = 0; column < tableColumns; column++) {
-                if(columnsToRemove.contains(column)) {
-                    continue;
-                }
-                newColumn[insertedColumnCounter++] = table[row][column];
-            }
-            newTable[insertedRowCounter++] = newColumn;
-        }
-        return newTable;
-    }
-
-    private GridLayoutData getLayoutData(Component component) {
-        LayoutData layoutData = component.getLayoutData();
-        if(layoutData == null || !(layoutData instanceof GridLayoutData)) {
-            return DEFAULT;
-        }
-        else {
-            return (GridLayoutData)layoutData;
-        }
-    }
-}