diff options
author | Jonatan Kronqvist <jonatan.kronqvist@itmill.com> | 2011-06-16 07:55:38 +0000 |
---|---|---|
committer | Jonatan Kronqvist <jonatan.kronqvist@itmill.com> | 2011-06-16 07:55:38 +0000 |
commit | d9ca864ff7acf2eedb4edda46276fdb98ad35a7a (patch) | |
tree | ab810b3b0fb817d7a7a185064bf6b5d101710c64 | |
parent | 5271adfa5c18a458dc69fc444fea454ad3542b80 (diff) | |
download | vaadin-framework-d9ca864ff7acf2eedb4edda46276fdb98ad35a7a.tar.gz vaadin-framework-d9ca864ff7acf2eedb4edda46276fdb98ad35a7a.zip |
Merged TreeTable into Vaadin core (#5371)
svn changeset:19416/svn branch:6.7
-rw-r--r-- | WebContent/VAADIN/themes/base/treetable/img/arrow-down.png | bin | 0 -> 1008 bytes | |||
-rw-r--r-- | WebContent/VAADIN/themes/base/treetable/img/arrow-right.png | bin | 0 -> 994 bytes | |||
-rw-r--r-- | WebContent/VAADIN/themes/base/treetable/treetable.css | 20 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java | 4 | ||||
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java | 345 | ||||
-rw-r--r-- | src/com/vaadin/ui/TreeTable.java | 579 | ||||
-rw-r--r-- | src/com/vaadin/ui/treetable/Collapsible.java | 66 | ||||
-rw-r--r-- | src/com/vaadin/ui/treetable/HierarchicalContainerOrderedWrapper.java | 58 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/table/Tables.java | 52 | ||||
-rw-r--r-- | tests/src/com/vaadin/tests/components/treetable/TreeTables.java | 291 |
10 files changed, 1387 insertions, 28 deletions
diff --git a/WebContent/VAADIN/themes/base/treetable/img/arrow-down.png b/WebContent/VAADIN/themes/base/treetable/img/arrow-down.png Binary files differnew file mode 100644 index 0000000000..cba812b799 --- /dev/null +++ b/WebContent/VAADIN/themes/base/treetable/img/arrow-down.png diff --git a/WebContent/VAADIN/themes/base/treetable/img/arrow-right.png b/WebContent/VAADIN/themes/base/treetable/img/arrow-right.png Binary files differnew file mode 100644 index 0000000000..7fe6e2b59f --- /dev/null +++ b/WebContent/VAADIN/themes/base/treetable/img/arrow-right.png diff --git a/WebContent/VAADIN/themes/base/treetable/treetable.css b/WebContent/VAADIN/themes/base/treetable/treetable.css new file mode 100644 index 0000000000..ee5aaed861 --- /dev/null +++ b/WebContent/VAADIN/themes/base/treetable/treetable.css @@ -0,0 +1,20 @@ +.v-treetable-treespacer { + display: inline-block; + background: transparent; + height: 9px; + /* defines the amount of indent per level */ + width: 18px; +} + +.v-treetable-node-closed { + background: url(../treetable/img/arrow-right.png) right center no-repeat; +} + +.v-treetable-node-open { + background: url(../treetable/img/arrow-down.png) right center no-repeat; +} + +.v-treetable .v-checkbox { + display: inline-block; + padding-bottom: 4px; +}
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java index d36c2137d8..fdf61b8292 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java @@ -1362,7 +1362,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * The key to search with * @return */ - private VScrollTableRow getRenderedRowByKey(String key) { + protected VScrollTableRow getRenderedRowByKey(String key) { if (scrollBody != null) { final Iterator<Widget> it = scrollBody.iterator(); VScrollTableRow r = null; @@ -5452,7 +5452,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler, * The row to where the selection head should move * @return Returns true if focus was moved successfully, else false */ - private boolean setRowFocus(VScrollTableRow row) { + protected boolean setRowFocus(VScrollTableRow row) { if (selectMode == SELECT_MODE_NONE) { return false; diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java b/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java new file mode 100644 index 0000000000..974e96e8ae --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VTreeTable.java @@ -0,0 +1,345 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Iterator; + +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.ImageElement; +import com.google.gwt.dom.client.SpanElement; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.RenderSpace; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.VConsole; +import com.vaadin.terminal.gwt.client.ui.VScrollTable.VScrollTableBody.VScrollTableRow; +import com.vaadin.terminal.gwt.client.ui.VTreeTable.VTreeTableScrollBody.VTreeTableRow; + +public class VTreeTable extends VScrollTable { + + public static final String ATTRIBUTE_HIERARCHY_COLUMN_INDEX = "hci"; + private boolean collapseRequest; + private boolean selectionPending; + private int colIndexOfHierarchy; + private String collapsedRowKey; + private VTreeTableScrollBody scrollBody; + + @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + FocusableScrollPanel widget = null; + int scrollPosition = 0; + if (collapseRequest) { + widget = (FocusableScrollPanel) getWidget(1); + scrollPosition = widget.getScrollPosition(); + } + colIndexOfHierarchy = uidl + .hasAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) ? uidl + .getIntAttribute(ATTRIBUTE_HIERARCHY_COLUMN_INDEX) : 0; + super.updateFromUIDL(uidl, client); + if (collapseRequest) { + if (collapsedRowKey != null && scrollBody != null) { + VScrollTableRow row = getRenderedRowByKey(collapsedRowKey); + if (row != null) { + setRowFocus(row); + focus(); + } + } + + int scrollPosition2 = widget.getScrollPosition(); + if (scrollPosition != scrollPosition2) { + VConsole.log("TT scrollpos from " + scrollPosition + " to " + + scrollPosition2); + widget.setScrollPosition(scrollPosition); + } + collapseRequest = false; + } + if (uidl.hasAttribute("focusedRow")) { + // TODO figure out if the row needs to focused at all + + // scrolled to parent by the server, focusedRow is probably the sam + // as the first row in view port + } + } + + @Override + protected VScrollTableBody createScrollBody() { + scrollBody = new VTreeTableScrollBody(); + return scrollBody; + } + + class VTreeTableScrollBody extends VScrollTable.VScrollTableBody { + private int identWidth = -1; + + VTreeTableScrollBody() { + super(); + } + + @Override + protected VScrollTableRow createRow(UIDL uidl, char[] aligns2) { + return new VTreeTableRow(uidl, aligns2); + } + + class VTreeTableRow extends + VScrollTable.VScrollTableBody.VScrollTableRow { + + private boolean isTreeCellAdded = false; + private SpanElement treeSpacer; + private boolean open; + private int depth; + private boolean canHaveChildren; + private Widget widgetInHierarchyColumn; + + public VTreeTableRow(UIDL uidl, char[] aligns2) { + super(uidl, aligns2); + } + + @Override + public void addCell(UIDL rowUidl, String text, char align, + String style, boolean textIsHTML, boolean isSorted) { + super.addCell(rowUidl, text, align, style, textIsHTML, isSorted); + + addTreeSpacer(rowUidl); + } + + private boolean addTreeSpacer(UIDL rowUidl) { + if (cellShowsTreeHierarchy(getElement().getChildCount() - 1)) { + Element container = (Element) getElement().getLastChild() + .getFirstChild(); + + if (rowUidl.hasAttribute("icon")) { + // icons are in first content cell in TreeTable + ImageElement icon = Document.get().createImageElement(); + icon.setClassName("v-icon"); + icon.setAlt("icon"); + icon.setSrc(client.translateVaadinUri(rowUidl + .getStringAttribute("icon"))); + container.insertFirst(icon); + } + + String classname = "v-treetable-treespacer"; + if (rowUidl.getBooleanAttribute("ca")) { + canHaveChildren = true; + open = rowUidl.getBooleanAttribute("open"); + classname += open ? " v-treetable-node-open" + : " v-treetable-node-closed"; + } + + treeSpacer = Document.get().createSpanElement(); + + treeSpacer.setClassName(classname); + container.insertFirst(treeSpacer); + depth = rowUidl.hasAttribute("depth") ? rowUidl + .getIntAttribute("depth") : 0; + setIdent(); + isTreeCellAdded = true; + return true; + } + return false; + } + + private boolean cellShowsTreeHierarchy(int curColIndex) { + if (isTreeCellAdded) { + return false; + } + return curColIndex == colIndexOfHierarchy + + (showRowHeaders ? 1 : 0); + } + + @Override + public void onBrowserEvent(Event event) { + if (event.getEventTarget().cast() == treeSpacer + && treeSpacer.getClassName().contains("node")) { + if (event.getTypeInt() == Event.ONMOUSEUP) { + sendToggleCollapsedUpdate(getKey()); + } + return; + } + super.onBrowserEvent(event); + } + + @Override + public void addCell(UIDL rowUidl, Widget w, char align, + String style, boolean isSorted) { + super.addCell(rowUidl, w, align, style, isSorted); + if (addTreeSpacer(rowUidl)) { + widgetInHierarchyColumn = w; + } + + } + + private void setIdent() { + if (getIdentWidth() > 0 && depth != 0) { + treeSpacer.getStyle().setWidth( + (depth + 1) * getIdentWidth(), Unit.PX); + } + } + + @Override + protected void onAttach() { + super.onAttach(); + if (getIdentWidth() < 0) { + detectIdent(this); + } + } + + @Override + public RenderSpace getAllocatedSpace(Widget child) { + if (widgetInHierarchyColumn == child) { + final int hierarchyAndIconWidth = getHierarchyAndIconWidth(); + final RenderSpace allocatedSpace = super + .getAllocatedSpace(child); + return new RenderSpace() { + @Override + public int getWidth() { + return allocatedSpace.getWidth() + - hierarchyAndIconWidth; + } + + @Override + public int getHeight() { + return allocatedSpace.getHeight(); + } + + }; + } + return super.getAllocatedSpace(child); + } + + private int getHierarchyAndIconWidth() { + int consumedSpace = treeSpacer.getOffsetWidth(); + if (treeSpacer.getParentElement().getChildCount() > 2) { + // icon next to tree spacer + consumedSpace += ((com.google.gwt.dom.client.Element) treeSpacer + .getNextSibling()).getOffsetWidth(); + } + return consumedSpace; + } + + } + + private int getIdentWidth() { + return identWidth; + } + + private void detectIdent(VTreeTableRow vTreeTableRow) { + identWidth = vTreeTableRow.treeSpacer.getOffsetWidth(); + if (identWidth == 0) { + identWidth = -1; + return; + } + Iterator<Widget> iterator = iterator(); + while (iterator.hasNext()) { + VTreeTableRow next = (VTreeTableRow) iterator.next(); + next.setIdent(); + } + } + } + + /** + * Icons rendered into first actual column in TreeTable, not to row header + * cell + */ + @Override + protected String buildCaptionHtmlSnippet(UIDL uidl) { + if (uidl.getTag().equals("column")) { + return super.buildCaptionHtmlSnippet(uidl); + } else { + String s = uidl.getStringAttribute("caption"); + return s; + } + } + + @Override + protected boolean handleNavigation(int keycode, boolean ctrl, boolean shift) { + VTreeTableRow focusedRow = (VTreeTableRow) getFocusedRow(); + if (focusedRow != null) { + if (focusedRow.canHaveChildren + && ((keycode == KeyCodes.KEY_RIGHT && !focusedRow.open) || (keycode == KeyCodes.KEY_LEFT && focusedRow.open))) { + if (!ctrl) { + client.updateVariable(paintableId, "selectCollapsed", true, + false); + } + sendToggleCollapsedUpdate(focusedRow.getKey()); + return true; + } else if (keycode == KeyCodes.KEY_RIGHT && focusedRow.open) { + // already expanded, move selection down if next is on a deeper + // level (is-a-child) + VTreeTableScrollBody body = (VTreeTableScrollBody) focusedRow + .getParent(); + Iterator<Widget> iterator = body.iterator(); + VTreeTableRow next = null; + while (iterator.hasNext()) { + next = (VTreeTableRow) iterator.next(); + if (next == focusedRow) { + next = (VTreeTableRow) iterator.next(); + break; + } + } + if (next != null) { + if (next.depth > focusedRow.depth) { + selectionPending = true; + return super.handleNavigation(getNavigationDownKey(), + ctrl, shift); + } + } else { + // Note, a minor change here for a bit false behavior if + // cache rows is disabled + last visible row + no childs for + // the node + selectionPending = true; + return super.handleNavigation(getNavigationDownKey(), ctrl, + shift); + } + } else if (keycode == KeyCodes.KEY_LEFT) { + // already collapsed move selection up to parent node + // do on the server side as the parent is not necessary + // rendered on the client, could check if parent is visible if + // a performance issue arises + + client.updateVariable(paintableId, "focusParent", + focusedRow.getKey(), true); + return true; + } + } + return super.handleNavigation(keycode, ctrl, shift); + } + + private void sendToggleCollapsedUpdate(String rowKey) { + collapsedRowKey = rowKey; + collapseRequest = true; + client.updateVariable(paintableId, "toggleCollapsed", rowKey, true); + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (event.getTypeInt() == Event.ONKEYUP && selectionPending) { + sendSelectedRows(); + } + } + + @Override + protected void sendSelectedRows() { + super.sendSelectedRows(); + selectionPending = false; + } + + @Override + protected void reOrderColumn(String columnKey, int newIndex) { + super.reOrderColumn(columnKey, newIndex); + // current impl not intelligent enough to survive without visiting the + // server to redraw content + client.sendPendingVariableChanges(); + } + + @Override + public void setStyleName(String style) { + super.setStyleName(style + " v-treetable"); + } + +} diff --git a/src/com/vaadin/ui/TreeTable.java b/src/com/vaadin/ui/TreeTable.java new file mode 100644 index 0000000000..20fc8e44d7 --- /dev/null +++ b/src/com/vaadin/ui/TreeTable.java @@ -0,0 +1,579 @@ +/* +@ITMillApache2LicenseForJavaFiles@ + */ + +package com.vaadin.ui; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import com.google.gwt.user.client.ui.Tree; +import com.vaadin.data.Container; +import com.vaadin.data.Container.Hierarchical; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.util.ContainerHierarchicalWrapper; +import com.vaadin.data.util.HierarchicalContainer; +import com.vaadin.terminal.PaintException; +import com.vaadin.terminal.PaintTarget; +import com.vaadin.terminal.Resource; +import com.vaadin.terminal.gwt.client.ui.VTreeTable; +import com.vaadin.ui.treetable.Collapsible; +import com.vaadin.ui.treetable.HierarchicalContainerOrderedWrapper; + +/** + * TreeTable extends the {@link Table} component so that it can also visualize a + * hierarchy of its Items in a similar manner that {@link Tree} does. The tree + * hierarchy is always displayed in the first actual column of the TreeTable. + * <p> + * The TreeTable supports the usual {@link Table} features like lazy loading, so + * it should be no problem to display lots of items at once. Only required rows + * and some cache rows are sent to the client. + * <p> + * TreeTable supports standard {@link Hierarchical} container interfaces, but + * also a more fine tuned version - {@link Collapsible}. A container + * implementing the {@link Collapsible} interface stores the collapsed/expanded + * state internally and can this way scale better on the server side than with + * standard Hierarchical implementations. Developer must however note that + * {@link Collapsible} containers can not be shared among several users as they + * share UI state in the container. + */ +@SuppressWarnings({ "serial" }) +@ClientWidget(VTreeTable.class) +public class TreeTable extends Table implements Hierarchical { + + private interface ContainerStrategy extends Serializable { + public int size(); + + public boolean isNodeOpen(Object itemId); + + public int getDepth(Object itemId); + + public void toggleChildVisibility(Object itemId); + + public Object getIdByIndex(int index); + + public int indexOfId(Object id); + + public Object nextItemId(Object itemId); + + public Object lastItemId(); + + public Object prevItemId(Object itemId); + + public boolean isLastId(Object itemId); + + public Collection<?> getItemIds(); + + public void containerItemSetChange(ItemSetChangeEvent event); + } + + private abstract class AbstractStrategy implements ContainerStrategy { + + /** + * Consider adding getDepth to {@link Collapsible}, might help + * scalability with some container implementations. + */ + public int getDepth(Object itemId) { + int depth = 0; + Hierarchical hierarchicalContainer = getContainerDataSource(); + while (!hierarchicalContainer.isRoot(itemId)) { + depth++; + itemId = hierarchicalContainer.getParent(itemId); + } + return depth; + } + + public void containerItemSetChange(ItemSetChangeEvent event) { + } + + } + + /** + * This strategy is used if current container implements {@link Collapsible} + * . + * + * open-collapsed logic diverted to container, otherwise use default + * implementations. + */ + private class CollapsibleStrategy extends AbstractStrategy { + + private Collapsible c() { + return (Collapsible) getContainerDataSource(); + } + + public void toggleChildVisibility(Object itemId) { + c().setCollapsed(itemId, !c().isCollapsed(itemId)); + } + + public boolean isNodeOpen(Object itemId) { + return !c().isCollapsed(itemId); + } + + public int size() { + return TreeTable.super.size(); + } + + public Object getIdByIndex(int index) { + return TreeTable.super.getIdByIndex(index); + } + + public int indexOfId(Object id) { + return TreeTable.super.indexOfId(id); + } + + public boolean isLastId(Object itemId) { + // using the default impl + return TreeTable.super.isLastId(itemId); + } + + public Object lastItemId() { + // using the default impl + return TreeTable.super.lastItemId(); + } + + public Object nextItemId(Object itemId) { + return TreeTable.super.nextItemId(itemId); + } + + public Object prevItemId(Object itemId) { + return TreeTable.super.prevItemId(itemId); + } + + public Collection<?> getItemIds() { + return TreeTable.super.getItemIds(); + } + + } + + /** + * Strategy for Hierarchical but not Collapsible container like + * {@link HierarchicalContainer}. + * + * Store collapsed/open states internally, fool Table to use preorder when + * accessing items from container via Ordered/Indexed methods. + */ + private class HierarchicalStrategy extends AbstractStrategy { + + private final HashSet<Object> openItems = new HashSet<Object>(); + + public boolean isNodeOpen(Object itemId) { + return openItems.contains(itemId); + } + + public int size() { + return getPreOrder().size(); + } + + public Collection<Object> getItemIds() { + return Collections.unmodifiableCollection(getPreOrder()); + } + + public boolean isLastId(Object itemId) { + return itemId.equals(lastItemId()); + } + + public Object lastItemId() { + if (getPreOrder().size() > 0) { + return getPreOrder().get(getPreOrder().size() - 1); + } else { + return null; + } + } + + public Object nextItemId(Object itemId) { + int indexOf = getPreOrder().indexOf(itemId); + if (indexOf == -1) { + return null; + } + indexOf++; + if (indexOf == getPreOrder().size()) { + return null; + } else { + return getPreOrder().get(indexOf); + } + } + + public Object prevItemId(Object itemId) { + int indexOf = getPreOrder().indexOf(itemId); + indexOf--; + if (indexOf < 0) { + return null; + } else { + return getPreOrder().get(indexOf); + } + } + + public void toggleChildVisibility(Object itemId) { + boolean removed = openItems.remove(itemId); + if (!removed) { + openItems.add(itemId); + } + clearPreorderCache(); + } + + private void clearPreorderCache() { + preOrder = null; // clear preorder cache + } + + List<Object> preOrder; + + /** + * Preorder of ids currently visible + * + * @return + */ + private List<Object> getPreOrder() { + if (preOrder == null) { + preOrder = new ArrayList<Object>(); + Collection<?> rootItemIds = getContainerDataSource() + .rootItemIds(); + for (Object id : rootItemIds) { + preOrder.add(id); + addVisibleChildTree(id); + } + } + return preOrder; + } + + private void addVisibleChildTree(Object id) { + if (isNodeOpen(id)) { + Collection<?> children = getContainerDataSource().getChildren( + id); + if (children != null) { + for (Object childId : children) { + preOrder.add(childId); + addVisibleChildTree(childId); + } + } + } + + } + + public int indexOfId(Object id) { + return getPreOrder().indexOf(id); + } + + public Object getIdByIndex(int index) { + return getPreOrder().get(index); + } + + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + // preorder becomes invalid on sort, item additions etc. + clearPreorderCache(); + super.containerItemSetChange(event); + } + + } + + /** + * Creates an empty TreeTable with a default container. + */ + public TreeTable() { + super(null, new HierarchicalContainer()); + } + + /** + * Creates an empty TreeTable with a default container. + * + * @param caption + * the caption for the TreeTable + */ + public TreeTable(String caption) { + this(); + setCaption(caption); + } + + /** + * Creates a TreeTable instance with given captions and data source. + * + * @param caption + * the caption for the component + * @param dataSource + * the dataSource that is used to list items in the component + */ + public TreeTable(String caption, Container dataSource) { + super(caption, dataSource); + } + + private ContainerStrategy cStrategy; + private Object focusedRowId = null; + private Object hierarchyColumnId; + + private ContainerStrategy getContainerStrategy() { + if (cStrategy == null) { + if (getContainerDataSource() instanceof Collapsible) { + cStrategy = new CollapsibleStrategy(); + } else { + cStrategy = new HierarchicalStrategy(); + } + } + return cStrategy; + } + + @Override + protected void paintRowAttributes(PaintTarget target, Object itemId) + throws PaintException { + super.paintRowAttributes(target, itemId); + target.addAttribute("depth", getContainerStrategy().getDepth(itemId)); + if (getContainerDataSource().areChildrenAllowed(itemId)) { + target.addAttribute("ca", true); + target.addAttribute("open", + getContainerStrategy().isNodeOpen(itemId)); + } + } + + @Override + protected void paintRowIcon(PaintTarget target, Object[][] cells, + int indexInRowbuffer) throws PaintException { + // always paint if present (in parent only if row headers visible) + if (getRowHeaderMode() == ROW_HEADER_MODE_HIDDEN) { + Resource itemIcon = getItemIcon(cells[CELL_ITEMID][indexInRowbuffer]); + if (itemIcon != null) { + target.addAttribute("icon", itemIcon); + } + } else if (cells[CELL_ICON][indexInRowbuffer] != null) { + target.addAttribute("icon", + (Resource) cells[CELL_ICON][indexInRowbuffer]); + } + } + + @Override + public void changeVariables(Object source, Map<String, Object> variables) { + super.changeVariables(source, variables); + + if (variables.containsKey("toggleCollapsed")) { + String object = (String) variables.get("toggleCollapsed"); + Object itemId = itemIdMapper.get(object); + toggleChildVisibility(itemId); + if (variables.containsKey("selectCollapsed")) { + // ensure collapsed is selected unless opened with selection + // head + if (isSelectable()) { + select(itemId); + } + } + } else if (variables.containsKey("focusParent")) { + String key = (String) variables.get("focusParent"); + Object refId = itemIdMapper.get(key); + Object itemId = getParent(refId); + focusParent(itemId); + } + } + + private void focusParent(Object itemId) { + boolean inView = false; + Object inPageId = getCurrentPageFirstItemId(); + for (int i = 0; inPageId != null && i < getPageLength(); i++) { + if (inPageId.equals(itemId)) { + inView = true; + break; + } + inPageId = nextItemId(inPageId); + i++; + } + if (!inView) { + setCurrentPageFirstItemId(itemId); + } + if (isSelectable()) { + if (isMultiSelect()) { + setValue(Collections.singleton(itemId)); + } else { + setValue(itemId); + } + } else { + // just instruct the VTreeTable to set focus the row (not to select) + setFocusedRow(itemId); + } + } + + private void setFocusedRow(Object itemId) { + focusedRowId = itemId; + requestRepaint(); + } + + @Override + public void paintContent(PaintTarget target) throws PaintException { + if (focusedRowId != null) { + target.addAttribute("focusedRow", itemIdMapper.key(focusedRowId)); + focusedRowId = null; + } + if (hierarchyColumnId != null) { + Object[] visibleColumns2 = getVisibleColumns(); + for (int i = 0; i < visibleColumns2.length; i++) { + Object object = visibleColumns2[i]; + if (hierarchyColumnId.equals(object)) { + target.addAttribute( + VTreeTable.ATTRIBUTE_HIERARCHY_COLUMN_INDEX, i); + break; + } + } + } + super.paintContent(target); + } + + private void toggleChildVisibility(Object itemId) { + getContainerStrategy().toggleChildVisibility(itemId); + // ensure that page still has first item in page, ignore buffer refresh + // (forced in this method) + setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex()); + + requestRepaint(); + } + + @Override + public int size() { + return getContainerStrategy().size(); + } + + @Override + public Hierarchical getContainerDataSource() { + return (Hierarchical) super.getContainerDataSource(); + } + + @Override + public void setContainerDataSource(Container newDataSource) { + cStrategy = null; + if (!(newDataSource instanceof Hierarchical)) { + newDataSource = new ContainerHierarchicalWrapper(newDataSource); + } + + if (!(newDataSource instanceof Ordered)) { + newDataSource = new HierarchicalContainerOrderedWrapper( + (Hierarchical) newDataSource); + } + + super.setContainerDataSource(newDataSource); + } + + @Override + public void containerItemSetChange( + com.vaadin.data.Container.ItemSetChangeEvent event) { + getContainerStrategy().containerItemSetChange(event); + super.containerItemSetChange(event); + } + + @Override + protected Object getIdByIndex(int index) { + return getContainerStrategy().getIdByIndex(index); + } + + @Override + protected int indexOfId(Object itemId) { + return getContainerStrategy().indexOfId(itemId); + } + + @Override + public Object nextItemId(Object itemId) { + return getContainerStrategy().nextItemId(itemId); + } + + @Override + public Object lastItemId() { + return getContainerStrategy().lastItemId(); + } + + @Override + public Object prevItemId(Object itemId) { + return getContainerStrategy().prevItemId(itemId); + } + + @Override + public boolean isLastId(Object itemId) { + return getContainerStrategy().isLastId(itemId); + } + + @Override + public Collection<?> getItemIds() { + return getContainerStrategy().getItemIds(); + } + + public boolean areChildrenAllowed(Object itemId) { + return getContainerDataSource().areChildrenAllowed(itemId); + } + + public Collection<?> getChildren(Object itemId) { + return getContainerDataSource().getChildren(itemId); + } + + public Object getParent(Object itemId) { + return getContainerDataSource().getParent(itemId); + } + + public boolean hasChildren(Object itemId) { + return getContainerDataSource().hasChildren(itemId); + } + + public boolean isRoot(Object itemId) { + return getContainerDataSource().isRoot(itemId); + } + + public Collection<?> rootItemIds() { + return getContainerDataSource().rootItemIds(); + } + + public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) + throws UnsupportedOperationException { + return getContainerDataSource().setChildrenAllowed(itemId, + areChildrenAllowed); + } + + public boolean setParent(Object itemId, Object newParentId) + throws UnsupportedOperationException { + return getContainerDataSource().setParent(itemId, newParentId); + } + + /** + * Sets the Item specified by given identifier collapsed or expanded. If the + * Item is collapsed, its children is not displayed in for the user. + * + * @param itemId + * the identifier of the Item + * @param collapsed + * true if the Item should be collapsed, false if expanded + */ + public void setCollapsed(Object itemId, boolean collapsed) { + if (isCollapsed(itemId) != collapsed) { + toggleChildVisibility(itemId); + } + } + + /** + * Checks if Item with given identifier is collapsed in the UI. + * + * <p> + * + * @param itemId + * the identifier of the checked Item + * @return true if the Item with given id is collapsed + * @see Collapsible#isCollapsed(Object) + */ + public boolean isCollapsed(Object itemId) { + return !getContainerStrategy().isNodeOpen(itemId); + } + + /** + * Explicitly sets the column in which the TreeTable visualizes the + * hierarchy. If hierarchyColumnId is not set, the hierarchy is visualized + * in the first visible column. + * + * @param hierarchyColumnId + */ + public void setHierarchyColumn(Object hierarchyColumnId) { + this.hierarchyColumnId = hierarchyColumnId; + } + + /** + * @return the identifier of column into which the hierarchy will be + * visualized or null if the column is not explicitly defined. + */ + public Object getHierarchyColumnId() { + return hierarchyColumnId; + } + +} diff --git a/src/com/vaadin/ui/treetable/Collapsible.java b/src/com/vaadin/ui/treetable/Collapsible.java new file mode 100644 index 0000000000..649687716c --- /dev/null +++ b/src/com/vaadin/ui/treetable/Collapsible.java @@ -0,0 +1,66 @@ +package com.vaadin.ui.treetable; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Hierarchical; +import com.vaadin.data.Container.Ordered; +import com.vaadin.data.Item; + +/** + * Container needed by large lazy loading hierarchies displayed in TreeTable. + * <p> + * Container of this type gets notified when a subtree is opened/closed in a + * component displaying its content. This allows container to lazy load subtrees + * and release memory when a sub-tree is no longer displayed. + * <p> + * Methods from {@link Container.Ordered} (and from {@linkContainer.Indexed} if + * implemented) are expected to work as in "preorder" of the currently visible + * hierarchy. This means for example that the return value of size method + * changes when subtree is collapsed/expanded. In other words items in collapsed + * sub trees should be "ignored" by container when the container is accessed + * with methods introduced in {@link Container.Ordered} or + * {@linkContainer.Indexed}. From the accessors point of view, items in + * collapsed subtrees don't exist. + * <p> + * + */ +public interface Collapsible extends Hierarchical, Ordered { + + /** + * <p> + * Collapsing the {@link Item} indicated by <code>itemId</code> hides all + * children, and their respective children, from the {@link Container}. + * </p> + * + * <p> + * If called on a leaf {@link Item}, this method does nothing. + * </p> + * + * @param itemId + * the identifier of the collapsed {@link Item} + * @param collapsed + * <code>true</code> if you want to collapse the children below + * this {@link Item}. <code>false</code> if you want to + * uncollapse the children. + */ + public void setCollapsed(Object itemId, boolean collapsed); + + /** + * <p> + * Checks whether the {@link Item}, identified by <code>itemId</code> is + * collapsed or not. + * </p> + * + * <p> + * If an {@link Item} is "collapsed" its children are not included in + * methods used to list Items in this container. + * </p> + * + * @param itemId + * The {@link Item}'s identifier that is to be checked. + * @return <code>true</code> iff the {@link Item} identified by + * <code>itemId</code> is currently collapsed, otherwise + * <code>false</code>. + */ + public boolean isCollapsed(Object itemId); + +} diff --git a/src/com/vaadin/ui/treetable/HierarchicalContainerOrderedWrapper.java b/src/com/vaadin/ui/treetable/HierarchicalContainerOrderedWrapper.java new file mode 100644 index 0000000000..0c00027f26 --- /dev/null +++ b/src/com/vaadin/ui/treetable/HierarchicalContainerOrderedWrapper.java @@ -0,0 +1,58 @@ +package com.vaadin.ui.treetable; + +import java.util.Collection; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Hierarchical; +import com.vaadin.data.util.ContainerOrderedWrapper; + +@SuppressWarnings({ "serial", "unchecked" }) +/** + * Helper for TreeTable. Does the same thing as ContainerOrderedWrapper + * to fit into table but retains Hierarchical feature. + */ +public class HierarchicalContainerOrderedWrapper extends + ContainerOrderedWrapper implements Hierarchical { + + private Hierarchical hierarchical; + + public HierarchicalContainerOrderedWrapper(Hierarchical toBeWrapped) { + super(toBeWrapped); + hierarchical = toBeWrapped; + } + + public boolean areChildrenAllowed(Object itemId) { + return hierarchical.areChildrenAllowed(itemId); + } + + public Collection<?> getChildren(Object itemId) { + return hierarchical.getChildren(itemId); + } + + public Object getParent(Object itemId) { + return hierarchical.getParent(itemId); + } + + public boolean hasChildren(Object itemId) { + return hierarchical.hasChildren(itemId); + } + + public boolean isRoot(Object itemId) { + return hierarchical.isRoot(itemId); + } + + public Collection<?> rootItemIds() { + return hierarchical.rootItemIds(); + } + + public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) + throws UnsupportedOperationException { + return hierarchical.setChildrenAllowed(itemId, areChildrenAllowed); + } + + public boolean setParent(Object itemId, Object newParentId) + throws UnsupportedOperationException { + return hierarchical.setParent(itemId, newParentId); + } + +} diff --git a/tests/src/com/vaadin/tests/components/table/Tables.java b/tests/src/com/vaadin/tests/components/table/Tables.java index 4a0b914534..a128879e21 100644 --- a/tests/src/com/vaadin/tests/components/table/Tables.java +++ b/tests/src/com/vaadin/tests/components/table/Tables.java @@ -18,8 +18,8 @@ import com.vaadin.ui.Table.FooterClickListener; import com.vaadin.ui.Table.HeaderClickEvent;
import com.vaadin.ui.Table.HeaderClickListener;
-public class Tables extends AbstractSelectTestCase<Table> implements
- ItemClickListener, HeaderClickListener, FooterClickListener,
+public class Tables<T extends Table> extends AbstractSelectTestCase<T>
+ implements ItemClickListener, HeaderClickListener, FooterClickListener,
ColumnResizeListener {
protected static final String CATEGORY_ROWS = "Rows";
@@ -28,12 +28,12 @@ public class Tables extends AbstractSelectTestCase<Table> implements private static final String CATEGORY_VISIBLE_COLUMNS = "Visible columns";
@Override
- protected Class<Table> getTestClass() {
- return Table.class;
+ protected Class<T> getTestClass() {
+ return (Class) Table.class;
}
/* COMMANDS */
- private Command<Table, Boolean> visibleColumnCommand = new Command<Table, Boolean>() {
+ private Command<T, Boolean> visibleColumnCommand = new Command<T, Boolean>() {
public void execute(Table c, Boolean visible, Object propertyId) {
List<Object> visibleColumns = new ArrayList<Object>(Arrays.asList(c
.getVisibleColumns()));
@@ -50,7 +50,7 @@ public class Tables extends AbstractSelectTestCase<Table> implements }
};
- protected Command<Table, Boolean> columnResizeListenerCommand = new Command<Table, Boolean>() {
+ protected Command<T, Boolean> columnResizeListenerCommand = new Command<T, Boolean>() {
public void execute(Table c, Boolean value, Object data) {
if (value) {
@@ -61,9 +61,9 @@ public class Tables extends AbstractSelectTestCase<Table> implements }
};
- protected Command<Table, Boolean> headerClickListenerCommand = new Command<Table, Boolean>() {
+ protected Command<T, Boolean> headerClickListenerCommand = new Command<T, Boolean>() {
- public void execute(Table c, Boolean value, Object data) {
+ public void execute(T c, Boolean value, Object data) {
if (value) {
c.addListener((HeaderClickListener) Tables.this);
} else {
@@ -72,7 +72,7 @@ public class Tables extends AbstractSelectTestCase<Table> implements }
};
- protected Command<Table, Boolean> footerClickListenerCommand = new Command<Table, Boolean>() {
+ protected Command<T, Boolean> footerClickListenerCommand = new Command<T, Boolean>() {
public void execute(Table c, Boolean value, Object data) {
if (value) {
@@ -83,7 +83,7 @@ public class Tables extends AbstractSelectTestCase<Table> implements }
};
- protected Command<Table, Integer> rowHeaderModeCommand = new Command<Table, Integer>() {
+ protected Command<T, Integer> rowHeaderModeCommand = new Command<T, Integer>() {
public void execute(Table c, Integer value, Object data) {
if (value == Table.ROW_HEADER_MODE_PROPERTY) {
@@ -93,7 +93,7 @@ public class Tables extends AbstractSelectTestCase<Table> implements }
};
- protected Command<Table, String> footerTextCommand = new Command<Table, String>() {
+ protected Command<T, String> footerTextCommand = new Command<T, String>() {
public void execute(Table c, String value, Object data) {
for (Object propertyId : c.getContainerPropertyIds()) {
@@ -111,18 +111,18 @@ public class Tables extends AbstractSelectTestCase<Table> implements }
- protected Command<Table, Alignments> columnAlignmentCommand = new Command<Table, Alignments>() {
+ protected Command<T, Alignments> columnAlignmentCommand = new Command<T, Alignments>() {
- public void execute(Table c, Alignments value, Object data) {
+ public void execute(T c, Alignments value, Object data) {
// TODO
// for (Object propertyId : c.getContainerPropertyIds()) {
// }
}
};
- private Command<Table, ContextMenu> contextMenuCommand = new Command<Table, ContextMenu>() {
+ private Command<T, ContextMenu> contextMenuCommand = new Command<T, ContextMenu>() {
- public void execute(Table c, final ContextMenu value, Object data) {
+ public void execute(T c, final ContextMenu value, Object data) {
c.removeAllActionHandlers();
if (value != null) {
c.addActionHandler(new Handler() {
@@ -207,7 +207,7 @@ public class Tables extends AbstractSelectTestCase<Table> implements private void createColumnReorderingAllowedCheckbox(String category) {
createBooleanAction("Column reordering allowed", category, true,
- new Command<Table, Boolean>() {
+ new Command<T, Boolean>() {
public void execute(Table c, Boolean value, Object data) {
c.setColumnReorderingAllowed(value);
}
@@ -216,8 +216,8 @@ public class Tables extends AbstractSelectTestCase<Table> implements private void createColumnCollapsingAllowedCheckbox(String category) {
createBooleanAction("Column collapsing allowed", category, true,
- new Command<Table, Boolean>() {
- public void execute(Table c, Boolean value, Object data) {
+ new Command<T, Boolean>() {
+ public void execute(T c, Boolean value, Object data) {
c.setColumnCollapsingAllowed(value);
}
});
@@ -266,8 +266,8 @@ public class Tables extends AbstractSelectTestCase<Table> implements options.put("Header {id} - every second", "Header {id}");
createSelectAction("Texts in header", category, options, "None",
- new Command<Table, String>() {
- public void execute(Table c, String value, Object data) {
+ new Command<T, String>() {
+ public void execute(T c, String value, Object data) {
int nr = 0;
for (Object propertyId : c.getContainerPropertyIds()) {
nr++;
@@ -326,9 +326,9 @@ public class Tables extends AbstractSelectTestCase<Table> implements protected void createFooterVisibilityCheckbox(String category) {
createBooleanAction("Footer visible", category, true,
- new Command<Table, Boolean>() {
+ new Command<T, Boolean>() {
- public void execute(Table c, Boolean value, Object data) {
+ public void execute(T c, Boolean value, Object data) {
c.setFooterVisible(value);
}
});
@@ -343,9 +343,9 @@ public class Tables extends AbstractSelectTestCase<Table> implements options.put("Hidden", Table.COLUMN_HEADER_MODE_HIDDEN);
createSelectAction("Header mode", category, options,
- "Explicit defaults id", new Command<Table, Integer>() {
+ "Explicit defaults id", new Command<T, Integer>() {
- public void execute(Table c, Integer value, Object data) {
+ public void execute(T c, Integer value, Object data) {
c.setColumnHeaderMode(value);
}
@@ -361,7 +361,7 @@ public class Tables extends AbstractSelectTestCase<Table> implements options.put("50", 50);
createSelectAction("PageLength", category, options, "10",
- new Command<Table, Integer>() {
+ new Command<T, Integer>() {
public void execute(Table t, Integer value, Object data) {
t.setPageLength(value);
@@ -381,7 +381,7 @@ public class Tables extends AbstractSelectTestCase<Table> implements options.put("Multi - ctrl/shift", SelectMode.MULTI);
createSelectAction("Selection Mode", category, options,
- "Multi - ctrl/shift", new Command<Table, SelectMode>() {
+ "Multi - ctrl/shift", new Command<T, SelectMode>() {
public void execute(Table t, SelectMode value, Object data) {
switch (value) {
diff --git a/tests/src/com/vaadin/tests/components/treetable/TreeTables.java b/tests/src/com/vaadin/tests/components/treetable/TreeTables.java new file mode 100644 index 0000000000..523869e838 --- /dev/null +++ b/tests/src/com/vaadin/tests/components/treetable/TreeTables.java @@ -0,0 +1,291 @@ +package com.vaadin.tests.components.treetable;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.Container.Hierarchical;
+import com.vaadin.data.util.HierarchicalContainer;
+import com.vaadin.tests.components.table.Tables;
+import com.vaadin.ui.Table.CellStyleGenerator;
+import com.vaadin.ui.Tree.CollapseEvent;
+import com.vaadin.ui.Tree.CollapseListener;
+import com.vaadin.ui.Tree.ExpandEvent;
+import com.vaadin.ui.TreeTable;
+
+public class TreeTables extends Tables<TreeTable> implements CollapseListener {
+
+ @Override
+ protected Class<TreeTable> getTestClass() {
+ return TreeTable.class;
+ }
+
+ private int rootItemIds = 3;
+ private CellStyleGenerator rootGreenSecondLevelRed = new com.vaadin.ui.Table.CellStyleGenerator() {
+
+ public String getStyle(Object itemId, Object propertyId) {
+ if (propertyId != null) {
+ return null;
+ }
+
+ Hierarchical c = getComponent().getContainerDataSource();
+ if (c.isRoot(itemId)) {
+ return "green";
+ }
+
+ Object parent = c.getParent(itemId);
+ if (!c.isRoot(parent)) {
+ return "red";
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "Root green, second level red";
+ }
+
+ };
+
+ private CellStyleGenerator evenItemsBold = new CellStyleGenerator() {
+
+ public String getStyle(Object itemId, Object propertyId) {
+ if (propertyId != null) {
+ return null;
+ }
+
+ Hierarchical c = getComponent().getContainerDataSource();
+ int idx = 0;
+
+ for (Iterator<?> i = c.getItemIds().iterator(); i.hasNext();) {
+ Object id = i.next();
+ if (id == itemId) {
+ if (idx % 2 == 1) {
+ return "bold";
+ } else {
+ return null;
+ }
+ }
+
+ idx++;
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "Even items bold";
+ };
+
+ };
+
+ @Override
+ protected void createActions() {
+ super.createActions();
+
+ // Causes container changes so doing this first..
+ createRootItemSelectAction(CATEGORY_DATA_SOURCE);
+
+ createExpandCollapseActions(CATEGORY_FEATURES);
+ createSelectionModeSelect(CATEGORY_SELECTION);
+ createChildrenAllowedAction(CATEGORY_DATA_SOURCE);
+
+ createListeners(CATEGORY_LISTENERS);
+ // createItemStyleGenerator(CATEGORY_FEATURES);
+
+ // TODO: DropHandler
+ // TODO: DragMode
+ // TODO: ActionHandler
+
+ }
+
+ @Override
+ protected Container createContainer(int properties, int items) {
+ return createHierarchicalContainer(properties, items, rootItemIds);
+ }
+
+ private void createListeners(String category) {
+ // createBooleanAction("Expand listener", category, false,
+ // expandListenerCommand);
+ // createBooleanAction("Collapse listener", category, false,
+ // collapseListenerCommand);
+ createBooleanAction("Item click listener", category, false,
+ itemClickListenerCommand);
+
+ }
+
+ private Container.Hierarchical createHierarchicalContainer(int properties,
+ int items, int roots) {
+ Container.Hierarchical c = new HierarchicalContainer();
+
+ populateContainer(c, properties, items);
+
+ if (items <= roots) {
+ return c;
+ }
+
+ // "roots" roots, each with
+ // "firstLevel" children, two with no children (one with childAllowed,
+ // one without)
+ // ("firstLevel"-2)*"secondLevel" children ("secondLevel"/2 with
+ // childAllowed, "secondLevel"/2 without)
+
+ // N*M+N*(M-2)*C = items
+ // items=N(M+MC-2C)
+
+ // Using secondLevel=firstLevel/2 =>
+ // items = roots*(firstLevel+firstLevel*firstLevel/2-2*firstLevel/2)
+ // =roots*(firstLevel+firstLevel^2/2-firstLevel)
+ // = roots*firstLevel^2/2
+ // => firstLevel = sqrt(items/roots*2)
+
+ int firstLevel = (int) Math.ceil(Math.sqrt(items / roots * 2.0));
+ int secondLevel = firstLevel / 2;
+
+ while (roots * (1 + 2 + (firstLevel - 2) * secondLevel) < items) {
+ // Increase something so we get enough items
+ secondLevel++;
+ }
+
+ List<Object> itemIds = new ArrayList<Object>(c.getItemIds());
+
+ int nextItemId = roots;
+ for (int rootIndex = 0; rootIndex < roots; rootIndex++) {
+ // roots use items 0..roots-1
+ Object rootItemId = itemIds.get(rootIndex);
+
+ // force roots to be roots even though they automatically should be
+ c.setParent(rootItemId, null);
+
+ for (int firstLevelIndex = 0; firstLevelIndex < firstLevel; firstLevelIndex++) {
+ if (nextItemId >= items) {
+ break;
+ }
+ Object firstLevelItemId = itemIds.get(nextItemId++);
+ c.setParent(firstLevelItemId, rootItemId);
+
+ if (firstLevelIndex < 2) {
+ continue;
+ }
+
+ // firstLevelChildren 2.. have child nodes
+ for (int secondLevelIndex = 0; secondLevelIndex < secondLevel; secondLevelIndex++) {
+ if (nextItemId >= items) {
+ break;
+ }
+
+ Object secondLevelItemId = itemIds.get(nextItemId++);
+ c.setParent(secondLevelItemId, firstLevelItemId);
+ }
+ }
+ }
+
+ return c;
+ }
+
+ private void createRootItemSelectAction(String category) {
+ LinkedHashMap<String, Integer> options = new LinkedHashMap<String, Integer>();
+ for (int i = 1; i <= 10; i++) {
+ options.put(String.valueOf(i), i);
+ }
+ options.put("20", 20);
+ options.put("50", 50);
+ options.put("100", 100);
+
+ createSelectAction("Number of root items", category, options, "3",
+ rootItemIdsCommand);
+ }
+
+ private void createExpandCollapseActions(String category) {
+ LinkedHashMap<String, Object> options = new LinkedHashMap<String, Object>();
+
+ for (Object id : getComponent().getItemIds()) {
+ options.put(id.toString(), id);
+ }
+ createMultiClickAction("Expand", category, options, expandItemCommand,
+ null);
+ // createMultiClickAction("Expand recursively", category, options,
+ // expandItemRecursivelyCommand, null);
+ createMultiClickAction("Collapse", category, options,
+ collapseItemCommand, null);
+
+ }
+
+ private void createChildrenAllowedAction(String category) {
+ LinkedHashMap<String, Object> options = new LinkedHashMap<String, Object>();
+
+ for (Object id : getComponent().getItemIds()) {
+ options.put(id.toString(), id);
+ }
+ createMultiToggleAction("Children allowed", category, options,
+ setChildrenAllowedCommand, true);
+
+ }
+
+ /*
+ * COMMANDS
+ */
+ private Command<TreeTable, Integer> rootItemIdsCommand = new Command<TreeTable, Integer>() {
+
+ public void execute(TreeTable c, Integer value, Object data) {
+ rootItemIds = value;
+ updateContainer();
+ }
+ };
+
+ private Command<TreeTable, Object> expandItemCommand = new Command<TreeTable, Object>() {
+
+ public void execute(TreeTable c, Object itemId, Object data) {
+ c.setCollapsed(itemId, false);
+ }
+ };
+
+ private Command<TreeTable, Object> collapseItemCommand = new Command<TreeTable, Object>() {
+
+ public void execute(TreeTable c, Object itemId, Object data) {
+ c.setCollapsed(itemId, true);
+ }
+ };
+
+ private Command<TreeTable, Boolean> setChildrenAllowedCommand = new Command<TreeTable, Boolean>() {
+
+ public void execute(TreeTable c, Boolean areChildrenAllowed,
+ Object itemId) {
+ c.setChildrenAllowed(itemId, areChildrenAllowed);
+ }
+ };
+
+ // private Command<TreeTable, Boolean> expandListenerCommand = new
+ // Command<TreeTable, Boolean>() {
+ // public void execute(TreeTable c, Boolean value, Object data) {
+ // if (value) {
+ // c.addListener((ExpandListener) TreeTables.this);
+ // } else {
+ // c.removeListener((ExpandListener) TreeTables.this);
+ // }
+ // }
+ // };
+ //
+ // private Command<TreeTable, Boolean> collapseListenerCommand = new
+ // Command<TreeTable, Boolean>() {
+ // public void execute(TreeTable c, Boolean value, Object data) {
+ // if (value) {
+ // c.addListener((CollapseListener) TreeTables.this);
+ // } else {
+ // c.removeListener((CollapseListener) TreeTables.this);
+ // }
+ // }
+ // };
+
+ public void nodeCollapse(CollapseEvent event) {
+ log(event.getClass().getSimpleName() + ": " + event.getItemId());
+ }
+
+ public void nodeExpand(ExpandEvent event) {
+ log(event.getClass().getSimpleName() + ": " + event.getItemId());
+ }
+}
|