]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fixes insert/remove columns and testcase (#12645)
authorHenrik Paul <henrik@vaadin.com>
Tue, 12 Nov 2013 08:37:27 +0000 (10:37 +0200)
committerHenrik Paul <henrik@vaadin.com>
Tue, 12 Nov 2013 11:42:53 +0000 (13:42 +0200)
This includes also other bugs that were found while creating a more suitable testcase for this

Change-Id: I841e3643550b02d1ba16d2eee74deab9be15cc26

client/src/com/vaadin/client/ui/grid/Escalator.java
uitest/src/com/vaadin/tests/components/grid/GridTest.html
uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java
uitest/src/com/vaadin/tests/widgetset/client/grid/VTestGrid.java

index 051203ecb6be4d293948383f3705a8870ab2fe9c..d5311af3da3fddfe3ba7fd215998503f7650929c 100644 (file)
@@ -27,6 +27,7 @@ import java.util.logging.Logger;
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.NodeList;
 import com.google.gwt.dom.client.Style;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.event.logical.shared.AttachEvent;
@@ -215,6 +216,10 @@ public class Escalator extends Widget {
      * [[frozencol]]: This needs to be re-inspected once frozen columns are
      * being implemented.
      */
+    /*
+     * [[widgets]]: This needs to be re-inspected once GWT/Vaadin widgets are
+     * being supported.
+     */
 
     private static final int ROW_HEIGHT_PX = 20;
     private static final int COLUMN_WIDTH_PX = 100;
@@ -689,23 +694,26 @@ public class Escalator extends Widget {
                 return addedRows;
             }
 
-            Node referenceNode;
+            Node referenceRow;
             if (root.getChildCount() != 0 && visualIndex != 0) {
                 // get the row node we're inserting stuff after
-                referenceNode = root.getChild(visualIndex - 1);
+                referenceRow = root.getChild(visualIndex - 1);
             } else {
                 // index is 0, so just prepend.
-                referenceNode = null;
+                referenceRow = null;
             }
 
             for (int row = visualIndex; row < visualIndex + numberOfRows; row++) {
                 final Element tr = DOM.createTR();
                 addedRows.add(tr);
+                tr.addClassName(CLASS_NAME + "-row");
+                referenceRow = insertAfterReferenceAndUpdateIt(root, tr,
+                        referenceRow);
 
                 for (int col = 0; col < columnConfiguration.getColumnCount(); col++) {
                     final Element cellElem = createCellElement();
-                    paintCell(cellElem, row, col);
                     tr.appendChild(cellElem);
+                    paintCell(cellElem, row, col);
                 }
 
                 /*
@@ -715,27 +723,6 @@ public class Escalator extends Widget {
                  * updated to reduce the number of reflows.
                  */
                 recalculateRowWidth(tr);
-                tr.addClassName(CLASS_NAME + "-row");
-
-                if (referenceNode != null) {
-                    root.insertAfter(tr, referenceNode);
-                } else {
-                    /*
-                     * referencenode being null means we have index 0, i.e. make
-                     * it the first row
-                     */
-                    /*
-                     * TODO [[optimize]]: Is insertFirst or append faster for an
-                     * empty root?
-                     */
-                    root.insertFirst(tr);
-                }
-
-                /*
-                 * to get the rows to appear one after another in a logical
-                 * order, update the reference
-                 */
-                referenceNode = tr;
             }
 
             recalculateSectionHeight();
@@ -743,6 +730,24 @@ public class Escalator extends Widget {
             return addedRows;
         }
 
+        private Node insertAfterReferenceAndUpdateIt(final Element parent,
+                final Element elem, final Node referenceNode) {
+            if (referenceNode != null) {
+                parent.insertAfter(elem, referenceNode);
+            } else {
+                /*
+                 * referencenode being null means we have offset 0, i.e. make it
+                 * the first row
+                 */
+                /*
+                 * TODO [[optimize]]: Is insertFirst or append faster for an
+                 * empty root?
+                 */
+                parent.insertFirst(elem);
+            }
+            return elem;
+        }
+
         protected void recalculateSectionHeight() {
             final double newHeight = root.getChildCount() * ROW_HEIGHT_PX;
             if (newHeight != height) {
@@ -844,6 +849,83 @@ public class Escalator extends Widget {
          */
         abstract protected Element getTrByVisualIndex(int index)
                 throws IndexOutOfBoundsException;
+
+        abstract protected int getTopVisualRowLogicalIndex();
+
+        protected void paintRemoveColumns(final int offset,
+                final int numberOfColumns) {
+            final NodeList<Node> childNodes = root.getChildNodes();
+            for (int visualRowIndex = 0; visualRowIndex < childNodes
+                    .getLength(); visualRowIndex++) {
+                final Node tr = childNodes.getItem(visualRowIndex);
+
+                for (int column = 0; column < numberOfColumns; column++) {
+                    // TODO [[widgets]]
+                    tr.getChild(offset).removeFromParent();
+                }
+                recalculateRowWidth((Element) tr);
+            }
+
+            final int firstRemovedColumnLeft = offset * COLUMN_WIDTH_PX;
+            final boolean columnsWereRemovedFromLeftOfTheViewport = scroller.lastScrollLeft > firstRemovedColumnLeft;
+
+            if (columnsWereRemovedFromLeftOfTheViewport) {
+                final int removedColumnsPxAmount = numberOfColumns
+                        * COLUMN_WIDTH_PX;
+                final int leftByDiff = (int) (scroller.lastScrollLeft - removedColumnsPxAmount);
+                final int newScrollLeft = Math.max(firstRemovedColumnLeft,
+                        leftByDiff);
+                scrollerElem.setScrollLeft(newScrollLeft);
+            }
+
+            // this needs to be after the scroll position adjustment above.
+            scroller.recalculateScrollbarsForVirtualViewport();
+
+        }
+
+        protected void paintInsertColumns(final int offset,
+                final int numberOfColumns) {
+            final NodeList<Node> childNodes = root.getChildNodes();
+            final int topVisualRowLogicalIndex = getTopVisualRowLogicalIndex();
+
+            for (int row = 0; row < childNodes.getLength(); row++) {
+                final Element tr = getTrByVisualIndex(row);
+
+                Node referenceCell;
+                if (offset != 0) {
+                    referenceCell = tr.getChild(offset - 1);
+                } else {
+                    referenceCell = null;
+                }
+
+                for (int col = offset; col < offset + numberOfColumns; col++) {
+                    final Element cellElem = createCellElement();
+                    referenceCell = insertAfterReferenceAndUpdateIt(tr,
+                            cellElem, referenceCell);
+                    paintCell(cellElem, topVisualRowLogicalIndex + row, col);
+                }
+
+                /*
+                 * TODO [[optimize]] [[colwidth]]: When this method is updated
+                 * to measure things instead of using hardcoded values, it would
+                 * be better to do everything at once after all rows have been
+                 * updated to reduce the number of reflows.
+                 */
+                recalculateRowWidth(tr);
+            }
+
+            // this needs to be before the scrollbar adjustment.
+            scroller.recalculateScrollbarsForVirtualViewport();
+
+            final boolean columnsWereAddedToTheLeftOfViewport = scroller.lastScrollLeft > offset
+                    * COLUMN_WIDTH_PX;
+
+            if (columnsWereAddedToTheLeftOfViewport) {
+                scrollerElem
+                        .setScrollLeft((int) (scroller.lastScrollLeft + numberOfColumns
+                                * COLUMN_WIDTH_PX));
+            }
+        }
     }
 
     private abstract class AbstractStaticRowContainer extends
@@ -856,6 +938,7 @@ public class Escalator extends Widget {
         protected void paintRemoveRows(final int index, final int numberOfRows) {
             for (int i = index; i < index + numberOfRows; i++) {
                 final Element tr = (Element) root.getChild(i);
+                // TODO [[widgets]]
                 tr.removeFromParent();
             }
             recalculateSectionHeight();
@@ -871,6 +954,11 @@ public class Escalator extends Widget {
                         + index);
             }
         }
+
+        @Override
+        protected int getTopVisualRowLogicalIndex() {
+            return 0;
+        }
     }
 
     private class HeaderRowContainer extends AbstractStaticRowContainer {
@@ -1374,6 +1462,7 @@ public class Escalator extends Widget {
                     for (int i = 0; i < escalatorRowsToRemove; i++) {
                         final Element tr = visualRowOrder
                                 .remove(removedVisualInside.getStart());
+                        // TODO [[widgets]]
                         tr.removeFromParent();
                         rowTopPosMap.remove(tr);
                     }
@@ -1733,6 +1822,15 @@ public class Escalator extends Widget {
             tBodyScrollTop = scrollTop;
             position.set(bodyElem, -tBodyScrollLeft, -tBodyScrollTop);
         }
+
+        @Override
+        protected int getTopVisualRowLogicalIndex() {
+            if (!visualRowOrder.isEmpty()) {
+                return getLogicalRowIndex(visualRowOrder.getFirst());
+            } else {
+                return 0;
+            }
+        }
     }
 
     private class ColumnConfigurationImpl implements ColumnConfiguration {
@@ -1751,17 +1849,11 @@ public class Escalator extends Widget {
         public void removeColumns(final int index, final int numberOfColumns) {
             assertArgumentsAreValidAndWithinRange(index, numberOfColumns);
 
-            columns--;
+            columns -= numberOfColumns;
 
-            // FIXME [[escalator]]: broken on escalator
             if (hasSomethingInDom()) {
                 for (final AbstractRowContainer rowContainer : rowContainers) {
-                    for (int row = 0; row < rowContainer.getRowCount(); row++) {
-                        final Node tr = rowContainer.root.getChild(row);
-                        for (int col = 0; col < numberOfColumns; col++) {
-                            tr.getChild(index).removeFromParent();
-                        }
-                    }
+                    rowContainer.paintRemoveColumns(index, numberOfColumns);
                 }
             }
         }
@@ -1807,58 +1899,9 @@ public class Escalator extends Widget {
             }
 
             columns += numberOfColumns;
-            if (!hasColumnAndRowData()) {
-                return;
-            }
-
-            for (final AbstractRowContainer rowContainer : rowContainers) {
-                // FIXME: broken on escalator
-                final Element element = rowContainer.root;
-
-                for (int row = 0; row < element.getChildCount(); row++) {
-                    final Element tr = (Element) element.getChild(row);
-
-                    Node referenceElement;
-                    if (index != 0) {
-                        referenceElement = tr.getChild(index - 1);
-                    } else {
-                        referenceElement = null;
-                    }
-
-                    for (int col = index; col < index + numberOfColumns; col++) {
-                        final Element cellElem = rowContainer
-                                .createCellElement();
-                        rowContainer.paintCell(cellElem, row, col);
-
-                        if (referenceElement != null) {
-                            tr.insertAfter(cellElem, referenceElement);
-                        } else {
-                            /*
-                             * referenceElement being null means we have index
-                             * 0, make it the first cell.
-                             */
-                            /*
-                             * TODO [[optimize]]: Is insertFirst or append
-                             * faster for an empty tr?
-                             */
-                            tr.insertFirst(cellElem);
-                        }
-
-                        /*
-                         * update reference to insert cells in logical order,
-                         * the latter after the former
-                         */
-                        referenceElement = cellElem;
-                    }
-
-                    /*
-                     * TODO [[optimize]] [[colwidth]]: When this method is
-                     * updated to measure things instead of using hardcoded
-                     * values, it would be better to do everything at once after
-                     * all rows have been updated to reduce the number of
-                     * reflows.
-                     */
-                    recalculateRowWidth(tr);
+            if (hasColumnAndRowData()) {
+                for (final AbstractRowContainer rowContainer : rowContainers) {
+                    rowContainer.paintInsertColumns(index, numberOfColumns);
                 }
             }
         }
index 76ebbf1807987da81492387e239acaeaf9a7c245..33dda17471ed5fdf6f8ac45c95c18916dd63c1cf 100644 (file)
 <tr>
        <td>verifyText</td>
        <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td>
-       <td>Logical row 0/0</td>
+       <td>Row 0: 0,0 (0)</td>
 </tr>
 <tr>
        <td>verifyText</td>
-       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[9]/domChild[0]</td>
-       <td>Logical row 9/9</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[17]/domChild[9]</td>
+       <td>Cell: 9,17 (179)</td>
 </tr>
 <tr>
        <td>verifyTextNotPresent</td>
-       <td>Logical row 0/10</td>
+       <td>Cell: 0,100</td>
        <td></td>
 </tr>
 <tr>
        <td>verifyTextNotPresent</td>
-       <td>Logical row 11/11</td>
+       <td>Cell: 0,101</td>
        <td></td>
 </tr>
 <tr>
@@ -53,8 +53,8 @@
 </tr>
 <tr>
        <td>verifyText</td>
-       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td>
-       <td>Logical row 0/10</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[18]/domChild[0]</td>
+       <td>Row 0: 0,100 (190)</td>
 </tr>
 <tr>
        <td>type</td>
@@ -68,8 +68,8 @@
 </tr>
 <tr>
        <td>verifyText</td>
-       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[11]/domChild[0]</td>
-       <td>Logical row 11/11</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[17]/domChild[0]</td>
+       <td>Row 11: 0,101 (200)</td>
 </tr>
 <tr>
        <td>type</td>
 </tr>
 <tr>
        <td>verifyText</td>
-       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]</td>
-       <td>Logical row 0/12</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[16]/domChild[0]</td>
+       <td>Row 0: 0,102 (210)</td>
 </tr>
 <tr>
        <td>verifyText</td>
-       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[10]/domChild[0]</td>
-       <td>Logical row 17/29</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[0]</td>
+       <td>Row 16: 0,118 (370)</td>
 </tr>
 <tr>
        <td>scroll</td>
 </tr>
 <tr>
        <td>verifyTextPresent</td>
-       <td>Logical row 56/68</td>
+       <td>Row 56: 0,158</td>
        <td></td>
 </tr>
 <tr>
        <td>verifyTextPresent</td>
-       <td>Logical row 72/84</td>
+       <td>Row 72: 0,174</td>
        <td></td>
 </tr>
 <tr>
        <td>scroll</td>
        <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[0]</td>
-       <td>1875</td>
+       <td>3690</td>
 </tr>
 <tr>
        <td>verifyTextPresent</td>
-       <td>Logical row 111/</td>
+       <td>Row 201: 0,99</td>
        <td></td>
 </tr>
 <tr>
        <td>type</td>
        <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VHorizontalLayout[0]/Slot[0]/VTextField[0]</td>
-       <td>111</td>
+       <td>201</td>
 </tr>
 <tr>
        <td>type</td>
 </tr>
 <tr>
        <td>verifyText</td>
-       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[17]/domChild[0]</td>
-       <td>Logical row 110/144</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[0]</td>
+       <td>Row 200: 0,98 (960)</td>
 </tr>
 <tr>
        <td>verifyTextNotPresent</td>
-       <td>Logical row 111/</td>
+       <td>Row 201:</td>
        <td></td>
 </tr>
+<tr>
+       <td>type</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VHorizontalLayout[0]/Slot[0]/VTextField[0]</td>
+       <td>0</td>
+</tr>
+<tr>
+       <td>type</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VHorizontalLayout[0]/Slot[1]/VTextField[0]</td>
+       <td>2</td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VHorizontalLayout[0]/Slot[2]/VButton[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>verifyText</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[16]/domChild[0]</td>
+       <td>Row 184: 10,82 (974)</td>
+</tr>
+<tr>
+       <td>verifyText</td>
+       <td>vaadin=runcomvaadintestscomponentsgridGridTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTestGrid[0]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[0]</td>
+       <td>Row 200: 10,98 (1006)</td>
+</tr>
 </tbody></table>
 </body>
 </html>
index 80eeeb6849844300eb01307b6ee5d891fc4ba701..6a024b9d96168b930ba74f9d7420b75e30d1ce12 100644 (file)
@@ -32,24 +32,22 @@ public class TestGridConnector extends AbstractComponentConnector {
         registerRpc(TestGridClientRpc.class, new TestGridClientRpc() {
             @Override
             public void insertRows(int offset, int amount) {
-                getWidget().getBody().insertRows(offset, amount);
+                getWidget().insertRows(offset, amount);
             }
 
             @Override
             public void removeRows(int offset, int amount) {
-                getWidget().getBody().removeRows(offset, amount);
+                getWidget().removeRows(offset, amount);
             }
 
             @Override
             public void removeColumns(int offset, int amount) {
-                getWidget().getColumnConfiguration().removeColumns(offset,
-                        amount);
+                getWidget().removeColumns(offset, amount);
             }
 
             @Override
             public void insertColumns(int offset, int amount) {
-                getWidget().getColumnConfiguration().insertColumns(offset,
-                        amount);
+                getWidget().insertColumns(offset, amount);
             }
 
             @Override
index 04fe5561be519b7fa1dcabe4a83dde95ea3eb5f4..154b1d2bc9d821b017aa8fa7b965b7f9960f6e56 100644 (file)
@@ -1,5 +1,8 @@
 package com.vaadin.tests.widgetset.client.grid;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import com.google.gwt.user.client.ui.Composite;
 import com.vaadin.client.ui.grid.Cell;
 import com.vaadin.client.ui.grid.CellRenderer;
@@ -9,77 +12,130 @@ import com.vaadin.client.ui.grid.RowContainer;
 import com.vaadin.client.ui.grid.ScrollDestination;
 
 public class VTestGrid extends Composite {
-    public static class HeaderRenderer implements CellRenderer {
-        private int i = 0;
 
-        @Override
-        public void renderCell(final Cell cell) {
-            cell.getElement().setInnerText("Header " + (i++));
-        }
-    }
+    private static class Data {
+        private int columnCounter = 0;
+        private int rowCounter = 0;
+        private final List<Integer> columns = new ArrayList<Integer>();
+        private final List<Integer> rows = new ArrayList<Integer>();
 
-    public static class BodyRenderer implements CellRenderer {
-        private int i = 0;
-        private int ri = 0;
-
-        @Override
-        public void renderCell(final Cell cell) {
-            if (cell.getColumn() != 0) {
-                cell.getElement().setInnerText("Cell #" + (i++));
-            } else {
-                cell.getElement().setInnerText(
-                        "Logical row " + cell.getRow() + "/" + (ri++));
+        public void insertRows(int offset, int amount) {
+            List<Integer> newRows = new ArrayList<Integer>();
+            for (int i = 0; i < amount; i++) {
+                newRows.add(rowCounter++);
             }
+            rows.addAll(offset, newRows);
+        }
 
-            double c = i * .1;
-            int r = (int) ((Math.cos(c) + 1) * 128);
-            int g = (int) ((Math.cos(c / Math.PI) + 1) * 128);
-            int b = (int) ((Math.cos(c / (Math.PI * 2)) + 1) * 128);
-            cell.getElement().getStyle()
-                    .setBackgroundColor("rgb(" + r + "," + g + "," + b + ")");
-            if ((r * .8 + g * 1.3 + b * .9) / 3 < 127) {
-                cell.getElement().getStyle().setColor("white");
-            } else {
-                cell.getElement().getStyle().clearColor();
+        public void insertColumns(int offset, int amount) {
+            List<Integer> newColumns = new ArrayList<Integer>();
+            for (int i = 0; i < amount; i++) {
+                newColumns.add(columnCounter++);
             }
+            columns.addAll(offset, newColumns);
+        }
+
+        public CellRenderer createHeaderRenderer() {
+            return new CellRenderer() {
+                @Override
+                public void renderCell(Cell cell) {
+                    int columnName = columns.get(cell.getColumn());
+                    cell.getElement().setInnerText("Header " + columnName);
+                }
+            };
+        }
+
+        public CellRenderer createFooterRenderer() {
+            return new CellRenderer() {
+                @Override
+                public void renderCell(Cell cell) {
+                    int columnName = columns.get(cell.getColumn());
+                    cell.getElement().setInnerText("Footer " + columnName);
+                }
+            };
+        }
+
+        public CellRenderer createBodyRenderer() {
+            return new CellRenderer() {
+                int i = 0;
+
+                @Override
+                public void renderCell(Cell cell) {
+                    int columnName = columns.get(cell.getColumn());
+                    int rowName = rows.get(cell.getRow());
+                    String cellInfo = columnName + "," + rowName + " (" + i
+                            + ")";
+
+                    if (cell.getColumn() > 0) {
+                        cell.getElement().setInnerText("Cell: " + cellInfo);
+                    } else {
+                        cell.getElement().setInnerText(
+                                "Row " + cell.getRow() + ": " + cellInfo);
+                    }
+
+                    double c = i * .1;
+                    int r = (int) ((Math.cos(c) + 1) * 128);
+                    int g = (int) ((Math.cos(c / Math.PI) + 1) * 128);
+                    int b = (int) ((Math.cos(c / (Math.PI * 2)) + 1) * 128);
+                    cell.getElement()
+                            .getStyle()
+                            .setBackgroundColor(
+                                    "rgb(" + r + "," + g + "," + b + ")");
+                    if ((r * .8 + g * 1.3 + b * .9) / 3 < 127) {
+                        cell.getElement().getStyle().setColor("white");
+                    } else {
+                        cell.getElement().getStyle().clearColor();
+                    }
+
+                    i++;
+                }
+            };
         }
-    }
 
-    public static class FooterRenderer implements CellRenderer {
-        private int i = 0;
+        public void removeRows(int offset, int amount) {
+            for (int i = 0; i < amount; i++) {
+                rows.remove(offset);
+            }
+        }
 
-        @Override
-        public void renderCell(final Cell cell) {
-            cell.getElement().setInnerText("Footer " + (i++));
+        public void removeColumns(int offset, int amount) {
+            for (int i = 0; i < amount; i++) {
+                columns.remove(offset);
+            }
         }
     }
 
     private Escalator escalator = new Escalator();
+    private Data data = new Data();
 
     public VTestGrid() {
         initWidget(escalator);
-        final ColumnConfiguration cConf = escalator.getColumnConfiguration();
-        cConf.insertColumns(cConf.getColumnCount(), 10);
+        RowContainer header = escalator.getHeader();
+        header.setCellRenderer(data.createHeaderRenderer());
+        header.insertRows(0, 1);
 
-        final RowContainer h = escalator.getHeader();
-        h.setCellRenderer(new HeaderRenderer());
-        h.insertRows(0, 1);
+        RowContainer footer = escalator.getFooter();
+        footer.setCellRenderer(data.createFooterRenderer());
+        footer.insertRows(0, 1);
 
-        final RowContainer b = escalator.getBody();
-        b.setCellRenderer(new BodyRenderer());
-        b.insertRows(0, 10);
+        escalator.getBody().setCellRenderer(data.createBodyRenderer());
 
-        final RowContainer f = escalator.getFooter();
-        f.setCellRenderer(new FooterRenderer());
-        f.insertRows(0, 1);
+        insertRows(0, 100);
+        insertColumns(0, 10);
 
         setWidth(TestGridState.DEFAULT_WIDTH);
         setHeight(TestGridState.DEFAULT_HEIGHT);
 
     }
 
-    public RowContainer getBody() {
-        return escalator.getBody();
+    public void insertRows(int offset, int number) {
+        data.insertRows(offset, number);
+        escalator.getBody().insertRows(offset, number);
+    }
+
+    public void insertColumns(int offset, int number) {
+        data.insertColumns(offset, number);
+        escalator.getColumnConfiguration().insertColumns(offset, number);
     }
 
     public ColumnConfiguration getColumnConfiguration() {
@@ -103,4 +159,14 @@ public class VTestGrid extends Composite {
             escalator.scrollToColumn(index, destination);
         }
     }
+
+    public void removeRows(int offset, int amount) {
+        data.removeRows(offset, amount);
+        escalator.getBody().removeRows(offset, amount);
+    }
+
+    public void removeColumns(int offset, int amount) {
+        data.removeColumns(offset, amount);
+        escalator.getColumnConfiguration().removeColumns(offset, amount);
+    }
 }