diff options
author | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-04-15 10:24:44 +0300 |
---|---|---|
committer | Teemu Suo-Anttila <teemusa@vaadin.com> | 2015-04-15 10:24:44 +0300 |
commit | 2c4e533c9f2aa68211329ea6ce4de0b577407863 (patch) | |
tree | 1d9c1cc8bb72678b0edb991947c6fc94112baf0f /server/src/com | |
parent | 93235f05c9dd4739cdccf87a4858a61904dbf4b5 (diff) | |
parent | 7cb23bc63f794a7549dd79c37da2f8bb8e88e20d (diff) | |
download | vaadin-framework-2c4e533c9f2aa68211329ea6ce4de0b577407863.tar.gz vaadin-framework-2c4e533c9f2aa68211329ea6ce4de0b577407863.zip |
Merge remote-tracking branch 'origin/master' into grid-7.5
Change-Id: I03fdd2014fd0393341db0f650c065f6d27905b73
Diffstat (limited to 'server/src/com')
-rw-r--r-- | server/src/com/vaadin/data/fieldgroup/FieldGroup.java | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/AbsoluteLayout.java | 3 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/AbstractOrderedLayout.java | 57 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/AbstractSelect.java | 141 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/ColorPicker.java | 12 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/Grid.java | 503 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/GridLayout.java | 325 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/MenuBar.java | 3 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/OptionGroup.java | 27 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/Tree.java | 119 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java | 80 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/declarative/DesignContext.java | 13 |
12 files changed, 1179 insertions, 106 deletions
diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java index 8f1bf8b40a..d370c3906b 100644 --- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java @@ -702,7 +702,7 @@ public class FieldGroup implements Serializable { /** * Called after changes are committed to the fields and the item is - * updated.. + * updated. * <p> * Throw a {@link CommitException} to abort the commit. * diff --git a/server/src/com/vaadin/ui/AbsoluteLayout.java b/server/src/com/vaadin/ui/AbsoluteLayout.java index 63bbe70157..303e8efd6b 100644 --- a/server/src/com/vaadin/ui/AbsoluteLayout.java +++ b/server/src/com/vaadin/ui/AbsoluteLayout.java @@ -724,7 +724,7 @@ public class AbsoluteLayout extends AbstractLayout implements for (Component child : this) { Element childElement = designContext.createElement(child); design.appendChild(childElement); - child.writeDesign(childElement, designContext); + // handle position ComponentPosition position = getPosition(child); writePositionAttribute(childElement, ATTR_TOP, position @@ -735,6 +735,7 @@ public class AbsoluteLayout extends AbstractLayout implements .getBottomUnits().getSymbol(), position.getBottomValue()); writePositionAttribute(childElement, ATTR_LEFT, position .getLeftUnits().getSymbol(), position.getLeftValue()); + // handle z-index if (position.getZIndex() >= 0) { childElement diff --git a/server/src/com/vaadin/ui/AbstractOrderedLayout.java b/server/src/com/vaadin/ui/AbstractOrderedLayout.java index 3aec3b2d7a..0214ff4be1 100644 --- a/server/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/server/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -477,11 +477,32 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements public void readDesign(Element design, DesignContext designContext) { // process default attributes super.readDesign(design, designContext); - // handle margin + + // handle margins if (design.hasAttr("margin")) { setMargin(DesignAttributeHandler.readAttribute("margin", design.attributes(), Boolean.class)); + } else { + boolean marginLeft = DesignAttributeHandler.readAttribute( + "margin-left", design.attributes(), getMargin().hasLeft(), + Boolean.class); + + boolean marginRight = DesignAttributeHandler.readAttribute( + "margin-right", design.attributes(), + getMargin().hasRight(), Boolean.class); + + boolean marginTop = DesignAttributeHandler.readAttribute( + "margin-top", design.attributes(), getMargin().hasTop(), + Boolean.class); + + boolean marginBottom = DesignAttributeHandler.readAttribute( + "margin-bottom", design.attributes(), getMargin() + .hasBottom(), Boolean.class); + + setMargin(new MarginInfo(marginTop, marginBottom, marginLeft, + marginRight)); } + // handle children for (Element childComponent : design.children()) { Attributes attr = childComponent.attributes(); @@ -532,12 +553,36 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements public void writeDesign(Element design, DesignContext designContext) { // write default attributes super.writeDesign(design, designContext); - // handle margin + AbstractOrderedLayout def = (AbstractOrderedLayout) designContext .getDefaultInstance(this); - if (getMargin().getBitMask() != def.getMargin().getBitMask()) { - design.attr("margin", ""); + + // handle margin + MarginInfo marginInfo = getMargin(); + + if (marginInfo.hasAll()) { + DesignAttributeHandler.writeAttribute("margin", + design.attributes(), marginInfo.hasAll(), def.getMargin() + .hasAll(), Boolean.class); + } else { + + DesignAttributeHandler.writeAttribute("margin-left", design + .attributes(), marginInfo.hasLeft(), def.getMargin() + .hasLeft(), Boolean.class); + + DesignAttributeHandler.writeAttribute("margin-right", design + .attributes(), marginInfo.hasRight(), def.getMargin() + .hasRight(), Boolean.class); + + DesignAttributeHandler.writeAttribute("margin-top", design + .attributes(), marginInfo.hasTop(), def.getMargin() + .hasTop(), Boolean.class); + + DesignAttributeHandler.writeAttribute("margin-bottom", design + .attributes(), marginInfo.hasBottom(), def.getMargin() + .hasBottom(), Boolean.class); } + // handle children if (!designContext.shouldWriteChildren(this, def)) { return; @@ -578,6 +623,10 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements protected Collection<String> getCustomAttributes() { Collection<String> customAttributes = super.getCustomAttributes(); customAttributes.add("margin"); + customAttributes.add("margin-left"); + customAttributes.add("margin-right"); + customAttributes.add("margin-top"); + customAttributes.add("margin-bottom"); return customAttributes; } diff --git a/server/src/com/vaadin/ui/AbstractSelect.java b/server/src/com/vaadin/ui/AbstractSelect.java index 4c5e6b6ea3..6ac0dad5e4 100644 --- a/server/src/com/vaadin/ui/AbstractSelect.java +++ b/server/src/com/vaadin/ui/AbstractSelect.java @@ -2188,28 +2188,13 @@ public abstract class AbstractSelect extends AbstractField<Object> implements } @Override - public void readDesign(Element design, DesignContext designContext) { + public void readDesign(Element design, DesignContext context) { // handle default attributes - super.readDesign(design, designContext); + super.readDesign(design, context); // handle children specifying selectable items (<option>) Set<String> selected = new HashSet<String>(); for (Element child : design.children()) { - if (!"option".equals(child.nodeName())) { - throw new DesignException( - "Unsupported child element in a select: " - + child.nodeName() + "."); - } - String itemId = child.html(); - addItem(itemId); - if (child.hasAttr("icon")) { - setItemIcon( - itemId, - DesignAttributeHandler.readAttribute("icon", - child.attributes(), Resource.class)); - } - if (child.hasAttr("selected")) { - selected.add(itemId); - } + readItem(child, selected, context); } if (!selected.isEmpty()) { if (isMultiSelect()) { @@ -2223,29 +2208,117 @@ public abstract class AbstractSelect extends AbstractField<Object> implements } } + /** + * Reads an Item from a design and inserts it into the data source. + * Hierarchical select components should override this method to recursively + * recursively read any child items as well. + * + * @since + * @param child + * a child element representing the item + * @param selected + * A set accumulating selected items. If the item that is read is + * marked as selected, its item id should be added to this set. + * @param context + * the DesignContext instance used in parsing + * @return the item id of the new item + * + * @throws DesignException + * if the tag name of the {@code child} element is not + * {@code option}. + */ + protected String readItem(Element child, Set<String> selected, + DesignContext context) { + if (!"option".equals(child.tagName())) { + throw new DesignException("Unrecognized child element in " + + getClass().getSimpleName() + ": " + child.tagName()); + } + + String itemId; + if (child.hasAttr("item-id")) { + itemId = child.attr("item-id"); + addItem(itemId); + setItemCaption(itemId, child.html()); + } else { + addItem(itemId = child.html()); + } + + if (child.hasAttr("icon")) { + setItemIcon( + itemId, + DesignAttributeHandler.readAttribute("icon", + child.attributes(), Resource.class)); + } + + if (child.hasAttr("selected")) { + selected.add(itemId); + } + + return itemId; + } + @Override - public void writeDesign(Element design, DesignContext designContext) { + public void writeDesign(Element design, DesignContext context) { // Write default attributes - super.writeDesign(design, designContext); + super.writeDesign(design, context); // Write options if warranted - if (designContext.shouldWriteData(this)) { - for (Object itemId : getItemIds()) { - Element optionElement = design.appendElement("option"); + if (context.shouldWriteData(this)) { + writeItems(design, context); + } + } - optionElement.html(getItemCaption(itemId)); + /** + * Writes the data source items to a design. Hierarchical select components + * should override this method to only write the root items. + * + * @since + * @param design + * the element into which to insert the items + * @param context + * the DesignContext instance used in writing + */ + protected void writeItems(Element design, DesignContext context) { + for (Object itemId : getItemIds()) { + writeItem(design, itemId, context); + } + } - Resource icon = getItemIcon(itemId); - if (icon != null) { - DesignAttributeHandler.writeAttribute("icon", - optionElement.attributes(), icon, null, - Resource.class); - } + /** + * Writes a data source Item to a design. Hierarchical select components + * should override this method to recursively write any child items as well. + * + * @since + * @param design + * the element into which to insert the item + * @param itemId + * the id of the item to write + * @param context + * the DesignContext instance used in writing + * @return + */ + protected Element writeItem(Element design, Object itemId, + DesignContext context) { + Element element = design.appendElement("option"); - if (isSelected(itemId)) { - optionElement.attr("selected", ""); - } - } + String caption = getItemCaption(itemId); + if (caption != null && !caption.equals(itemId.toString())) { + element.html(caption); + element.attr("item-id", itemId.toString()); + } else { + element.html(itemId.toString()); } + + Resource icon = getItemIcon(itemId); + if (icon != null) { + DesignAttributeHandler.writeAttribute("icon", element.attributes(), + icon, null, Resource.class); + } + + if (isSelected(itemId)) { + element.attr("selected", ""); + } + + return element; } }
\ No newline at end of file diff --git a/server/src/com/vaadin/ui/ColorPicker.java b/server/src/com/vaadin/ui/ColorPicker.java index f65b67db72..9e46c4e718 100644 --- a/server/src/com/vaadin/ui/ColorPicker.java +++ b/server/src/com/vaadin/ui/ColorPicker.java @@ -64,16 +64,4 @@ public class ColorPicker extends AbstractColorPicker { addStyleName(STYLENAME_DEFAULT); } - @Override - public void beforeClientResponse(boolean initial) { - super.beforeClientResponse(initial); - - if (isDefaultCaptionEnabled() - && ((getState().caption == null || "" - .equals(getState().caption))) - && "".equals(getState().width)) { - getState().width = "100px"; - } - } - } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index cd97e90e2e..1d7eac7b3c 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -37,6 +37,10 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + import com.google.gwt.thirdparty.guava.common.collect.Sets; import com.google.gwt.thirdparty.guava.common.collect.Sets.SetView; import com.vaadin.data.Container; @@ -93,6 +97,10 @@ import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.util.SharedUtil; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; +import com.vaadin.ui.renderers.HtmlRenderer; import com.vaadin.ui.renderers.Renderer; import com.vaadin.ui.renderers.TextRenderer; import com.vaadin.util.ReflectTools; @@ -1448,7 +1456,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier, * @param <ROWTYPE> * the type of the rows in the section */ - protected static abstract class StaticSection<ROWTYPE extends StaticSection.StaticRow<?>> + abstract static class StaticSection<ROWTYPE extends StaticSection.StaticRow<?>> implements Serializable { /** @@ -1620,6 +1628,86 @@ public class Grid extends AbstractComponent implements SelectionNotifier, getRowState().styleName = styleName; } + /** + * Writes the declarative design to the given table row element. + * + * @since + * @param trElement + * Element to write design to + * @param designContext + * the design context + */ + protected void writeDesign(Element trElement, + DesignContext designContext) { + Set<CELLTYPE> visited = new HashSet<CELLTYPE>(); + for (Grid.Column column : section.grid.getColumns()) { + CELLTYPE cell = getCell(column.getPropertyId()); + if (visited.contains(cell)) { + continue; + } + visited.add(cell); + + Element cellElement = trElement + .appendElement(getCellTagName()); + cell.writeDesign(cellElement, designContext); + + for (Entry<Set<CELLTYPE>, CELLTYPE> entry : cellGroups + .entrySet()) { + if (entry.getValue() == cell) { + cellElement.attr("colspan", "" + + entry.getKey().size()); + break; + } + } + } + } + + /** + * Reads the declarative design from the given table row element. + * + * @since + * @param trElement + * Element to read design from + * @param designContext + * the design context + * @throws DesignException + * if the given table row contains unexpected children + */ + protected void readDesign(Element trElement, + DesignContext designContext) throws DesignException { + Elements cellElements = trElement.children(); + int totalColSpans = 0; + for (int i = 0; i < cellElements.size(); ++i) { + Element element = cellElements.get(i); + if (!element.tagName().equals(getCellTagName())) { + throw new DesignException( + "Unexpected element in tr while expecting " + + getCellTagName() + ": " + + element.tagName()); + } + + int columnIndex = i + totalColSpans; + + int colspan = DesignAttributeHandler.readAttribute( + "colspan", element.attributes(), 1, int.class); + + Set<CELLTYPE> cells = new HashSet<CELLTYPE>(); + for (int c = 0; c < colspan; ++c) { + cells.add(getCell(section.grid.getColumns() + .get(columnIndex + c).getPropertyId())); + } + + if (colspan > 1) { + totalColSpans += colspan - 1; + join(cells).readDesign(element, designContext); + } else { + cells.iterator().next() + .readDesign(element, designContext); + } + } + } + + abstract protected String getCellTagName(); } /** @@ -1739,6 +1827,15 @@ public class Grid extends AbstractComponent implements SelectionNotifier, } /** + * Returns the type of content stored in this cell. + * + * @return cell content type + */ + public GridStaticCellType getCellType() { + return cellState.type; + } + + /** * Returns the custom style name for this cell. * * @return the style name or null if no style name has been set @@ -1766,6 +1863,57 @@ public class Grid extends AbstractComponent implements SelectionNotifier, cellState.connector = null; } } + + /** + * Writes the declarative design to the given table cell element. + * + * @since + * @param cellElement + * Element to write design to + * @param designContext + * the design context + */ + protected void writeDesign(Element cellElement, + DesignContext designContext) { + switch (cellState.type) { + case TEXT: + DesignAttributeHandler.writeAttribute("plain-text", + cellElement.attributes(), "", null, String.class); + cellElement.appendText(getText()); + break; + case HTML: + cellElement.append(getHtml()); + break; + case WIDGET: + cellElement.appendChild(designContext + .createElement(getComponent())); + break; + } + } + + /** + * Reads the declarative design from the given table cell element. + * + * @since + * @param cellElement + * Element to read design from + * @param designContext + * the design context + */ + protected void readDesign(Element cellElement, + DesignContext designContext) { + if (!cellElement.hasAttr("plain-text")) { + if (cellElement.children().size() > 0 + && cellElement.child(0).tagName().contains("-")) { + setComponent(designContext.readDesign(cellElement + .child(0))); + } else { + setHtml(cellElement.html()); + } + } else { + setText(cellElement.html()); + } + } } protected Grid grid; @@ -1995,6 +2143,50 @@ public class Grid extends AbstractComponent implements SelectionNotifier, } return false; } + + /** + * Writes the declarative design to the given table section element. + * + * @since + * @param tableSectionElement + * Element to write design to + * @param designContext + * the design context + */ + protected void writeDesign(Element tableSectionElement, + DesignContext designContext) { + for (ROWTYPE row : rows) { + row.writeDesign(tableSectionElement.appendElement("tr"), + designContext); + } + } + + /** + * Writes the declarative design from the given table section element. + * + * @since + * @param tableSectionElement + * Element to read design from + * @param designContext + * the design context + * @throws DesignException + * if the table section contains unexpected children + */ + protected void readDesign(Element tableSectionElement, + DesignContext designContext) throws DesignException { + while (rows.size() > 0) { + removeRow(0); + } + + for (Element row : tableSectionElement.children()) { + if (!row.tagName().equals("tr")) { + throw new DesignException("Unexpected element in " + + tableSectionElement.tagName() + ": " + + row.tagName()); + } + appendRow().readDesign(row, designContext); + } + } } /** @@ -2092,6 +2284,16 @@ public class Grid extends AbstractComponent implements SelectionNotifier, } } } + + @Override + protected void readDesign(Element tableSectionElement, + DesignContext designContext) { + super.readDesign(tableSectionElement, designContext); + + if (defaultRow == null && !rows.isEmpty()) { + grid.setDefaultHeaderRow(rows.get(0)); + } + } } /** @@ -2107,10 +2309,41 @@ public class Grid extends AbstractComponent implements SelectionNotifier, getRowState().defaultRow = value; } + private boolean isDefaultRow() { + return getRowState().defaultRow; + } + @Override protected HeaderCell createCell() { return new HeaderCell(this); } + + @Override + protected String getCellTagName() { + return "th"; + } + + @Override + protected void writeDesign(Element trElement, + DesignContext designContext) { + super.writeDesign(trElement, designContext); + + if (section.grid.getDefaultHeaderRow() == this) { + DesignAttributeHandler.writeAttribute("default", + trElement.attributes(), true, null, boolean.class); + } + } + + @Override + protected void readDesign(Element trElement, DesignContext designContext) { + super.readDesign(trElement, designContext); + + boolean defaultRow = DesignAttributeHandler.readAttribute( + "default", trElement.attributes(), false, boolean.class); + if (defaultRow) { + section.grid.setDefaultHeaderRow(this); + } + } } /** @@ -2167,6 +2400,11 @@ public class Grid extends AbstractComponent implements SelectionNotifier, return new FooterCell(this); } + @Override + protected String getCellTagName() { + return "td"; + } + } /** @@ -2930,6 +3168,80 @@ public class Grid extends AbstractComponent implements SelectionNotifier, public boolean isHidable() { return getState().hidable; } + + /* + * Writes the design attributes for this column into given element. + * + * @since + * + * @param design Element to write attributes into + * + * @param designContext the design context + */ + protected void writeDesign(Element design, DesignContext designContext) { + Attributes attributes = design.attributes(); + GridColumnState def = new GridColumnState(); + // Sortable is a special attribute that depends on the container. + DesignAttributeHandler.writeAttribute("sortable", attributes, + isSortable(), null, boolean.class); + DesignAttributeHandler.writeAttribute("editable", attributes, + isEditable(), def.editable, boolean.class); + DesignAttributeHandler.writeAttribute("width", attributes, + getWidth(), def.width, Double.class); + DesignAttributeHandler.writeAttribute("min-width", attributes, + getMinimumWidth(), def.minWidth, Double.class); + DesignAttributeHandler.writeAttribute("max-width", attributes, + getMaximumWidth(), def.maxWidth, Double.class); + DesignAttributeHandler.writeAttribute("expand", attributes, + getExpandRatio(), def.expandRatio, Integer.class); + DesignAttributeHandler.writeAttribute("property-id", attributes, + getPropertyId(), null, Object.class); + } + + /** + * Reads the design attributes for this column from given element. + * + * @since + * @param design + * Element to read attributes from + * @param designContext + * the design context + */ + protected void readDesign(Element design, DesignContext designContext) { + Attributes attributes = design.attributes(); + + if (design.hasAttr("sortable")) { + setSortable(DesignAttributeHandler.readAttribute("sortable", + attributes, boolean.class)); + } + + if (design.hasAttr("editable")) { + setEditable(DesignAttributeHandler.readAttribute("editable", + attributes, boolean.class)); + } + + // Read size info where necessary. + if (design.hasAttr("width")) { + setWidth(DesignAttributeHandler.readAttribute("width", + attributes, Double.class)); + } + if (design.hasAttr("min-width")) { + setMinimumWidth(DesignAttributeHandler.readAttribute( + "min-width", attributes, Double.class)); + } + if (design.hasAttr("max-width")) { + setMaximumWidth(DesignAttributeHandler.readAttribute( + "max-width", attributes, Double.class)); + } + if (design.hasAttr("expand")) { + if (design.attr("expand").isEmpty()) { + setExpandRatio(1); + } else { + setExpandRatio(DesignAttributeHandler.readAttribute( + "expand", attributes, Integer.class)); + } + } + } } /** @@ -3109,13 +3421,15 @@ public class Grid extends AbstractComponent implements SelectionNotifier, setFrozenColumnCount(columns.size()); } - // Update sortable columns - if (event.getContainer() instanceof Sortable) { - Collection<?> sortableProperties = ((Sortable) event - .getContainer()).getSortableContainerPropertyIds(); - for (Entry<Object, Column> columnEntry : columns.entrySet()) { - columnEntry.getValue().setSortable( - sortableProperties.contains(columnEntry.getKey())); + // Unset sortable for non-sortable columns. + if (datasource instanceof Sortable) { + Collection<?> sortables = ((Sortable) datasource) + .getSortableContainerPropertyIds(); + for (Object propertyId : columns.keySet()) { + Column column = columns.get(propertyId); + if (!sortables.contains(propertyId) && column.isSortable()) { + column.setSortable(false); + } } } } @@ -3233,7 +3547,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier, * Grid initial setup */ private void initGrid() { - setSelectionMode(SelectionMode.SINGLE); + setSelectionMode(getDefaultSelectionMode()); addSelectionListener(new SelectionListener() { @Override public void select(SelectionEvent event) { @@ -3905,6 +4219,12 @@ public class Grid extends AbstractComponent implements SelectionNotifier, column.setHeaderCaption(humanFriendlyPropertyId); column.setHidingToggleCaption(humanFriendlyPropertyId); + if (datasource instanceof Sortable + && ((Sortable) datasource).getSortableContainerPropertyIds() + .contains(datasourcePropertyId)) { + column.setSortable(true); + } + return column; } @@ -3986,7 +4306,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier, if (numberOfColumns < -1 || numberOfColumns > columns.size()) { throw new IllegalArgumentException( "count must be between -1 and the current number of columns (" - + columns + ")"); + + columns.size() + "): " + numberOfColumns); } getState().frozenColumnCount = numberOfColumns; @@ -5626,4 +5946,167 @@ public class Grid extends AbstractComponent implements SelectionNotifier, public boolean isDetailsVisible(Object itemId) { return datasourceExtension.isDetailsVisible(itemId); } + + protected SelectionMode getDefaultSelectionMode() { + return SelectionMode.SINGLE; + } + + @Override + public void readDesign(Element design, DesignContext context) { + super.readDesign(design, context); + + Attributes attrs = design.attributes(); + if (attrs.hasKey("editable")) { + setEditorEnabled(DesignAttributeHandler.readAttribute("editable", + attrs, boolean.class)); + } + if (attrs.hasKey("frozen-columns")) { + setFrozenColumnCount(DesignAttributeHandler.readAttribute( + "frozen-columns", attrs, int.class)); + } + if (attrs.hasKey("rows")) { + setHeightByRows(DesignAttributeHandler.readAttribute("rows", attrs, + double.class)); + setHeightMode(HeightMode.ROW); + } + if (attrs.hasKey("selection-mode")) { + setSelectionMode(DesignAttributeHandler.readAttribute( + "selection-mode", attrs, SelectionMode.class)); + } + + if (design.children().size() > 0) { + if (design.children().size() > 1 + || !design.child(0).tagName().equals("table")) { + throw new DesignException( + "Grid needs to have a table element as its only child"); + } + Element table = design.child(0); + + Elements colgroups = table.getElementsByTag("colgroup"); + if (colgroups.size() != 1) { + throw new DesignException( + "Table element in declarative Grid needs to have a" + + " colgroup defining the columns used in Grid"); + } + + int i = 0; + for (Element col : colgroups.get(0).getElementsByTag("col")) { + String propertyId = DesignAttributeHandler.readAttribute( + "property-id", col.attributes(), "property-" + i, + String.class); + addColumn(propertyId, String.class).readDesign(col, context); + ++i; + } + + for (Element child : table.children()) { + if (child.tagName().equals("thead")) { + header.readDesign(child, context); + } else if (child.tagName().equals("tbody")) { + for (Element row : child.children()) { + Elements cells = row.children(); + Object[] data = new String[cells.size()]; + for (int c = 0; c < cells.size(); ++c) { + data[c] = cells.get(c).html(); + } + addRow(data); + } + + // Since inline data is used, set HTML renderer for columns + for (Column c : getColumns()) { + c.setRenderer(new HtmlRenderer()); + } + } else if (child.tagName().equals("tfoot")) { + footer.readDesign(child, context); + } + } + } + } + + @Override + public void writeDesign(Element design, DesignContext context) { + super.writeDesign(design, context); + + Attributes attrs = design.attributes(); + Grid def = context.getDefaultInstance(this); + + DesignAttributeHandler.writeAttribute("editable", attrs, + isEditorEnabled(), def.isEditorEnabled(), boolean.class); + + DesignAttributeHandler.writeAttribute("frozen-columns", attrs, + getFrozenColumnCount(), def.getFrozenColumnCount(), int.class); + + if (getHeightMode() == HeightMode.ROW) { + DesignAttributeHandler.writeAttribute("rows", attrs, + getHeightByRows(), def.getHeightByRows(), double.class); + } + + SelectionMode selectionMode = null; + + if (selectionModel.getClass().equals(SingleSelectionModel.class)) { + selectionMode = SelectionMode.SINGLE; + } else if (selectionModel.getClass().equals(MultiSelectionModel.class)) { + selectionMode = SelectionMode.MULTI; + } else if (selectionModel.getClass().equals(NoSelectionModel.class)) { + selectionMode = SelectionMode.NONE; + } + + assert selectionMode != null : "Unexpected selection model " + + selectionModel.getClass().getName(); + + DesignAttributeHandler.writeAttribute("selection-mode", attrs, + selectionMode, getDefaultSelectionMode(), SelectionMode.class); + + if (columns.isEmpty()) { + // Empty grid. Structure not needed. + return; + } + + // Do structure. + Element tableElement = design.appendElement("table"); + Element colGroup = tableElement.appendElement("colgroup"); + + List<Column> columnOrder = getColumns(); + for (int i = 0; i < columnOrder.size(); ++i) { + Column column = columnOrder.get(i); + Element colElement = colGroup.appendElement("col"); + column.writeDesign(colElement, context); + } + + // Always write thead. Reads correctly when there no header rows + header.writeDesign(tableElement.appendElement("thead"), context); + + if (context.shouldWriteData(this)) { + Element bodyElement = tableElement.appendElement("tbody"); + for (Object itemId : datasource.getItemIds()) { + Element tableRow = bodyElement.appendElement("tr"); + for (Column c : getColumns()) { + Object value = datasource.getItem(itemId) + .getItemProperty(c.getPropertyId()).getValue(); + tableRow.appendElement("td").append( + (value != null ? value.toString() : "")); + } + } + } + + if (footer.getRowCount() > 0) { + footer.writeDesign(tableElement.appendElement("tfoot"), context); + } + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> result = super.getCustomAttributes(); + result.add("editor-enabled"); + result.add("editable"); + result.add("frozen-column-count"); + result.add("frozen-columns"); + result.add("height-by-rows"); + result.add("rows"); + result.add("selection-mode"); + result.add("header-visible"); + result.add("footer-visible"); + result.add("editor-error-handler"); + result.add("height-mode"); + return result; + } } diff --git a/server/src/com/vaadin/ui/GridLayout.java b/server/src/com/vaadin/ui/GridLayout.java index 96854c5b1b..35110b39ab 100644 --- a/server/src/com/vaadin/ui/GridLayout.java +++ b/server/src/com/vaadin/ui/GridLayout.java @@ -17,12 +17,21 @@ package com.vaadin.ui; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; + +import org.jsoup.nodes.Attributes; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; import com.vaadin.event.LayoutEvents.LayoutClickEvent; import com.vaadin.event.LayoutEvents.LayoutClickListener; @@ -36,6 +45,8 @@ import com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.gridlayout.GridLayoutServerRpc; import com.vaadin.shared.ui.gridlayout.GridLayoutState; import com.vaadin.shared.ui.gridlayout.GridLayoutState.ChildComponentData; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; /** * A layout where the components are laid out on a grid using cell coordinates. @@ -1282,4 +1293,316 @@ public class GridLayout extends AbstractLayout implements public boolean isHideEmptyRowsAndColumns() { return getState(false).hideEmptyRowsAndColumns; } -} + + /** + * {@inheritDoc} + * <p> + * After reading the design, cursorY is set to point to a row outside of the + * GridLayout area. CursorX is reset to 0. + */ + @Override + public void readDesign(Element design, DesignContext designContext) { + super.readDesign(design, designContext); + + // Prepare a 2D map for reading column contents + Elements rowElements = design.getElementsByTag("row"); + List<Map<Integer, Component>> rows = new ArrayList<Map<Integer, Component>>(); + for (int i = 0; i < rowElements.size(); ++i) { + rows.add(new HashMap<Integer, Component>()); + } + setRows(Math.max(rows.size(), 1)); + + List<Integer> columnExpandRatios = new ArrayList<Integer>(); + for (int row = 0; row < rowElements.size(); ++row) { + Element rowElement = rowElements.get(row); + + // Row Expand + if (rowElement.hasAttr("expand")) { + int expand = DesignAttributeHandler.readAttribute("expand", + rowElement.attributes(), int.class); + setRowExpandRatio(row, expand); + } + + Elements cols = rowElement.children(); + + // Amount of skipped columns due to spanned components + int skippedColumns = 0; + + for (int column = 0; column < cols.size(); ++column) { + while (rows.get(row).containsKey(column + skippedColumns)) { + // Skip any spanned components + skippedColumns++; + } + + Element col = cols.get(column); + Component child = null; + + if (col.children().size() > 0) { + child = designContext.readDesign(col.child(0)); + // TODO: Currently ignoring any extra children. + // Needs Error handling? + } // Else: Empty placeholder. No child component. + + // Handle rowspan and colspan for this child component + Attributes attr = col.attributes(); + int colspan = DesignAttributeHandler.readAttribute("colspan", + attr, 1, int.class); + int rowspan = DesignAttributeHandler.readAttribute("rowspan", + attr, 1, int.class); + + for (int rowIndex = row; rowIndex < row + rowspan; ++rowIndex) { + for (int colIndex = column; colIndex < column + colspan; ++colIndex) { + if (rowIndex == rows.size()) { + // Rowspan with not enough rows. Fix by adding rows. + rows.add(new HashMap<Integer, Component>()); + } + rows.get(rowIndex) + .put(colIndex + skippedColumns, child); + } + } + + // Read column expand ratios if handling the first row. + if (row == 0) { + if (col.hasAttr("expand")) { + for (String expand : col.attr("expand").split(",")) { + columnExpandRatios.add(Integer.parseInt(expand)); + } + } else { + for (int c = 0; c < colspan; ++c) { + columnExpandRatios.add(0); + } + } + } + + skippedColumns += (colspan - 1); + } + } + + // Calculate highest column count and set columns + int colMax = 0; + for (Map<Integer, Component> cols : rows) { + if (colMax < cols.size()) { + colMax = cols.size(); + } + } + setColumns(Math.max(colMax, 1)); + + for (int i = 0; i < columnExpandRatios.size(); ++i) { + setColumnExpandRatio(i, columnExpandRatios.get(i)); + } + + // Reiterate through the 2D map and add components to GridLayout + Set<Component> visited = new HashSet<Component>(); + + // Ignore any missing components + visited.add(null); + + for (int i = 0; i < rows.size(); ++i) { + Map<Integer, Component> row = rows.get(i); + for (int j = 0; j < colMax; ++j) { + Component child = row.get(j); + if (visited.contains(child)) { + // Empty location or already handled child + continue; + } + visited.add(child); + + // Figure out col and rowspan from 2D map + int colspan = 0; + while (j + colspan + 1 < row.size() + && row.get(j + colspan + 1) == child) { + ++colspan; + } + + int rowspan = 0; + while (i + rowspan + 1 < rows.size() + && rows.get(i + rowspan + 1).get(j) == child) { + ++rowspan; + } + + // Add component with area + addComponent(child, j, i, j + colspan, i + rowspan); + } + } + // Set cursor position explicitly + setCursorY(getRows()); + setCursorX(0); + } + + @Override + public void writeDesign(Element design, DesignContext designContext) { + super.writeDesign(design, designContext); + + GridLayout def = designContext.getDefaultInstance(this); + if (components.isEmpty() + || !designContext.shouldWriteChildren(this, def)) { + return; + } + + final Map<Connector, ChildComponentData> childData = getState().childData; + + // Make a 2D map of component areas. + Component[][] componentMap = new Component[getState().rows][getState().columns]; + final Component dummyComponent = new Label(""); + + for (Component component : components) { + ChildComponentData coords = childData.get(component); + for (int row = coords.row1; row <= coords.row2; ++row) { + for (int col = coords.column1; col <= coords.column2; ++col) { + componentMap[row][col] = component; + } + } + } + + // Go through the map and write only needed column tags + Set<Connector> visited = new HashSet<Connector>(); + + // Skip the dummy placeholder + visited.add(dummyComponent); + + for (int i = 0; i < componentMap.length; ++i) { + Element row = design.appendElement("row"); + + // Row Expand + DesignAttributeHandler.writeAttribute("expand", row.attributes(), + (int) getRowExpandRatio(i), 0, int.class); + + int colspan = 1; + Element col; + for (int j = 0; j < componentMap[i].length; ++j) { + Component child = componentMap[i][j]; + if (child != null) { + if (visited.contains(child)) { + // Child has already been written in the design + continue; + } + visited.add(child); + + Element childElement = designContext.createElement(child); + col = row.appendElement("column"); + + // Write child data into design + ChildComponentData coords = childData.get(child); + + Alignment alignment = getComponentAlignment(child); + if (alignment.isMiddle()) { + childElement.attr(":middle", ""); + } else if (alignment.isBottom()) { + childElement.attr(":bottom", ""); + } + if (alignment.isCenter()) { + childElement.attr(":center", ""); + } else if (alignment.isRight()) { + childElement.attr(":right", ""); + } + + col.appendChild(childElement); + if (coords.row1 != coords.row2) { + col.attr("rowspan", "" + + (1 + coords.row2 - coords.row1)); + } + + colspan = 1 + coords.column2 - coords.column1; + if (colspan > 1) { + col.attr("colspan", "" + colspan); + } + + } else { + boolean hasExpands = false; + if (i == 0 + && lastComponentOnRow(componentMap[i], j, visited)) { + // A column with expand and no content in the end of + // first row needs to be present. + for (int c = j; c < componentMap[i].length; ++c) { + if ((int) getColumnExpandRatio(c) > 0) { + hasExpands = true; + } + } + } + + if (lastComponentOnRow(componentMap[i], j, visited) + && !hasExpands) { + continue; + } + + // Empty placeholder tag. + col = row.appendElement("column"); + + // Use colspan to make placeholders more pleasant + while (j + colspan < componentMap[i].length + && componentMap[i][j + colspan] == child) { + ++colspan; + } + + int rowspan = getRowSpan(componentMap, i, j, colspan, child); + if (colspan > 1) { + col.attr("colspan", "" + colspan); + } + if (rowspan > 1) { + col.attr("rowspan", "" + rowspan); + } + for (int x = 0; x < rowspan; ++x) { + for (int y = 0; y < colspan; ++y) { + // Mark handled columns + componentMap[i + x][j + y] = dummyComponent; + } + } + } + + // Column expands + if (i == 0) { + // Only do expands on first row + String expands = ""; + boolean expandRatios = false; + for (int c = 0; c < colspan; ++c) { + int colExpand = (int) getColumnExpandRatio(j + c); + if (colExpand > 0) { + expandRatios = true; + } + expands += (c > 0 ? "," : "") + colExpand; + } + if (expandRatios) { + col.attr("expand", expands); + } + } + + j += colspan - 1; + } + } + } + + private int getRowSpan(Component[][] compMap, int i, int j, int colspan, + Component child) { + int rowspan = 1; + while (i + rowspan < compMap.length && compMap[i + rowspan][j] == child) { + for (int k = 0; k < colspan; ++k) { + if (compMap[i + rowspan][j + k] != child) { + return rowspan; + } + } + rowspan++; + } + return rowspan; + } + + private boolean lastComponentOnRow(Component[] componentArray, int j, + Set<Connector> visited) { + while ((++j) < componentArray.length) { + Component child = componentArray[j]; + if (child != null && !visited.contains(child)) { + return false; + } + } + return true; + } + + @Override + protected Collection<String> getCustomAttributes() { + Collection<String> result = super.getCustomAttributes(); + result.add("cursor-x"); + result.add("cursor-y"); + result.add("rows"); + result.add("columns"); + return result; + } +}
\ No newline at end of file diff --git a/server/src/com/vaadin/ui/MenuBar.java b/server/src/com/vaadin/ui/MenuBar.java index 747ce42727..c11cf02aaf 100644 --- a/server/src/com/vaadin/ui/MenuBar.java +++ b/server/src/com/vaadin/ui/MenuBar.java @@ -1008,8 +1008,9 @@ public class MenuBar extends AbstractComponent implements LegacyComponent, if (node instanceof Element && ((Element) node).tagName().equals("menu")) { subMenus.add((Element) node); + } else { + caption += node.toString(); } - caption += node.toString(); } MenuItem menu = new MenuItem(caption.trim(), icon, null); diff --git a/server/src/com/vaadin/ui/OptionGroup.java b/server/src/com/vaadin/ui/OptionGroup.java index 393f5399f6..ff2e384285 100644 --- a/server/src/com/vaadin/ui/OptionGroup.java +++ b/server/src/com/vaadin/ui/OptionGroup.java @@ -21,6 +21,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.jsoup.nodes.Element; + import com.vaadin.data.Container; import com.vaadin.event.FieldEvents; import com.vaadin.event.FieldEvents.BlurEvent; @@ -30,6 +32,7 @@ import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.shared.ui.optiongroup.OptionGroupConstants; +import com.vaadin.ui.declarative.DesignContext; /** * Configures select to be used as an option group. @@ -252,4 +255,28 @@ public class OptionGroup extends AbstractSelect implements public boolean isHtmlContentAllowed() { return htmlContentAllowed; } + + @Override + protected String readItem(Element child, Set<String> selected, + DesignContext context) { + String itemId = super.readItem(child, selected, context); + + if (child.hasAttr("disabled")) { + setItemEnabled(itemId, false); + } + + return itemId; + } + + @Override + protected Element writeItem(Element design, Object itemId, + DesignContext context) { + Element elem = super.writeItem(design, itemId, context); + + if (!isItemEnabled(itemId)) { + elem.attr("disabled", ""); + } + + return elem; + } } diff --git a/server/src/com/vaadin/ui/Tree.java b/server/src/com/vaadin/ui/Tree.java index a86a9bd64b..6f5ec96f63 100644 --- a/server/src/com/vaadin/ui/Tree.java +++ b/server/src/com/vaadin/ui/Tree.java @@ -30,6 +30,8 @@ import java.util.Set; import java.util.Stack; import java.util.StringTokenizer; +import org.jsoup.nodes.Element; + import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.util.ContainerHierarchicalWrapper; @@ -57,6 +59,9 @@ import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.MultiSelectMode; import com.vaadin.shared.ui.dd.VerticalDropLocation; import com.vaadin.shared.ui.tree.TreeConstants; +import com.vaadin.ui.declarative.DesignAttributeHandler; +import com.vaadin.ui.declarative.DesignContext; +import com.vaadin.ui.declarative.DesignException; import com.vaadin.util.ReflectTools; /** @@ -1301,19 +1306,6 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, } } - /** - * Tree does not support lazy options loading mode. Setting this true will - * throw UnsupportedOperationException. - * - * @see com.vaadin.ui.Select#setLazyLoading(boolean) - */ - public void setLazyLoading(boolean useLazyLoading) { - if (useLazyLoading) { - throw new UnsupportedOperationException( - "Lazy options loading is not supported by Tree."); - } - } - private ItemStyleGenerator itemStyleGenerator; private DropHandler dropHandler; @@ -1803,4 +1795,105 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, } expanded.removeAll(removedItemIds); } + + /** + * Reads an Item from a design and inserts it into the data source. + * Recursively handles any children of the item as well. + * + * @since + * @param node + * an element representing the item (tree node). + * @param selected + * A set accumulating selected items. If the item that is read is + * marked as selected, its item id should be added to this set. + * @param context + * the DesignContext instance used in parsing + * @return the item id of the new item + * + * @throws DesignException + * if the tag name of the {@code node} element is not + * {@code node}. + */ + @Override + protected String readItem(Element node, Set<String> selected, + DesignContext context) { + + if (!"node".equals(node.tagName())) { + throw new DesignException("Unrecognized child element in " + + getClass().getSimpleName() + ": " + node.tagName()); + } + + String itemId = node.attr("text"); + addItem(itemId); + if (node.hasAttr("icon")) { + Resource icon = DesignAttributeHandler.readAttribute("icon", + node.attributes(), Resource.class); + setItemIcon(itemId, icon); + } + if (node.hasAttr("selected")) { + selected.add(itemId); + } + + for (Element child : node.children()) { + String childItemId = readItem(child, selected, context); + setParent(childItemId, itemId); + } + return itemId; + } + + /** + * Recursively writes the root items and their children to a design. + * + * @since + * @param design + * the element into which to insert the items + * @param context + * the DesignContext instance used in writing + */ + @Override + protected void writeItems(Element design, DesignContext context) { + for (Object itemId : rootItemIds()) { + writeItem(design, itemId, context); + } + } + + /** + * Recursively writes a data source Item and its children to a design. + * + * @since + * @param design + * the element into which to insert the item + * @param itemId + * the id of the item to write + * @param context + * the DesignContext instance used in writing + * @return + */ + @Override + protected Element writeItem(Element design, Object itemId, + DesignContext context) { + Element element = design.appendElement("node"); + + element.attr("text", itemId.toString()); + + Resource icon = getItemIcon(itemId); + if (icon != null) { + DesignAttributeHandler.writeAttribute("icon", element.attributes(), + icon, null, Resource.class); + } + + if (isSelected(itemId)) { + element.attr("selected", ""); + } + + Collection<?> children = getChildren(itemId); + if (children != null) { + // Yeah... see #5864 + for (Object childItemId : children) { + writeItem(element, childItemId, context); + } + } + + return element; + } } diff --git a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java index 2b446bda0e..e93a5c51cb 100644 --- a/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java +++ b/server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java @@ -218,18 +218,68 @@ public class DesignAttributeHandler implements Serializable { } /** - * Reads the given attribute from a set of attributes. + * Writes the given attribute value to a set of attributes if it differs + * from the default attribute value. + * + * @param attribute + * the attribute key + * @param attributes + * the set of attributes where the new attribute is written + * @param value + * the attribute value + * @param defaultValue + * the default attribute value + * @param inputType + * the type of the input value + */ + public static <T> void writeAttribute(String attribute, + Attributes attributes, T value, T defaultValue, Class<T> inputType) { + if (!getFormatter().canConvert(inputType)) { + throw new IllegalArgumentException("input type: " + + inputType.getName() + " not supported"); + } + if (!SharedUtil.equals(value, defaultValue)) { + String attributeValue = toAttributeValue(inputType, value); + attributes.put(attribute, attributeValue); + } + } + + /** + * Reads the given attribute from a set of attributes. If attribute does not + * exist return a given default value. * * @param attribute * the attribute key * @param attributes * the set of attributes to read from + * @param defaultValue + * the default value to return if attribute does not exist * @param outputType * the output type for the attribute * @return the attribute value or the default value if the attribute is not * found */ public static <T> T readAttribute(String attribute, Attributes attributes, + T defaultValue, Class<T> outputType) { + T value = readAttribute(attribute, attributes, outputType); + if (value != null) { + return value; + } + return defaultValue; + } + + /** + * Reads the given attribute from a set of attributes. + * + * @param attribute + * the attribute key + * @param attributes + * the set of attributes to read from + * @param outputType + * the output type for the attribute + * @return the attribute value or null + */ + public static <T> T readAttribute(String attribute, Attributes attributes, Class<T> outputType) { if (!getFormatter().canConvert(outputType)) { throw new IllegalArgumentException("output type: " @@ -249,33 +299,6 @@ public class DesignAttributeHandler implements Serializable { } /** - * Writes the given attribute value to a set of attributes if it differs - * from the default attribute value. - * - * @param attribute - * the attribute key - * @param attributes - * the set of attributes where the new attribute is written - * @param value - * the attribute value - * @param defaultValue - * the default attribute value - * @param inputType - * the type of the input value - */ - public static <T> void writeAttribute(String attribute, - Attributes attributes, T value, T defaultValue, Class<T> inputType) { - if (!getFormatter().canConvert(inputType)) { - throw new IllegalArgumentException("input type: " - + inputType.getName() + " not supported"); - } - if (!SharedUtil.equals(value, defaultValue)) { - String attributeValue = toAttributeValue(inputType, value); - attributes.put(attribute, attributeValue); - } - } - - /** * Returns the design attribute name corresponding the given method name. * For example given a method name <code>setPrimaryStyleName</code> the * return value would be <code>primary-style-name</code> @@ -425,5 +448,4 @@ public class DesignAttributeHandler implements Serializable { return (methods != null && methods.length > 1) ? methods[1] : null; } } - } diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java index fe3abcfb77..657e15896c 100644 --- a/server/src/com/vaadin/ui/declarative/DesignContext.java +++ b/server/src/com/vaadin/ui/declarative/DesignContext.java @@ -170,6 +170,7 @@ public class DesignContext implements Serializable { * component, the mapping from c to the string is removed. Similarly, if * component was mapped to some string s different from localId, the mapping * from s to component is removed. + * * @param component * The component whose local id is to be set. * @param localId @@ -185,6 +186,18 @@ public class DesignContext implements Serializable { } /** + * Returns the local id for a component + * + * @param component + * The component whose local id to get. + * @return the local id of the component, or null if the component has no + * local id assigned + */ + public String getComponentLocalId(Component component) { + return componentToLocalId.get(component); + } + + /** * Creates a mapping between the given caption and the component. Returns * true if caption was already mapped to some component. * |