]> source.dussan.org Git - vaadin-framework.git/commitdiff
Implemented row generation for Table and TreeTable #6720#
authorJonatan Kronqvist <jonatan.kronqvist@itmill.com>
Fri, 19 Aug 2011 07:16:01 +0000 (07:16 +0000)
committerJonatan Kronqvist <jonatan.kronqvist@itmill.com>
Fri, 19 Aug 2011 07:16:01 +0000 (07:16 +0000)
svn changeset:20495/svn branch:6.7

12 files changed:
WebContent/VAADIN/themes/base/table/table.css
WebContent/VAADIN/themes/chameleon/components/table/table.css
WebContent/VAADIN/themes/liferay/table/table.css
WebContent/VAADIN/themes/reindeer/table/table.css
WebContent/VAADIN/themes/runo/table/table.css
src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java
src/com/vaadin/ui/Table.java
tests/src/com/vaadin/tests/components/table/RowGenerators.java [new file with mode: 0644]
tests/src/com/vaadin/tests/components/table/TableGeneratedRows.html [new file with mode: 0644]
tests/src/com/vaadin/tests/components/table/Tables.java
tests/src/com/vaadin/tests/components/treetable/TreeTableGeneratedRows.html [new file with mode: 0644]

index c618c0758738f78b6aff534012310f2426875ee3..a5611e809f395135e087ae1da93e4ec6832f8e70 100644 (file)
        cursor: pointer;
 }
 
+.v-table-generated-row {
+          background: #efefef;
+}
+
 .v-table-body-noselection .v-table-row,
 .v-table-body-noselection .v-table-row-odd {
        cursor: default;
index ae1ada07932c93212c36711b7f629b4f69c35a99..0974f7588bdcf8d9a41d750ad6cf2c2899a84c15 100644 (file)
@@ -38,6 +38,11 @@ div.v-table-focus-slot-left {
        margin: 0;
        }
 
+.v-table-generated-row {
+    background: #c9c9c9;
+    }
+
+
 div.v-table-focus-slot-right {
        background: transparent;
        border-right: 2px solid #b3b3b3;
index 50b5ed918245b26ac2340bdb6201ea19dc72ee68..020bac882b2464862caf2d8e645c691e037ba502 100644 (file)
        background: #eef0f2;
 }
 
+.v-table-generated-row {
+    color: #336699;
+    font-weight: bold;
+    font-size: 11px;
+    padding-left: 0px;
+    padding-top: 6px;
+    background: #c0c2c5;
+}
+
 .v-table .v-selected {
        background-color: #5B677D;
        color: #FFF;
index 84c423bec9f2c89ebb1cc67cdb9f0769183883c6..461f4642b3f47ef04b26f0dcc7148ed22bea90ec 100644 (file)
 .v-table-row-odd {
        background: #eff0f1;
 }
+.v-table-generated-row {
+       background: #dcdee0;
+    text-transform: uppercase;
+    font-size: 10px;
+    font-weight: bold;
+    color: #222;
+    text-shadow: #f3f5f8 0 1px 0;
+    line-height: normal;
+}
 .v-table-cell-content:last-child {
        border-right-color: transparent;
 }
index 7a7de1962b0ede053ba17168b1f4d907d07eb9ed..2b2bcbce05a0bd09143adc656ed69615f97b2fbf 100644 (file)
@@ -67,6 +67,13 @@ tr.v-table-row-odd:hover {
 .v-table-body-noselection .v-table-row-odd:hover {
        background-color: #f6f7f7;
 }
+.v-table-generated-row {
+    color: #393a3c;
+    font-size: 15px;
+    padding: 9px 2px 9px 0;
+    text-shadow: #ffffff 0 1px 0;
+    background: #e7e9ea;
+}
 .v-table tr.v-selected {
        background: #57a7ed;
        color: #fff;
index fb51919b102243c7fcbac3f2a8151f77ae7f57ec..f0db3e55b2a58077b663791a6fa53d43f1df2a86 100644 (file)
@@ -1679,9 +1679,13 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
 
     /**
      * Run only once when component is attached and received its initial
-     * content. This function : * Syncs headers and bodys "natural widths and
-     * saves the values. * Sets proper width and height * Makes deferred request
-     * to get some cache rows
+     * content. This function:
+     * 
+     * * Syncs headers and bodys "natural widths and saves the values.
+     * 
+     * * Sets proper width and height
+     * 
+     * * Makes deferred request to get some cache rows
      */
     private void sizeInit() {
         /*
@@ -3968,22 +3972,16 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
          */
         private VScrollTableRow prepareRow(UIDL uidl) {
             final VScrollTableRow row = createRow(uidl, aligns);
-            final int cells = DOM.getChildCount(row.getElement());
-            for (int i = 0; i < cells; i++) {
-                final Element cell = DOM.getChild(row.getElement(), i);
-                int w = VScrollTable.this.getColWidth(getColKeyByIndex(i));
-                if (w < 0) {
-                    w = 0;
-                }
-                cell.getFirstChildElement().getStyle()
-                        .setPropertyPx("width", w);
-                cell.getStyle().setPropertyPx("width", w);
-            }
+            row.initCellWidths();
             return row;
         }
 
         protected VScrollTableRow createRow(UIDL uidl, char[] aligns2) {
-            return new VScrollTableRow(uidl, aligns);
+            if (uidl.hasAttribute("gen_html")) {
+                // This is a generated row.
+                return new VScrollTableGeneratedRow(uidl, aligns2);
+            }
+            return new VScrollTableRow(uidl, aligns2);
         }
 
         private void addRowBeforeFirstRendered(VScrollTableRow row) {
@@ -4187,16 +4185,19 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
          */
         public int getColWidth(int columnIndex) {
             if (tBodyMeasurementsDone) {
-                NodeList<TableRowElement> rows = tBodyElement.getRows();
-                if (rows.getLength() == 0) {
+                if (renderedRows.isEmpty()) {
                     // no rows yet rendered
                     return 0;
-                } else {
-                    com.google.gwt.dom.client.Element wrapperdiv = rows
-                            .getItem(0).getCells().getItem(columnIndex)
-                            .getFirstChildElement();
-                    return wrapperdiv.getOffsetWidth();
                 }
+                for (Widget row : renderedRows) {
+                    if (!(row instanceof VScrollTableGeneratedRow)) {
+                        TableRowElement tr = row.getElement().cast();
+                        Element wrapperdiv = tr.getCells().getItem(columnIndex)
+                                .getFirstChildElement().cast();
+                        return wrapperdiv.getOffsetWidth();
+                    }
+                }
+                return 0;
             } else {
                 return 0;
             }
@@ -4216,14 +4217,22 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
          * @param w
          */
         public void setColWidth(int colIndex, int w) {
-            NodeList<TableRowElement> rows2 = tBodyElement.getRows();
-            final int rows = rows2.getLength();
-            for (int i = 0; i < rows; i++) {
-                TableRowElement row = rows2.getItem(i);
-                TableCellElement cell = row.getCells().getItem(colIndex);
-                cell.getFirstChildElement().getStyle()
-                        .setPropertyPx("width", w);
-                cell.getStyle().setPropertyPx("width", w);
+            for (Widget row : renderedRows) {
+                TableRowElement tr = row.getElement().cast();
+                TableCellElement cell = tr.getCells().getItem(colIndex);
+                boolean spanned = false;
+                if (row instanceof VScrollTableGeneratedRow) {
+                    spanned = ((VScrollTableGeneratedRow) row).isSpanColumns();
+                }
+                if (!spanned) {
+                    cell.getFirstChildElement().getStyle()
+                            .setPropertyPx("width", w);
+                    cell.getStyle().setPropertyPx("width", w);
+                } else if (colIndex == 0) {
+                    cell.getFirstChildElement().getStyle()
+                            .clearProperty("width");
+                    cell.getStyle().clearProperty("width");
+                }
             }
         }
 
@@ -4323,8 +4332,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
             protected final int rowKey;
             private List<UIDL> pendingComponentPaints;
 
-            private String[] actionKeys = null;
-            private final TableRowElement rowElement;
+            protected String[] actionKeys = null;
+            protected final TableRowElement rowElement;
             private boolean mDown;
             private int index;
             private Event touchStart;
@@ -4345,6 +4354,120 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
                         | Event.ONCONTEXTMENU | VTooltip.TOOLTIP_EVENTS);
             }
 
+            public VScrollTableRow(UIDL uidl, char[] aligns) {
+                this(uidl.getIntAttribute("key"));
+
+                /*
+                 * Rendering the rows as hidden improves Firefox and Safari
+                 * performance drastically.
+                 */
+                getElement().getStyle().setProperty("visibility", "hidden");
+
+                String rowStyle = uidl.getStringAttribute("rowstyle");
+                if (rowStyle != null) {
+                    addStyleName(CLASSNAME + "-row-" + rowStyle);
+                }
+
+                String rowDescription = uidl.getStringAttribute("rowdescr");
+                if (rowDescription != null && !rowDescription.equals("")) {
+                    TooltipInfo info = new TooltipInfo(rowDescription);
+                    client.registerTooltip(VScrollTable.this, rowElement, info);
+                } else {
+                    // Remove possibly previously set tooltip
+                    client.registerTooltip(VScrollTable.this, rowElement, null);
+                }
+
+                tHead.getColumnAlignments();
+                int col = 0;
+                int visibleColumnIndex = -1;
+
+                // row header
+                if (showRowHeaders) {
+                    boolean sorted = tHead.getHeaderCell(col).isSorted();
+                    addCell(uidl, buildCaptionHtmlSnippet(uidl), aligns[col++],
+                            "rowheader", true, sorted);
+                    visibleColumnIndex++;
+                }
+
+                if (uidl.hasAttribute("al")) {
+                    actionKeys = uidl.getStringArrayAttribute("al");
+                }
+
+                addCellsFromUIDL(uidl, aligns, col, visibleColumnIndex);
+
+                if (uidl.hasAttribute("selected") && !isSelected()) {
+                    toggleSelection();
+                }
+            }
+
+            /**
+             * Add a dummy row, used for measurements if Table is empty.
+             */
+            public VScrollTableRow() {
+                this(0);
+                addStyleName(CLASSNAME + "-row");
+                addCell(null, "_", 'b', "", true, false);
+            }
+
+            protected void initCellWidths() {
+                final int cells = DOM.getChildCount(getElement());
+                for (int i = 0; i < cells; i++) {
+                    final Element cell = DOM.getChild(getElement(), i);
+                    int w = VScrollTable.this.getColWidth(getColKeyByIndex(i));
+                    if (w < 0) {
+                        w = 0;
+                    }
+                    cell.getFirstChildElement().getStyle()
+                            .setPropertyPx("width", w);
+                    cell.getStyle().setPropertyPx("width", w);
+                }
+            }
+
+            protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
+                    int visibleColumnIndex) {
+                final Iterator<?> cells = uidl.getChildIterator();
+                while (cells.hasNext()) {
+                    final Object cell = cells.next();
+                    visibleColumnIndex++;
+
+                    String columnId = visibleColOrder[visibleColumnIndex];
+
+                    String style = "";
+                    if (uidl.hasAttribute("style-" + columnId)) {
+                        style = uidl.getStringAttribute("style-" + columnId);
+                    }
+
+                    String description = null;
+                    if (uidl.hasAttribute("descr-" + columnId)) {
+                        description = uidl.getStringAttribute("descr-"
+                                + columnId);
+                    }
+
+                    boolean sorted = tHead.getHeaderCell(col).isSorted();
+                    if (cell instanceof String) {
+                        addCell(uidl, cell.toString(), aligns[col++], style,
+                                isRenderCellsAsHtml(), sorted, description);
+                    } else {
+                        final Paintable cellContent = client
+                                .getPaintable((UIDL) cell);
+
+                        addCell(uidl, (Widget) cellContent, aligns[col++],
+                                style, sorted);
+                        paintComponent(cellContent, (UIDL) cell);
+                    }
+                }
+            }
+
+            /**
+             * Overriding this and returning true causes all text cells to be
+             * rendered as HTML.
+             * 
+             * @return always returns false in the default implementation
+             */
+            protected boolean isRenderCellsAsHtml() {
+                return false;
+            }
+
             /**
              * Detects whether row is visible in tables viewport.
              * 
@@ -4403,7 +4526,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
                 return index;
             }
 
-            private void paintComponent(Paintable p, UIDL uidl) {
+            protected void paintComponent(Paintable p, UIDL uidl) {
                 if (isAttached()) {
                     p.updateFromUIDL(uidl, client);
                 } else {
@@ -4435,90 +4558,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
                 return String.valueOf(rowKey);
             }
 
-            public VScrollTableRow(UIDL uidl, char[] aligns) {
-                this(uidl.getIntAttribute("key"));
-
-                /*
-                 * Rendering the rows as hidden improves Firefox and Safari
-                 * performance drastically.
-                 */
-                getElement().getStyle().setProperty("visibility", "hidden");
-
-                String rowStyle = uidl.getStringAttribute("rowstyle");
-                if (rowStyle != null) {
-                    addStyleName(CLASSNAME + "-row-" + rowStyle);
-                }
-
-                String rowDescription = uidl.getStringAttribute("rowdescr");
-                if (rowDescription != null && !rowDescription.equals("")) {
-                    TooltipInfo info = new TooltipInfo(rowDescription);
-                    client.registerTooltip(VScrollTable.this, rowElement, info);
-                } else {
-                    // Remove possibly previously set tooltip
-                    client.registerTooltip(VScrollTable.this, rowElement, null);
-                }
-
-                tHead.getColumnAlignments();
-                int col = 0;
-                int visibleColumnIndex = -1;
-
-                // row header
-                if (showRowHeaders) {
-                    boolean sorted = tHead.getHeaderCell(col).isSorted();
-                    addCell(uidl, buildCaptionHtmlSnippet(uidl), aligns[col++],
-                            "rowheader", true, sorted);
-                    visibleColumnIndex++;
-                }
-
-                if (uidl.hasAttribute("al")) {
-                    actionKeys = uidl.getStringArrayAttribute("al");
-                }
-
-                final Iterator<?> cells = uidl.getChildIterator();
-                while (cells.hasNext()) {
-                    final Object cell = cells.next();
-                    visibleColumnIndex++;
-
-                    String columnId = visibleColOrder[visibleColumnIndex];
-
-                    String style = "";
-                    if (uidl.hasAttribute("style-" + columnId)) {
-                        style = uidl.getStringAttribute("style-" + columnId);
-                    }
-
-                    String description = null;
-                    if (uidl.hasAttribute("descr-" + columnId)) {
-                        description = uidl.getStringAttribute("descr-"
-                                + columnId);
-                    }
-
-                    boolean sorted = tHead.getHeaderCell(col).isSorted();
-                    if (cell instanceof String) {
-                        addCell(uidl, cell.toString(), aligns[col++], style,
-                                false, sorted, description);
-                    } else {
-                        final Paintable cellContent = client
-                                .getPaintable((UIDL) cell);
-
-                        addCell(uidl, (Widget) cellContent, aligns[col++],
-                                style, sorted);
-                        paintComponent(cellContent, (UIDL) cell);
-                    }
-                }
-                if (uidl.hasAttribute("selected") && !isSelected()) {
-                    toggleSelection();
-                }
-            }
-
-            /**
-             * Add a dummy row, used for measurements if Table is empty.
-             */
-            public VScrollTableRow() {
-                this(0);
-                addStyleName(CLASSNAME + "-row");
-                addCell(null, "_", 'b', "", true, false);
-            }
-
             public void addCell(UIDL rowUidl, String text, char align,
                     String style, boolean textIsHTML, boolean sorted) {
                 addCell(rowUidl, text, align, style, textIsHTML, sorted, null);
@@ -4528,7 +4567,14 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
                     String style, boolean textIsHTML, boolean sorted,
                     String description) {
                 // String only content is optimized by not using Label widget
-                final Element td = DOM.createTD();
+                final TableCellElement td = DOM.createTD().cast();
+                initCellWithText(text, align, style, textIsHTML, sorted,
+                        description, td);
+            }
+
+            protected void initCellWithText(String text, char align,
+                    String style, boolean textIsHTML, boolean sorted,
+                    String description, final TableCellElement td) {
                 final Element container = DOM.createDiv();
                 String className = CLASSNAME + "-cell-content";
                 if (style != null && !style.equals("")) {
@@ -4570,7 +4616,12 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
 
             public void addCell(UIDL rowUidl, Widget w, char align,
                     String style, boolean sorted) {
-                final Element td = DOM.createTD();
+                final TableCellElement td = DOM.createTD().cast();
+                initCellWithWidget(w, align, style, sorted, td);
+            }
+
+            protected void initCellWithWidget(Widget w, char align,
+                    String style, boolean sorted, final TableCellElement td) {
                 final Element container = DOM.createDiv();
                 String className = CLASSNAME + "-cell-content";
                 if (style != null && !style.equals("")) {
@@ -5223,7 +5274,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
                 };
             }
 
-            private int getColIndexOf(Widget child) {
+            protected int getColIndexOf(Widget child) {
                 com.google.gwt.dom.client.Element widgetCell = child
                         .getElement().getParentElement().getParentElement();
                 NodeList<TableCellElement> cells = rowElement.getCells();
@@ -5269,6 +5320,75 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
             }
         }
 
+        protected class VScrollTableGeneratedRow extends VScrollTableRow {
+
+            private boolean spanColumns;
+            private boolean renderAsHtml;
+
+            public VScrollTableGeneratedRow(UIDL uidl, char[] aligns) {
+                super(uidl, aligns);
+                addStyleName("v-table-generated-row");
+            }
+
+            @Override
+            protected void initCellWidths() {
+                if (!spanColumns) {
+                    super.initCellWidths();
+                }
+            }
+
+            public boolean isSpanColumns() {
+                return spanColumns;
+            }
+
+            @Override
+            protected boolean isRenderCellsAsHtml() {
+                return renderAsHtml;
+            }
+
+            @Override
+            protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
+                    int visibleColumnIndex) {
+                renderAsHtml = uidl.getBooleanAttribute("gen_html");
+                spanColumns = uidl.getBooleanAttribute("gen_span");
+
+                final Iterator<?> cells = uidl.getChildIterator();
+                if (spanColumns) {
+                    int colCount = uidl.getChildCount();
+                    if (cells.hasNext()) {
+                        final Object cell = cells.next();
+                        if (cell instanceof String) {
+                            addSpannedCell(uidl, cell.toString(), aligns[0],
+                                    "", renderAsHtml, false, null, colCount);
+                        } else {
+                            addSpannedCell(uidl, (Widget) cell, aligns[0], "",
+                                    false, colCount);
+                        }
+                    }
+                } else {
+                    super.addCellsFromUIDL(uidl, aligns, col,
+                            visibleColumnIndex);
+                }
+            }
+
+            private void addSpannedCell(UIDL rowUidl, Widget w, char align,
+                    String style, boolean sorted, int colCount) {
+                TableCellElement td = DOM.createTD().cast();
+                td.setColSpan(colCount);
+                initCellWithWidget(w, align, style, sorted, td);
+            }
+
+            private void addSpannedCell(UIDL rowUidl, String text, char align,
+                    String style, boolean textIsHTML, boolean sorted,
+                    String description, int colCount) {
+                // String only content is optimized by not using Label widget
+                final TableCellElement td = DOM.createTD().cast();
+                td.setColSpan(colCount);
+                initCellWithText(text, align, style, textIsHTML, sorted,
+                        description, td);
+            }
+        }
+
         /**
          * Ensure the component has a focus.
          * 
index a3b7008e773ee15fb1ddf657fe33f34d7941525e..b21a5dfdce74d95cd17b98c7a4ee2ee74c077e41 100644 (file)
@@ -15,6 +15,7 @@ import com.google.gwt.dom.client.SpanElement;
 import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.dom.client.Style.Visibility;
+import com.google.gwt.dom.client.TableCellElement;
 import com.google.gwt.event.dom.client.KeyCodes;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
@@ -122,6 +123,10 @@ public class VTreeTable extends VScrollTable {
 
         @Override
         protected VScrollTableRow createRow(UIDL uidl, char[] aligns2) {
+            if (uidl.hasAttribute("gen_html")) {
+                // This is a generated row.
+                return new VTreeTableGeneratedRow(uidl, aligns2);
+            }
             return new VTreeTableRow(uidl, aligns2);
         }
 
@@ -133,7 +138,7 @@ public class VTreeTable extends VScrollTable {
             private boolean open;
             private int depth;
             private boolean canHaveChildren;
-            private Widget widgetInHierarchyColumn;
+            protected Widget widgetInHierarchyColumn;
 
             public VTreeTableRow(UIDL uidl, char[] aligns2) {
                 super(uidl, aligns2);
@@ -149,7 +154,7 @@ public class VTreeTable extends VScrollTable {
                 addTreeSpacer(rowUidl);
             }
 
-            private boolean addTreeSpacer(UIDL rowUidl) {
+            protected boolean addTreeSpacer(UIDL rowUidl) {
                 if (cellShowsTreeHierarchy(getElement().getChildCount() - 1)) {
                     Element container = (Element) getElement().getLastChild()
                             .getFirstChild();
@@ -265,6 +270,79 @@ public class VTreeTable extends VScrollTable {
 
         }
 
+        protected class VTreeTableGeneratedRow extends VTreeTableRow {
+
+            private boolean spanColumns;
+            private boolean renderAsHtml;
+
+            public VTreeTableGeneratedRow(UIDL uidl, char[] aligns) {
+                super(uidl, aligns);
+                addStyleName("v-table-generated-row");
+            }
+
+            @Override
+            protected void initCellWidths() {
+                if (!spanColumns) {
+                    super.initCellWidths();
+                }
+            }
+
+            public boolean isSpanColumns() {
+                return spanColumns;
+            }
+
+            @Override
+            protected boolean isRenderCellsAsHtml() {
+                return renderAsHtml;
+            }
+
+            @Override
+            protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
+                    int visibleColumnIndex) {
+                renderAsHtml = uidl.getBooleanAttribute("gen_html");
+                spanColumns = uidl.getBooleanAttribute("gen_span");
+
+                final Iterator<?> cells = uidl.getChildIterator();
+                if (spanColumns) {
+                    int colCount = uidl.getChildCount();
+                    if (cells.hasNext()) {
+                        final Object cell = cells.next();
+                        if (cell instanceof String) {
+                            addSpannedCell(uidl, cell.toString(), aligns[0],
+                                    "", renderAsHtml, false, null, colCount);
+                        } else {
+                            addSpannedCell(uidl, (Widget) cell, aligns[0], "",
+                                    false, colCount);
+                        }
+                    }
+                } else {
+                    super.addCellsFromUIDL(uidl, aligns, col,
+                            visibleColumnIndex);
+                }
+            }
+
+            private void addSpannedCell(UIDL rowUidl, Widget w, char align,
+                    String style, boolean sorted, int colCount) {
+                TableCellElement td = DOM.createTD().cast();
+                td.setColSpan(colCount);
+                initCellWithWidget(w, align, style, sorted, td);
+                if (addTreeSpacer(rowUidl)) {
+                    widgetInHierarchyColumn = w;
+                }
+            }
+
+            private void addSpannedCell(UIDL rowUidl, String text, char align,
+                    String style, boolean textIsHTML, boolean sorted,
+                    String description, int colCount) {
+                // String only content is optimized by not using Label widget
+                final TableCellElement td = DOM.createTD().cast();
+                td.setColSpan(colCount);
+                initCellWithText(text, align, style, textIsHTML, sorted,
+                        description, td);
+                addTreeSpacer(rowUidl);
+            }
+        }
+
         private int getIdentWidth() {
             return identWidth;
         }
index 5f0fd0727744e96d85d477c1dc172e27fe83f5df..09625d88d652ef6810557920b3dc419e99db6faf 100644 (file)
@@ -109,7 +109,9 @@ public class Table extends AbstractSelect implements Action.Container,
 
     protected static final int CELL_ITEMID = 3;
 
-    protected static final int CELL_FIRSTCOL = 4;
+    protected static final int CELL_GENERATED_ROW = 4;
+
+    protected static final int CELL_FIRSTCOL = 5;
 
     /**
      * Left column alignment. <b>This is the default behaviour. </b>
@@ -400,6 +402,8 @@ public class Table extends AbstractSelect implements Action.Container,
 
     private boolean rowCacheInvalidated;
 
+    private RowGenerator rowGenerator = null;
+
     /* Table constructors */
 
     /**
@@ -1533,61 +1537,79 @@ public class Table extends AbstractSelect implements Action.Container,
                 cells[CELL_ICON][i] = getItemIcon(id);
             }
 
+            GeneratedRow generatedRow = rowGenerator != null ? rowGenerator
+                    .generateRow(this, id) : null;
+            cells[CELL_GENERATED_ROW][i] = generatedRow;
+
             for (int j = 0; j < cols; j++) {
                 if (isColumnCollapsed(colids[j])) {
                     continue;
                 }
                 Property p = null;
                 Object value = "";
-                boolean isGenerated = columnGenerators.containsKey(colids[j]);
+                boolean isGeneratedRow = generatedRow != null;
+                boolean isGeneratedColumn = columnGenerators
+                        .containsKey(colids[j]);
+                boolean isGenerated = isGeneratedRow || isGeneratedColumn;
 
                 if (!isGenerated) {
                     p = getContainerProperty(id, colids[j]);
                 }
 
-                // check in current pageBuffer already has row
-                int index = firstIndex + i;
-                if (p != null || isGenerated) {
-                    if (index < firstIndexNotInCache
-                            && index >= pageBufferFirstIndex) {
-                        // we have data already in our cache,
-                        // recycle it instead of fetching it via
-                        // getValue/getPropertyValue
+                if (isGeneratedRow) {
+                    if (generatedRow.isSpanColumns() && j > 0) {
+                        value = null;
+                    } else if (generatedRow.getText().length > j) {
+                        value = generatedRow.getText()[j];
+                    }
+                } else {
+                    // check in current pageBuffer already has row
+                    int index = firstIndex + i;
+                    if (p != null || isGenerated) {
                         int indexInOldBuffer = index - pageBufferFirstIndex;
-                        value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
-                        if (!isGenerated && iscomponent[j]
-                                || !(value instanceof Component)) {
-                            listenProperty(p, oldListenedProperties);
-                        }
-                    } else {
-                        if (isGenerated) {
-                            ColumnGenerator cg = columnGenerators
-                                    .get(colids[j]);
-                            value = cg.generateCell(this, id, colids[j]);
-                            if (value != null && !(value instanceof Component)
-                                    && !(value instanceof String)) {
-                                // Avoid errors if a generator returns something
-                                // other than a Component or a String
-                                value = value.toString();
-                            }
-                        } else if (iscomponent[j]) {
-                            value = p.getValue();
-                            listenProperty(p, oldListenedProperties);
-                        } else if (p != null) {
-                            value = getPropertyValue(id, colids[j], p);
-                            /*
-                             * If returned value is Component (via fieldfactory
-                             * or overridden getPropertyValue) we excpect it to
-                             * listen property value changes. Otherwise if
-                             * property emits value change events, table will
-                             * start to listen them and refresh content when
-                             * needed.
-                             */
-                            if (!(value instanceof Component)) {
+                        if (index < firstIndexNotInCache
+                                && index >= pageBufferFirstIndex
+                                && pageBuffer[CELL_GENERATED_ROW][indexInOldBuffer] == null) {
+                            // we have data already in our cache,
+                            // recycle it instead of fetching it via
+                            // getValue/getPropertyValue
+                            value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
+                            if (!isGeneratedColumn && iscomponent[j]
+                                    || !(value instanceof Component)) {
                                 listenProperty(p, oldListenedProperties);
                             }
                         } else {
-                            value = getPropertyValue(id, colids[j], null);
+                            if (isGeneratedColumn) {
+                                ColumnGenerator cg = columnGenerators
+                                        .get(colids[j]);
+                                value = cg.generateCell(this, id, colids[j]);
+                                if (value != null
+                                        && !(value instanceof Component)
+                                        && !(value instanceof String)) {
+                                    // Avoid errors if a generator returns
+                                    // something
+                                    // other than a Component or a String
+                                    value = value.toString();
+                                }
+                            } else if (iscomponent[j]) {
+                                value = p.getValue();
+                                listenProperty(p, oldListenedProperties);
+                            } else if (p != null) {
+                                value = getPropertyValue(id, colids[j], p);
+                                /*
+                                 * If returned value is Component (via
+                                 * fieldfactory or overridden getPropertyValue)
+                                 * we excpect it to listen property value
+                                 * changes. Otherwise if property emits value
+                                 * change events, table will start to listen
+                                 * them and refresh content when needed.
+                                 */
+                                if (!(value instanceof Component)) {
+                                    listenProperty(p, oldListenedProperties);
+                                }
+                            } else {
+                                value = getPropertyValue(id, colids[j], null);
+                            }
                         }
                     }
                 }
@@ -2919,6 +2941,7 @@ public class Table extends AbstractSelect implements Action.Container,
 
         paintRowIcon(target, cells, indexInRowbuffer);
         paintRowHeader(target, cells, indexInRowbuffer);
+        paintGeneratedRowInfo(target, cells, indexInRowbuffer);
         target.addAttribute("key",
                 Integer.parseInt(cells[CELL_KEY][indexInRowbuffer].toString()));
 
@@ -2959,6 +2982,15 @@ public class Table extends AbstractSelect implements Action.Container,
         paintRowAttributes(target, itemId);
     }
 
+    private void paintGeneratedRowInfo(PaintTarget target, Object[][] cells,
+            int indexInRowBuffer) throws PaintException {
+        GeneratedRow generatedRow = (GeneratedRow) cells[CELL_GENERATED_ROW][indexInRowBuffer];
+        if (generatedRow != null) {
+            target.addAttribute("gen_html", generatedRow.isRenderAsHtml());
+            target.addAttribute("gen_span", generatedRow.isSpanColumns());
+        }
+    }
+
     protected void paintRowHeader(PaintTarget target, Object[][] cells,
             int indexInRowbuffer) throws PaintException {
         if (rowHeadersAreEnabled()) {
@@ -4604,4 +4636,114 @@ public class Table extends AbstractSelect implements Action.Container,
         return itemDescriptionGenerator;
     }
 
+    public interface RowGenerator {
+        /**
+         * Called for every row that is painted in the Table. Returning a
+         * GeneratedRow object will cause the row to be painted based on the
+         * contents of the GeneratedRow. A generated row is by default styled
+         * similarly to a header or footer row.
+         * <p>
+         * The GeneratedRow data object contains the text that should be
+         * rendered in the row. The itemId in the container thus works only as a
+         * placeholder.
+         * <p>
+         * If GeneratedRow.setSpanColumns(true) is used, there will be one
+         * String spanning all columns (use setText("Spanning text")). Otherwise
+         * you can define one String per visible column.
+         * <p>
+         * If GeneratedRow.setRenderAsHtml(true) is used, the strings can
+         * contain HTML markup, otherwise all strings will be rendered as text
+         * (the default).
+         * <p>
+         * A "v-table-generated-row" CSS class is added to all generated rows.
+         * For custom styling of a generated row you can combine a RowGenerator
+         * with a CellStyleGenerator.
+         * <p>
+         * 
+         * @param table
+         *            The Table that is being painted
+         * @param itemId
+         *            The itemId for the row
+         * @return A GeneratedRow describing how the row should be painted or
+         *         null to paint the row with the contents from the container
+         */
+        public GeneratedRow generateRow(Table table, Object itemId);
+    }
+
+    public static class GeneratedRow {
+        private boolean renderAsHtml = false;
+        private boolean spanColumns = false;
+        private String[] text = null;
+
+        /**
+         * Creates a new generated row. If only one string is passed in, columns
+         * are automatically spanned.
+         * 
+         * @param text
+         */
+        public GeneratedRow(String... text) {
+            setRenderAsHtml(false);
+            setSpanColumns(text.length == 1);
+            setText(text);
+        }
+
+        /**
+         * Pass one String if spanColumns is used, one String for each visible
+         * column otherwise
+         */
+        public void setText(String... text) {
+            this.text = text;
+        }
+
+        protected String[] getText() {
+            return text;
+        }
+
+        protected boolean isRenderAsHtml() {
+            return renderAsHtml;
+        }
+
+        /**
+         * If set to true, all strings passed to {@link #setText(String...)}
+         * will be rendered as HTML.
+         * 
+         * @param renderAsHtml
+         */
+        public void setRenderAsHtml(boolean renderAsHtml) {
+            this.renderAsHtml = renderAsHtml;
+        }
+
+        protected boolean isSpanColumns() {
+            return spanColumns;
+        }
+
+        /**
+         * If set to true, only one string will be rendered, spanning the entire
+         * row.
+         * 
+         * @param spanColumns
+         */
+        public void setSpanColumns(boolean spanColumns) {
+            this.spanColumns = spanColumns;
+        }
+    }
+
+    /**
+     * Assigns a row generator to the table. The row generator will be able to
+     * replace rows in the table when it is rendered.
+     * 
+     * @param generator
+     *            the new row generator
+     */
+    public void setRowGenerator(RowGenerator generator) {
+        rowGenerator = generator;
+        refreshRenderedCells();
+    }
+
+    /**
+     * @return the current row generator
+     */
+    public RowGenerator getRowGenerator() {
+        return rowGenerator;
+    }
 }
diff --git a/tests/src/com/vaadin/tests/components/table/RowGenerators.java b/tests/src/com/vaadin/tests/components/table/RowGenerators.java
new file mode 100644 (file)
index 0000000..1ddb03c
--- /dev/null
@@ -0,0 +1,59 @@
+package com.vaadin.tests.components.table;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.Item;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.tests.components.TestBase;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Table.GeneratedRow;
+import com.vaadin.ui.Table.RowGenerator;
+
+public class RowGenerators extends TestBase implements RowGenerator {
+
+    @Override
+    protected void setup() {
+        Table table = new Table();
+        table.setContainerDataSource(filledContainer());
+        table.setRowGenerator(this);
+        addComponent(table);
+    }
+
+    private Container filledContainer() {
+        IndexedContainer c = new IndexedContainer();
+        c.addContainerProperty("Property 1", String.class, "");
+        c.addContainerProperty("Property 2", String.class, "");
+        c.addContainerProperty("Property 3", String.class, "");
+        c.addContainerProperty("Property 4", String.class, "");
+        for (int ix = 0; ix < 500; ix++) {
+            Item i = c.addItem(ix);
+            i.getItemProperty("Property 1").setValue("Item " + ix + ",1");
+            i.getItemProperty("Property 2").setValue("Item " + ix + ",2");
+            i.getItemProperty("Property 3").setValue("Item " + ix + ",3");
+            i.getItemProperty("Property 4").setValue("Item " + ix + ",4");
+        }
+        return c;
+    }
+
+    public GeneratedRow generateRow(Table table, Object itemId) {
+        if ((Integer) itemId % 5 == 0) {
+            if ((Integer) itemId % 10 == 0) {
+                return new GeneratedRow(
+                        "foobarbazoof very extremely long, most definitely will span.");
+            } else {
+                return new GeneratedRow("foo", "bar", "baz", "oof");
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected String getDescription() {
+        return "Row generators should replace every fifth row in the table";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 6720;
+    }
+
+}
diff --git a/tests/src/com/vaadin/tests/components/table/TableGeneratedRows.html b/tests/src/com/vaadin/tests/components/table/TableGeneratedRows.html
new file mode 100644 (file)
index 0000000..50b91c5
--- /dev/null
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>New Test</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">New Test</td></tr>
+</thead><tbody>
+<tr>
+    <td>open</td>
+    <td>/run/com.vaadin.tests.components.table.Tables?restartApplication</td>
+    <td></td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::PID_Smenu#item0</td>
+    <td>46,13</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[0]/VMenuBar[0]#item8</td>
+    <td>42,5</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[1]/VMenuBar[0]#item6</td>
+    <td>82,3</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[2]/VMenuBar[0]#item1</td>
+    <td>56,4</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::PID_Smenu#item0</td>
+    <td>29,3</td>
+</tr>
+<tr>
+    <td>screenCapture</td>
+    <td></td>
+    <td>spanned</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::PID_Smenu#item0</td>
+    <td>30,5</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::PID_Smenu#item0</td>
+    <td>27,4</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[0]/VMenuBar[0]#item8</td>
+    <td>47,13</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[1]/VMenuBar[0]#item6</td>
+    <td>80,2</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[2]/VMenuBar[0]#item2</td>
+    <td>80,8</td>
+</tr>
+<tr>
+    <td>screenCapture</td>
+    <td></td>
+    <td>nospan</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::PID_Smenu#item0</td>
+    <td>36,12</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[0]/VMenuBar[0]#item8</td>
+    <td>47,5</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[1]/VMenuBar[0]#item6</td>
+    <td>95,7</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstableTables::Root/VOverlay[2]/VMenuBar[0]#item3</td>
+    <td>67,12</td>
+</tr>
+<tr>
+    <td>screenCapture</td>
+    <td></td>
+    <td>html</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
index 1f55cf538920956b536eed12143b01d5cefec4e4..621ef60abc53b2cb093451e76883d87b16000163 100644 (file)
@@ -20,8 +20,10 @@ import com.vaadin.ui.Table.ColumnResizeEvent;
 import com.vaadin.ui.Table.ColumnResizeListener;\r
 import com.vaadin.ui.Table.FooterClickEvent;\r
 import com.vaadin.ui.Table.FooterClickListener;\r
+import com.vaadin.ui.Table.GeneratedRow;\r
 import com.vaadin.ui.Table.HeaderClickEvent;\r
 import com.vaadin.ui.Table.HeaderClickListener;\r
+import com.vaadin.ui.Table.RowGenerator;\r
 \r
 public class Tables<T extends Table> extends AbstractSelectTestCase<T>\r
         implements ItemClickListener, HeaderClickListener, FooterClickListener,\r
@@ -308,6 +310,48 @@ public class Tables<T extends Table> extends AbstractSelectTestCase<T>
         }\r
     };\r
 \r
+    private class GeneratedRowInfo {\r
+\r
+        private final int nth;\r
+        private final String[] text;\r
+        private final boolean isHtml;\r
+\r
+        public GeneratedRowInfo(int nth, boolean isHtml, String... text) {\r
+            this.nth = nth;\r
+            this.isHtml = isHtml;\r
+            this.text = text;\r
+        }\r
+\r
+        public boolean appliesTo(Object itemId) {\r
+            int ix = Integer.valueOf(itemId.toString().substring(5));\r
+            return ix % nth == 0;\r
+        }\r
+    }\r
+\r
+    private Command<T, GeneratedRowInfo> rowGeneratorCommand = new Command<T, GeneratedRowInfo>() {\r
+\r
+        public void execute(T c, final GeneratedRowInfo generatedRowInfo,\r
+                Object data) {\r
+            if (generatedRowInfo == null) {\r
+                c.setRowGenerator(null);\r
+            } else {\r
+                c.setRowGenerator(new RowGenerator() {\r
+\r
+                    public GeneratedRow generateRow(Table table, Object itemId) {\r
+                        if (generatedRowInfo.appliesTo(itemId)) {\r
+                            GeneratedRow generatedRow = new GeneratedRow(\r
+                                    generatedRowInfo.text);\r
+                            generatedRow\r
+                                    .setRenderAsHtml(generatedRowInfo.isHtml);\r
+                            return generatedRow;\r
+                        }\r
+                        return null;\r
+                    }\r
+                });\r
+            }\r
+        }\r
+    };\r
+\r
     private Command<T, Boolean> setSortEnabledCommand = new Command<T, Boolean>() {\r
 \r
         public void execute(T c, Boolean value, Object data) {\r
@@ -348,6 +392,7 @@ public class Tables<T extends Table> extends AbstractSelectTestCase<T>
         createColumnHeaderMode(CATEGORY_FEATURES);\r
         createAddGeneratedColumnAction(CATEGORY_FEATURES);\r
         createCellStyleAction(CATEGORY_FEATURES);\r
+        createGeneratedRowAction(CATEGORY_FEATURES);\r
 \r
         createBooleanAction("Sort enabled", CATEGORY_FEATURES, true,\r
                 setSortEnabledCommand);\r
@@ -401,6 +446,26 @@ public class Tables<T extends Table> extends AbstractSelectTestCase<T>
                 "None", cellStyleCommand, true);\r
     }\r
 \r
+    private void createGeneratedRowAction(String categoryFeatures) {\r
+        LinkedHashMap<String, GeneratedRowInfo> options = new LinkedHashMap<String, GeneratedRowInfo>();\r
+        options.put("None", null);\r
+        options.put("Every fifth row, spanned", new GeneratedRowInfo(5, false,\r
+                "foobarbaz this is a long one that should span."));\r
+        int props = getComponent().getContainerPropertyIds().size();\r
+        String[] text = new String[props];\r
+        for (int ix = 0; ix < props; ix++) {\r
+            text[ix] = "foo" + ix;\r
+        }\r
+        options.put("Every tenth row, no spanning", new GeneratedRowInfo(10,\r
+                false, text));\r
+        options.put(\r
+                "Every eight row, spanned, html formatted",\r
+                new GeneratedRowInfo(8, true,\r
+                        "<b>foo</b> <i>bar</i> <span style='color:red;text-size:0.5em;'>baz</span>"));\r
+        createSelectAction("Row generator", categoryFeatures, options, "None",\r
+                rowGeneratorCommand, true);\r
+    }\r
+\r
     private void createColumnHeaderMode(String category) {\r
         LinkedHashMap<String, Integer> columnHeaderModeOptions = new LinkedHashMap<String, Integer>();\r
         columnHeaderModeOptions.put("Hidden", Table.COLUMN_HEADER_MODE_HIDDEN);\r
diff --git a/tests/src/com/vaadin/tests/components/treetable/TreeTableGeneratedRows.html b/tests/src/com/vaadin/tests/components/treetable/TreeTableGeneratedRows.html
new file mode 100644 (file)
index 0000000..6419a8c
--- /dev/null
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="" />
+<title>New Test</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">New Test</td></tr>
+</thead><tbody>
+<tr>
+    <td>open</td>
+    <td>/run/com.vaadin.tests.components.treetable.TreeTableTest?restartApplication=</td>
+    <td></td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_Smenu#item0</td>
+    <td>30,7</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[0]/VMenuBar[0]#item5</td>
+    <td>36,4</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[1]/VMenuBar[0]#item1</td>
+    <td>75,6</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[2]/VMenuBar[0]#item13</td>
+    <td>34,10</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_Smenu#item0</td>
+    <td>39,9</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[0]/VMenuBar[0]#item5</td>
+    <td>43,2</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[1]/VMenuBar[0]#item3</td>
+    <td>45,4</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[2]/VMenuBar[0]#item10</td>
+    <td>41,8</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_Smenu#item0</td>
+    <td>10,9</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[0]/VMenuBar[0]#item2</td>
+    <td>35,7</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[1]/VMenuBar[0]#item2</td>
+    <td>32,8</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[2]/VMenuBar[0]#item3</td>
+    <td>54,7</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_Smenu#item0</td>
+    <td>20,4</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[0]/VMenuBar[0]#item8</td>
+    <td>42,5</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[1]/VMenuBar[0]#item6</td>
+    <td>92,5</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[2]/VMenuBar[0]#item1</td>
+    <td>65,8</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_StestComponent/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[0]/domChild[0]</td>
+    <td>12,8</td>
+</tr>
+<tr>
+    <td>screenCapture</td>
+    <td></td>
+    <td>spanned</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_StestComponent/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[0]/domChild[0]</td>
+    <td>12,4</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_Smenu#item0</td>
+    <td>22,2</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[0]/VMenuBar[0]#item8</td>
+    <td>44,0</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[1]/VMenuBar[0]#item6</td>
+    <td>87,3</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[2]/VMenuBar[0]#item2</td>
+    <td>68,12</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_StestComponent/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[9]/domChild[0]/domChild[0]/domChild[0]</td>
+    <td>11,6</td>
+</tr>
+<tr>
+    <td>screenCapture</td>
+    <td></td>
+    <td>nospan</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_StestComponent/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[9]/domChild[0]/domChild[0]/domChild[0]</td>
+    <td>11,6</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_Smenu#item0</td>
+    <td>7,12</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[0]/VMenuBar[0]#item8</td>
+    <td>24,1</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[1]/VMenuBar[0]#item6</td>
+    <td>93,8</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::Root/VOverlay[2]/VMenuBar[0]#item3</td>
+    <td>61,6</td>
+</tr>
+<tr>
+    <td>mouseClick</td>
+    <td>vaadin=runcomvaadintestscomponentstreetableTreeTableTest::PID_StestComponent/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[7]/domChild[0]/domChild[0]/domChild[0]</td>
+    <td>12,7</td>
+</tr>
+<tr>
+    <td>screenCapture</td>
+    <td></td>
+    <td>html</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>