summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/ui
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2015-04-15 10:24:44 +0300
committerTeemu Suo-Anttila <teemusa@vaadin.com>2015-04-15 10:24:44 +0300
commit2c4e533c9f2aa68211329ea6ce4de0b577407863 (patch)
tree1d9c1cc8bb72678b0edb991947c6fc94112baf0f /server/src/com/vaadin/ui
parent93235f05c9dd4739cdccf87a4858a61904dbf4b5 (diff)
parent7cb23bc63f794a7549dd79c37da2f8bb8e88e20d (diff)
downloadvaadin-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/vaadin/ui')
-rw-r--r--server/src/com/vaadin/ui/AbsoluteLayout.java3
-rw-r--r--server/src/com/vaadin/ui/AbstractOrderedLayout.java57
-rw-r--r--server/src/com/vaadin/ui/AbstractSelect.java141
-rw-r--r--server/src/com/vaadin/ui/ColorPicker.java12
-rw-r--r--server/src/com/vaadin/ui/Grid.java503
-rw-r--r--server/src/com/vaadin/ui/GridLayout.java325
-rw-r--r--server/src/com/vaadin/ui/MenuBar.java3
-rw-r--r--server/src/com/vaadin/ui/OptionGroup.java27
-rw-r--r--server/src/com/vaadin/ui/Tree.java119
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignAttributeHandler.java80
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignContext.java13
11 files changed, 1178 insertions, 105 deletions
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.
*