--- /dev/null
+.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
* 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;
* 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;
--- /dev/null
+/*
+@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");
+ }
+
+}
--- /dev/null
+/*
+@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;
+ }
+
+}
--- /dev/null
+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);
+
+}
--- /dev/null
+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);
+ }
+
+}
import com.vaadin.ui.Table.HeaderClickEvent;\r
import com.vaadin.ui.Table.HeaderClickListener;\r
\r
-public class Tables extends AbstractSelectTestCase<Table> implements\r
- ItemClickListener, HeaderClickListener, FooterClickListener,\r
+public class Tables<T extends Table> extends AbstractSelectTestCase<T>\r
+ implements ItemClickListener, HeaderClickListener, FooterClickListener,\r
ColumnResizeListener {\r
\r
protected static final String CATEGORY_ROWS = "Rows";\r
private static final String CATEGORY_VISIBLE_COLUMNS = "Visible columns";\r
\r
@Override\r
- protected Class<Table> getTestClass() {\r
- return Table.class;\r
+ protected Class<T> getTestClass() {\r
+ return (Class) Table.class;\r
}\r
\r
/* COMMANDS */\r
- private Command<Table, Boolean> visibleColumnCommand = new Command<Table, Boolean>() {\r
+ private Command<T, Boolean> visibleColumnCommand = new Command<T, Boolean>() {\r
public void execute(Table c, Boolean visible, Object propertyId) {\r
List<Object> visibleColumns = new ArrayList<Object>(Arrays.asList(c\r
.getVisibleColumns()));\r
}\r
};\r
\r
- protected Command<Table, Boolean> columnResizeListenerCommand = new Command<Table, Boolean>() {\r
+ protected Command<T, Boolean> columnResizeListenerCommand = new Command<T, Boolean>() {\r
\r
public void execute(Table c, Boolean value, Object data) {\r
if (value) {\r
}\r
};\r
\r
- protected Command<Table, Boolean> headerClickListenerCommand = new Command<Table, Boolean>() {\r
+ protected Command<T, Boolean> headerClickListenerCommand = new Command<T, Boolean>() {\r
\r
- public void execute(Table c, Boolean value, Object data) {\r
+ public void execute(T c, Boolean value, Object data) {\r
if (value) {\r
c.addListener((HeaderClickListener) Tables.this);\r
} else {\r
}\r
};\r
\r
- protected Command<Table, Boolean> footerClickListenerCommand = new Command<Table, Boolean>() {\r
+ protected Command<T, Boolean> footerClickListenerCommand = new Command<T, Boolean>() {\r
\r
public void execute(Table c, Boolean value, Object data) {\r
if (value) {\r
}\r
};\r
\r
- protected Command<Table, Integer> rowHeaderModeCommand = new Command<Table, Integer>() {\r
+ protected Command<T, Integer> rowHeaderModeCommand = new Command<T, Integer>() {\r
\r
public void execute(Table c, Integer value, Object data) {\r
if (value == Table.ROW_HEADER_MODE_PROPERTY) {\r
}\r
};\r
\r
- protected Command<Table, String> footerTextCommand = new Command<Table, String>() {\r
+ protected Command<T, String> footerTextCommand = new Command<T, String>() {\r
\r
public void execute(Table c, String value, Object data) {\r
for (Object propertyId : c.getContainerPropertyIds()) {\r
\r
}\r
\r
- protected Command<Table, Alignments> columnAlignmentCommand = new Command<Table, Alignments>() {\r
+ protected Command<T, Alignments> columnAlignmentCommand = new Command<T, Alignments>() {\r
\r
- public void execute(Table c, Alignments value, Object data) {\r
+ public void execute(T c, Alignments value, Object data) {\r
// TODO\r
// for (Object propertyId : c.getContainerPropertyIds()) {\r
// }\r
}\r
};\r
\r
- private Command<Table, ContextMenu> contextMenuCommand = new Command<Table, ContextMenu>() {\r
+ private Command<T, ContextMenu> contextMenuCommand = new Command<T, ContextMenu>() {\r
\r
- public void execute(Table c, final ContextMenu value, Object data) {\r
+ public void execute(T c, final ContextMenu value, Object data) {\r
c.removeAllActionHandlers();\r
if (value != null) {\r
c.addActionHandler(new Handler() {\r
\r
private void createColumnReorderingAllowedCheckbox(String category) {\r
createBooleanAction("Column reordering allowed", category, true,\r
- new Command<Table, Boolean>() {\r
+ new Command<T, Boolean>() {\r
public void execute(Table c, Boolean value, Object data) {\r
c.setColumnReorderingAllowed(value);\r
}\r
\r
private void createColumnCollapsingAllowedCheckbox(String category) {\r
createBooleanAction("Column collapsing allowed", category, true,\r
- new Command<Table, Boolean>() {\r
- public void execute(Table c, Boolean value, Object data) {\r
+ new Command<T, Boolean>() {\r
+ public void execute(T c, Boolean value, Object data) {\r
c.setColumnCollapsingAllowed(value);\r
}\r
});\r
options.put("Header {id} - every second", "Header {id}");\r
\r
createSelectAction("Texts in header", category, options, "None",\r
- new Command<Table, String>() {\r
- public void execute(Table c, String value, Object data) {\r
+ new Command<T, String>() {\r
+ public void execute(T c, String value, Object data) {\r
int nr = 0;\r
for (Object propertyId : c.getContainerPropertyIds()) {\r
nr++;\r
\r
protected void createFooterVisibilityCheckbox(String category) {\r
createBooleanAction("Footer visible", category, true,\r
- new Command<Table, Boolean>() {\r
+ new Command<T, Boolean>() {\r
\r
- public void execute(Table c, Boolean value, Object data) {\r
+ public void execute(T c, Boolean value, Object data) {\r
c.setFooterVisible(value);\r
}\r
});\r
options.put("Hidden", Table.COLUMN_HEADER_MODE_HIDDEN);\r
\r
createSelectAction("Header mode", category, options,\r
- "Explicit defaults id", new Command<Table, Integer>() {\r
+ "Explicit defaults id", new Command<T, Integer>() {\r
\r
- public void execute(Table c, Integer value, Object data) {\r
+ public void execute(T c, Integer value, Object data) {\r
c.setColumnHeaderMode(value);\r
\r
}\r
options.put("50", 50);\r
\r
createSelectAction("PageLength", category, options, "10",\r
- new Command<Table, Integer>() {\r
+ new Command<T, Integer>() {\r
\r
public void execute(Table t, Integer value, Object data) {\r
t.setPageLength(value);\r
options.put("Multi - ctrl/shift", SelectMode.MULTI);\r
\r
createSelectAction("Selection Mode", category, options,\r
- "Multi - ctrl/shift", new Command<Table, SelectMode>() {\r
+ "Multi - ctrl/shift", new Command<T, SelectMode>() {\r
\r
public void execute(Table t, SelectMode value, Object data) {\r
switch (value) {\r
--- /dev/null
+package com.vaadin.tests.components.treetable;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.LinkedHashMap;\r
+import java.util.List;\r
+\r
+import com.vaadin.data.Container;\r
+import com.vaadin.data.Container.Hierarchical;\r
+import com.vaadin.data.util.HierarchicalContainer;\r
+import com.vaadin.tests.components.table.Tables;\r
+import com.vaadin.ui.Table.CellStyleGenerator;\r
+import com.vaadin.ui.Tree.CollapseEvent;\r
+import com.vaadin.ui.Tree.CollapseListener;\r
+import com.vaadin.ui.Tree.ExpandEvent;\r
+import com.vaadin.ui.TreeTable;\r
+\r
+public class TreeTables extends Tables<TreeTable> implements CollapseListener {\r
+\r
+ @Override\r
+ protected Class<TreeTable> getTestClass() {\r
+ return TreeTable.class;\r
+ }\r
+\r
+ private int rootItemIds = 3;\r
+ private CellStyleGenerator rootGreenSecondLevelRed = new com.vaadin.ui.Table.CellStyleGenerator() {\r
+\r
+ public String getStyle(Object itemId, Object propertyId) {\r
+ if (propertyId != null) {\r
+ return null;\r
+ }\r
+\r
+ Hierarchical c = getComponent().getContainerDataSource();\r
+ if (c.isRoot(itemId)) {\r
+ return "green";\r
+ }\r
+\r
+ Object parent = c.getParent(itemId);\r
+ if (!c.isRoot(parent)) {\r
+ return "red";\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "Root green, second level red";\r
+ }\r
+\r
+ };\r
+\r
+ private CellStyleGenerator evenItemsBold = new CellStyleGenerator() {\r
+\r
+ public String getStyle(Object itemId, Object propertyId) {\r
+ if (propertyId != null) {\r
+ return null;\r
+ }\r
+\r
+ Hierarchical c = getComponent().getContainerDataSource();\r
+ int idx = 0;\r
+\r
+ for (Iterator<?> i = c.getItemIds().iterator(); i.hasNext();) {\r
+ Object id = i.next();\r
+ if (id == itemId) {\r
+ if (idx % 2 == 1) {\r
+ return "bold";\r
+ } else {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ idx++;\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return "Even items bold";\r
+ };\r
+\r
+ };\r
+\r
+ @Override\r
+ protected void createActions() {\r
+ super.createActions();\r
+\r
+ // Causes container changes so doing this first..\r
+ createRootItemSelectAction(CATEGORY_DATA_SOURCE);\r
+\r
+ createExpandCollapseActions(CATEGORY_FEATURES);\r
+ createSelectionModeSelect(CATEGORY_SELECTION);\r
+ createChildrenAllowedAction(CATEGORY_DATA_SOURCE);\r
+\r
+ createListeners(CATEGORY_LISTENERS);\r
+ // createItemStyleGenerator(CATEGORY_FEATURES);\r
+\r
+ // TODO: DropHandler\r
+ // TODO: DragMode\r
+ // TODO: ActionHandler\r
+\r
+ }\r
+\r
+ @Override\r
+ protected Container createContainer(int properties, int items) {\r
+ return createHierarchicalContainer(properties, items, rootItemIds);\r
+ }\r
+\r
+ private void createListeners(String category) {\r
+ // createBooleanAction("Expand listener", category, false,\r
+ // expandListenerCommand);\r
+ // createBooleanAction("Collapse listener", category, false,\r
+ // collapseListenerCommand);\r
+ createBooleanAction("Item click listener", category, false,\r
+ itemClickListenerCommand);\r
+\r
+ }\r
+\r
+ private Container.Hierarchical createHierarchicalContainer(int properties,\r
+ int items, int roots) {\r
+ Container.Hierarchical c = new HierarchicalContainer();\r
+\r
+ populateContainer(c, properties, items);\r
+\r
+ if (items <= roots) {\r
+ return c;\r
+ }\r
+\r
+ // "roots" roots, each with\r
+ // "firstLevel" children, two with no children (one with childAllowed,\r
+ // one without)\r
+ // ("firstLevel"-2)*"secondLevel" children ("secondLevel"/2 with\r
+ // childAllowed, "secondLevel"/2 without)\r
+\r
+ // N*M+N*(M-2)*C = items\r
+ // items=N(M+MC-2C)\r
+\r
+ // Using secondLevel=firstLevel/2 =>\r
+ // items = roots*(firstLevel+firstLevel*firstLevel/2-2*firstLevel/2)\r
+ // =roots*(firstLevel+firstLevel^2/2-firstLevel)\r
+ // = roots*firstLevel^2/2\r
+ // => firstLevel = sqrt(items/roots*2)\r
+\r
+ int firstLevel = (int) Math.ceil(Math.sqrt(items / roots * 2.0));\r
+ int secondLevel = firstLevel / 2;\r
+\r
+ while (roots * (1 + 2 + (firstLevel - 2) * secondLevel) < items) {\r
+ // Increase something so we get enough items\r
+ secondLevel++;\r
+ }\r
+\r
+ List<Object> itemIds = new ArrayList<Object>(c.getItemIds());\r
+\r
+ int nextItemId = roots;\r
+ for (int rootIndex = 0; rootIndex < roots; rootIndex++) {\r
+ // roots use items 0..roots-1\r
+ Object rootItemId = itemIds.get(rootIndex);\r
+\r
+ // force roots to be roots even though they automatically should be\r
+ c.setParent(rootItemId, null);\r
+\r
+ for (int firstLevelIndex = 0; firstLevelIndex < firstLevel; firstLevelIndex++) {\r
+ if (nextItemId >= items) {\r
+ break;\r
+ }\r
+ Object firstLevelItemId = itemIds.get(nextItemId++);\r
+ c.setParent(firstLevelItemId, rootItemId);\r
+\r
+ if (firstLevelIndex < 2) {\r
+ continue;\r
+ }\r
+\r
+ // firstLevelChildren 2.. have child nodes\r
+ for (int secondLevelIndex = 0; secondLevelIndex < secondLevel; secondLevelIndex++) {\r
+ if (nextItemId >= items) {\r
+ break;\r
+ }\r
+\r
+ Object secondLevelItemId = itemIds.get(nextItemId++);\r
+ c.setParent(secondLevelItemId, firstLevelItemId);\r
+ }\r
+ }\r
+ }\r
+\r
+ return c;\r
+ }\r
+\r
+ private void createRootItemSelectAction(String category) {\r
+ LinkedHashMap<String, Integer> options = new LinkedHashMap<String, Integer>();\r
+ for (int i = 1; i <= 10; i++) {\r
+ options.put(String.valueOf(i), i);\r
+ }\r
+ options.put("20", 20);\r
+ options.put("50", 50);\r
+ options.put("100", 100);\r
+\r
+ createSelectAction("Number of root items", category, options, "3",\r
+ rootItemIdsCommand);\r
+ }\r
+\r
+ private void createExpandCollapseActions(String category) {\r
+ LinkedHashMap<String, Object> options = new LinkedHashMap<String, Object>();\r
+\r
+ for (Object id : getComponent().getItemIds()) {\r
+ options.put(id.toString(), id);\r
+ }\r
+ createMultiClickAction("Expand", category, options, expandItemCommand,\r
+ null);\r
+ // createMultiClickAction("Expand recursively", category, options,\r
+ // expandItemRecursivelyCommand, null);\r
+ createMultiClickAction("Collapse", category, options,\r
+ collapseItemCommand, null);\r
+\r
+ }\r
+\r
+ private void createChildrenAllowedAction(String category) {\r
+ LinkedHashMap<String, Object> options = new LinkedHashMap<String, Object>();\r
+\r
+ for (Object id : getComponent().getItemIds()) {\r
+ options.put(id.toString(), id);\r
+ }\r
+ createMultiToggleAction("Children allowed", category, options,\r
+ setChildrenAllowedCommand, true);\r
+\r
+ }\r
+\r
+ /*\r
+ * COMMANDS\r
+ */\r
+ private Command<TreeTable, Integer> rootItemIdsCommand = new Command<TreeTable, Integer>() {\r
+\r
+ public void execute(TreeTable c, Integer value, Object data) {\r
+ rootItemIds = value;\r
+ updateContainer();\r
+ }\r
+ };\r
+\r
+ private Command<TreeTable, Object> expandItemCommand = new Command<TreeTable, Object>() {\r
+\r
+ public void execute(TreeTable c, Object itemId, Object data) {\r
+ c.setCollapsed(itemId, false);\r
+ }\r
+ };\r
+\r
+ private Command<TreeTable, Object> collapseItemCommand = new Command<TreeTable, Object>() {\r
+\r
+ public void execute(TreeTable c, Object itemId, Object data) {\r
+ c.setCollapsed(itemId, true);\r
+ }\r
+ };\r
+\r
+ private Command<TreeTable, Boolean> setChildrenAllowedCommand = new Command<TreeTable, Boolean>() {\r
+\r
+ public void execute(TreeTable c, Boolean areChildrenAllowed,\r
+ Object itemId) {\r
+ c.setChildrenAllowed(itemId, areChildrenAllowed);\r
+ }\r
+ };\r
+\r
+ // private Command<TreeTable, Boolean> expandListenerCommand = new\r
+ // Command<TreeTable, Boolean>() {\r
+ // public void execute(TreeTable c, Boolean value, Object data) {\r
+ // if (value) {\r
+ // c.addListener((ExpandListener) TreeTables.this);\r
+ // } else {\r
+ // c.removeListener((ExpandListener) TreeTables.this);\r
+ // }\r
+ // }\r
+ // };\r
+ //\r
+ // private Command<TreeTable, Boolean> collapseListenerCommand = new\r
+ // Command<TreeTable, Boolean>() {\r
+ // public void execute(TreeTable c, Boolean value, Object data) {\r
+ // if (value) {\r
+ // c.addListener((CollapseListener) TreeTables.this);\r
+ // } else {\r
+ // c.removeListener((CollapseListener) TreeTables.this);\r
+ // }\r
+ // }\r
+ // };\r
+\r
+ public void nodeCollapse(CollapseEvent event) {\r
+ log(event.getClass().getSimpleName() + ": " + event.getItemId());\r
+ }\r
+\r
+ public void nodeExpand(ExpandEvent event) {\r
+ log(event.getClass().getSimpleName() + ": " + event.getItemId());\r
+ }\r
+}\r