You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2019/06/20 12:30:48 UTC

[myfaces-tobago] branch tobago-4.x updated: TOBAGO-1994: TreeListbox is not working correctly

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch tobago-4.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/tobago-4.x by this push:
     new 24c1493  TOBAGO-1994: TreeListbox is not working correctly
     new 49ce0ed  Merge remote-tracking branch 'origin/tobago-4.x' into tobago-4.x
24c1493 is described below

commit 24c1493737639509fbb52ed74337bb326261d3b1
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Wed May 22 11:19:40 2019 +0200

    TOBAGO-1994: TreeListbox is not working correctly
---
 .../renderkit/renderer/TreeListboxRenderer.java    | 12 +++-
 .../renderkit/renderer/TreeNodeRenderer.java       | 21 ++++--
 .../myfaces/tobago/internal/util/StringUtils.java  |  2 +-
 .../apache/myfaces/tobago/model/SelectedState.java | 27 +++++++-
 .../tobago/renderkit/html/DataAttributes.java      |  2 +-
 tobago-core/src/main/resources/scss/_tobago.scss   | 16 ++++-
 .../tobago/model/SelectedStateUnitTest.java        | 76 ++++++++++++++++++++++
 .../myfaces/tobago/example/demo/Release.java       |  1 +
 .../tobago/example/demo/TreeListboxController.java | 68 +++++++++++++++++++
 .../tobago/example/demo/TreeSelectController.java  | 31 +--------
 .../myfaces/tobago/example/demo/TreeUtils.java     | 41 ++++++++++++
 .../090-tree/04-listbox/tree-listbox.xhtml         | 10 ++-
 .../90000-attic/treeListbox/treeListbox.xhtml      | 35 ----------
 .../tobago-bootstrap/_version/css/bootstrap.css    | 10 +++
 .../_version/css/bootstrap.css.map                 |  2 +-
 .../_version/css/bootstrap.min.css                 |  2 +-
 .../_version/css/bootstrap.min.css.map             |  2 +-
 .../tobago-bootstrap/_version/css/bootstrap.css    | 10 +++
 .../_version/css/bootstrap.css.map                 |  2 +-
 .../_version/css/bootstrap.min.css                 |  2 +-
 .../_version/css/bootstrap.min.css.map             |  2 +-
 .../tobago-bootstrap/_version/css/bootstrap.css    | 10 +++
 .../_version/css/bootstrap.css.map                 |  2 +-
 .../_version/css/bootstrap.min.css                 |  2 +-
 .../_version/css/bootstrap.min.css.map             |  2 +-
 .../tobago-bootstrap/_version/css/bootstrap.css    | 10 +++
 .../_version/css/bootstrap.css.map                 |  2 +-
 .../_version/css/bootstrap.min.css                 |  2 +-
 .../_version/css/bootstrap.min.css.map             |  2 +-
 .../tobago-bootstrap/_version/css/bootstrap.css    | 10 +++
 .../_version/css/bootstrap.css.map                 |  2 +-
 .../_version/css/bootstrap.min.css                 |  2 +-
 .../_version/css/bootstrap.min.css.map             |  2 +-
 .../tobago-bootstrap/_version/js/tobago-tree.js    |  4 +-
 34 files changed, 325 insertions(+), 101 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeListboxRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeListboxRenderer.java
index ec2ca3d..4f4caa9 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeListboxRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeListboxRenderer.java
@@ -20,6 +20,7 @@
 package org.apache.myfaces.tobago.internal.renderkit.renderer;
 
 import org.apache.myfaces.tobago.context.Markup;
+import org.apache.myfaces.tobago.internal.component.AbstractUIData;
 import org.apache.myfaces.tobago.internal.component.AbstractUITree;
 import org.apache.myfaces.tobago.internal.component.AbstractUITreeLabel;
 import org.apache.myfaces.tobago.internal.component.AbstractUITreeListbox;
@@ -27,6 +28,7 @@ import org.apache.myfaces.tobago.internal.component.AbstractUITreeNode;
 import org.apache.myfaces.tobago.internal.component.AbstractUITreeSelect;
 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.internal.util.JsonUtils;
+import org.apache.myfaces.tobago.internal.util.RenderUtils;
 import org.apache.myfaces.tobago.renderkit.RendererBase;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
@@ -45,6 +47,12 @@ import java.util.List;
 public class TreeListboxRenderer extends RendererBase {
 
   @Override
+  public void decode(final FacesContext facesContext, final UIComponent component) {
+    final AbstractUITree tree = (AbstractUITree) component;
+    RenderUtils.decodedStateOfTreeData(facesContext, tree);
+  }
+
+  @Override
   public void encodeChildren(final FacesContext context, final UIComponent component) throws IOException {
     // will be rendered in encodeEnd()
   }
@@ -90,8 +98,8 @@ public class TreeListboxRenderer extends RendererBase {
     if (tree.getSelectable().isSupportedByTreeListbox()) {
       writer.startElement(HtmlElements.INPUT);
       writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN);
-      writer.writeNameAttribute(clientId + AbstractUITree.SELECT_STATE);
-      writer.writeIdAttribute(clientId + AbstractUITree.SELECT_STATE);
+      writer.writeNameAttribute(clientId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_SELECTED);
+      writer.writeIdAttribute(clientId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_SELECTED);
       writer.writeAttribute(HtmlAttributes.VALUE, ";", false);
       writer.writeAttribute(DataAttributes.SELECTION_MODE, tree.getSelectable().name(), false);
       writer.endElement(HtmlElements.INPUT);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java
index a856662..3eb1ead 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeNodeRenderer.java
@@ -30,6 +30,8 @@ import org.apache.myfaces.tobago.internal.component.AbstractUITreeSelect;
 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.internal.util.JsonUtils;
 import org.apache.myfaces.tobago.model.Selectable;
+import org.apache.myfaces.tobago.model.SelectedState;
+import org.apache.myfaces.tobago.model.TreePath;
 import org.apache.myfaces.tobago.renderkit.RendererBase;
 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
@@ -67,12 +69,13 @@ public class TreeNodeRenderer extends RendererBase {
       final String clientId = data.getClientId(facesContext);
       final String nodeStateId = node.nodeStateId(facesContext);
       final Map<String, String> requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
-      final String id = node.getClientId(facesContext);
+      final String nodeId = node.getClientId(facesContext);
       final boolean folder = node.isFolder();
 
       // expand state
       if (folder) {
-        final boolean expanded = Boolean.parseBoolean(requestParameterMap.get(id + "-expanded"));
+        final boolean expanded = Boolean.parseBoolean(requestParameterMap.get(
+            nodeId + ComponentUtils.SUB_SEPARATOR + AbstractUITree.SUFFIX_EXPANDED));
 /* XXX check
       if (node.isExpanded() != expanded) {
         new TreeExpansionEvent(node, node.isExpanded(), expanded).queue();
@@ -82,8 +85,9 @@ public class TreeNodeRenderer extends RendererBase {
 
       // select
       if (data.getSelectable() != Selectable.none) { // selection
-        final String selected = requestParameterMap.get(clientId + AbstractUITree.SELECT_STATE);
-        final String searchString = ";" + node.getClientId(facesContext) + ";";
+        final String selected = requestParameterMap.get(
+            clientId + ComponentUtils.SUB_SEPARATOR + AbstractUIData.SUFFIX_SELECTED);
+        final String searchString = ";" + nodeId + ";";
         final AbstractUITreeSelect treeSelect = ComponentUtils.findDescendant(node, AbstractUITreeSelect.class);
         if (treeSelect != null) {
           treeSelect.setSubmittedValue(selected.contains(searchString));
@@ -119,12 +123,14 @@ public class TreeNodeRenderer extends RendererBase {
     final boolean visible = data.isRowVisible();
     final boolean folder = node.isFolder();
     Markup markup = Markup.NULL;
-    if (data instanceof AbstractUITree && data.getSelectedState().isSelected(node.getPath())) {
+    final TreePath path = node.getPath();
+    final SelectedState selectedState = data.getSelectedState();
+    if (data instanceof AbstractUITree && selectedState.isSelected(path)) {
       markup = markup.add(Markup.SELECTED);
     }
     if (folder) {
       markup = markup.add(Markup.FOLDER);
-      if (data.getExpandedState().isExpanded(node.getPath())) {
+      if (data.getExpandedState().isExpanded(path)) {
         markup = markup.add(Markup.EXPANDED);
       }
     }
@@ -138,7 +144,8 @@ public class TreeNodeRenderer extends RendererBase {
       writer.writeAttribute(HtmlAttributes.VALUE, clientId, true);
       writer.writeIdAttribute(clientId);
       writer.writeAttribute(DataAttributes.MARKUP, JsonUtils.encode(markup), false);
-      writer.writeAttribute(HtmlAttributes.SELECTED, folder);
+      writer.writeAttribute(HtmlAttributes.SELECTED, selectedState.isAncestorOfSelected(path));
+      writer.writeAttribute(DataAttributes.ROW_INDEX, data.getRowIndex());
     } else {
       writer.startElement(HtmlElements.DIV);
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java
index a677b1b..30ad0e8 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java
@@ -31,7 +31,7 @@ public final class StringUtils {
   }
 
   public static List<Integer> parseIntegerList(final String integerList) throws NumberFormatException {
-    return parseIntegerList(integerList, ", ");
+    return parseIntegerList(integerList, ", ;");
   }
 
   public static List<Integer> parseIntegerList(final String integerList, final String delimiters)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/SelectedState.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/SelectedState.java
index f1e4357..d01d97e 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/SelectedState.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/SelectedState.java
@@ -32,13 +32,30 @@ public class SelectedState implements Serializable {
   private Set<TreePath> selectedPaths = new HashSet<>();
 
   /**
-   * Checks if the given is selected.
+   * Checks if the given path is selected.
    */
   public boolean isSelected(final TreePath path) {
     return selectedPaths.contains(path);
   }
 
   /**
+   * Checks if the given path is an ancestor of a selected node.
+   */
+  public boolean isAncestorOfSelected(final TreePath ancestorPath) {
+    if (ancestorPath.isRoot()) {
+      return !selectedPaths.isEmpty();
+    }
+    for (TreePath selectedPath : selectedPaths) {
+      for (TreePath p = selectedPath; !p.isRoot(); p = p.getParent()) {
+        if (p.equals(ancestorPath)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
    * Select the given path.
    */
   public void select(final TreePath path) {
@@ -53,8 +70,7 @@ public class SelectedState implements Serializable {
   }
 
   /**
-   * Set the selected path and remove all prior selections.
-   * This is useful for "single selection" mode.
+   * Set the selected path and remove all prior selections. This is useful for "single selection" mode.
    */
   public void clearAndSelect(final TreePath path) {
     clear();
@@ -78,4 +94,9 @@ public class SelectedState implements Serializable {
       unselect(path);
     }
   }
+
+  @Override
+  public String toString() {
+    return selectedPaths.toString();
+  }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
index b78c536..7825257 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
@@ -147,7 +147,7 @@ public enum DataAttributes implements MarkupLanguageAttributes {
   ROW_ACTION("data-tobago-row-action"),
 
   /*
-   * Holds the index of the row in a sheet, if the sheed has a rowRendered attribute.
+   * Holds the index of the row in a sheet, if the sheet has a rowRendered attribute.
    */
   ROW_INDEX("data-tobago-row-index"),
 
diff --git a/tobago-core/src/main/resources/scss/_tobago.scss b/tobago-core/src/main/resources/scss/_tobago.scss
index cad5cb0..549cebb 100644
--- a/tobago-core/src/main/resources/scss/_tobago.scss
+++ b/tobago-core/src/main/resources/scss/_tobago.scss
@@ -1485,9 +1485,6 @@ th.tobago-sheet-headerCell-markup-filler > .tobago-sheet-header {
 .tobago-tree-expanded,
 .tobago-tree-selected,
 .tobago-treeLabel,
-.tobago-treeListbox,
-.tobago-treeListbox-level,
-.tobago-treeListbox-select,
 .tobago-treeSelect,
 .tobago-treeSelect-label {
 }
@@ -1519,6 +1516,19 @@ th.tobago-sheet-headerCell-markup-filler > .tobago-sheet-header {
   }
 }
 
+/* treeListbox ---------------------------------------------------------------------- */
+.tobago-treeListbox {
+}
+
+.tobago-treeListbox-level {
+  display: inline-block;
+  min-width: 10rem;
+}
+
+.tobago-treeListbox-select {
+  width: 100%;
+}
+
 /* textarea --------------------------------------------------------- */
 .tobago-textarea {
   &:disabled {
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/model/SelectedStateUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/model/SelectedStateUnitTest.java
new file mode 100644
index 0000000..18a2f51
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/model/SelectedStateUnitTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.model;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SelectedStateUnitTest {
+
+  @Test
+  public void testAncestorOfSelected() {
+    SelectedState state = new SelectedState();
+    state.select(new TreePath(0,0));
+    state.select(new TreePath(1,1,1));
+
+    Assert.assertTrue(state.isAncestorOfSelected(new TreePath()));
+    Assert.assertTrue(state.isAncestorOfSelected(new TreePath(0)));
+    Assert.assertTrue(state.isAncestorOfSelected(new TreePath(0,0)));
+    Assert.assertTrue(state.isAncestorOfSelected(new TreePath(1)));
+    Assert.assertTrue(state.isAncestorOfSelected(new TreePath(1,1)));
+    Assert.assertTrue(state.isAncestorOfSelected(new TreePath(1,1,1)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(2)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(0,1)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(1,0)));
+  }
+
+  @Test
+  public void testAncestorOfSelectedEmpty() {
+    SelectedState state = new SelectedState();
+
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath()));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(0)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(0,0)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(1)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(1,1)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(1,1,1)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(2)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(0,1)));
+    Assert.assertFalse(state.isAncestorOfSelected(new TreePath(1,0)));
+  }
+
+  @Test
+  public void testSelected() {
+    SelectedState state = new SelectedState();
+    state.select(new TreePath(0,0));
+    state.select(new TreePath(1,1,1));
+
+    Assert.assertFalse(state.isSelected(new TreePath()));
+    Assert.assertFalse(state.isSelected(new TreePath(0)));
+    Assert.assertTrue(state.isSelected(new TreePath(0,0)));
+    Assert.assertFalse(state.isSelected(new TreePath(1)));
+    Assert.assertFalse(state.isSelected(new TreePath(1,1)));
+    Assert.assertTrue(state.isSelected(new TreePath(1,1,1)));
+    Assert.assertFalse(state.isSelected(new TreePath(2)));
+    Assert.assertFalse(state.isSelected(new TreePath(0,1)));
+    Assert.assertFalse(state.isSelected(new TreePath(1,0)));
+  }
+
+}
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Release.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Release.java
index 892d057..8c49ff4 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Release.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Release.java
@@ -133,6 +133,7 @@ public enum Release {
   v4_3_2("12344394"),
   v4_4_0("12344541"),
   v4_4_1("12345061", true),
+  v4_4_2("12345562", false ,true),
 
   v5_0_0("12338729", false, true),
   v5_0_1("12344151", false, true),
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeListboxController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeListboxController.java
new file mode 100644
index 0000000..fb06d30
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeListboxController.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.example.demo;
+
+import org.apache.myfaces.tobago.model.TreeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.enterprise.context.SessionScoped;
+import javax.faces.context.FacesContext;
+import javax.inject.Named;
+import javax.swing.tree.DefaultMutableTreeNode;
+import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
+
+@SessionScoped
+@Named
+public class TreeListboxController implements Serializable {
+
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private DefaultMutableTreeNode sample;
+
+  private TreeState state;
+
+  public TreeListboxController() {
+    sample = CategoryTree.createSample();
+  }
+
+  public String submit() {
+    LOG.info("Selected: {}", state.getSelectedState());
+    return FacesContext.getCurrentInstance().getViewRoot().getViewId();
+  }
+
+  public DefaultMutableTreeNode getSample() {
+    return sample;
+  }
+
+  public TreeState getState() {
+    return state;
+  }
+
+  public void setState(TreeState state) {
+    this.state = state;
+  }
+
+//  public String getSelectedNodes() {
+//    return TreeUtils.getSelectedNodes(sample);
+//  }
+
+}
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeSelectController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeSelectController.java
index dca962c..9de037a 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeSelectController.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeSelectController.java
@@ -45,37 +45,10 @@ public class TreeSelectController implements Serializable {
 
   public void setSelectable(final String selectable) {
     this.selectable = selectable;
-    resetSelection(sample);
-  }
-
-  public void resetSelection(final DefaultMutableTreeNode node) {
-    final Node userObject = (Node) node.getUserObject();
-    userObject.setSelected(false);
-    for (int i = 0; i < node.getChildCount(); i++) {
-      final DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
-      resetSelection(child);
-    }
+    TreeUtils.resetSelection(sample);
   }
 
   public String getSelectedNodes() {
-    final StringBuilder stringBuilder = new StringBuilder();
-    buildSelectedNodesString(stringBuilder, sample);
-    if (stringBuilder.length() > 2) {
-      return stringBuilder.substring(2); // Remove ', '.
-    } else {
-      return "";
-    }
-  }
-
-  private void buildSelectedNodesString(final StringBuilder stringBuilder, final DefaultMutableTreeNode node) {
-    final Node userObject = (Node) node.getUserObject();
-    if (userObject.isSelected()) {
-      stringBuilder.append(", ");
-      stringBuilder.append(userObject.getName());
-    }
-    for (int i = 0; i < node.getChildCount(); i++) {
-      final DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
-      buildSelectedNodesString(stringBuilder, child);
-    }
+    return TreeUtils.getSelectedNodes(sample);
   }
 }
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeUtils.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeUtils.java
new file mode 100644
index 0000000..3401454
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/TreeUtils.java
@@ -0,0 +1,41 @@
+package org.apache.myfaces.tobago.example.demo;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+public class TreeUtils {
+
+  private TreeUtils() {
+  }
+
+  public static void resetSelection(final DefaultMutableTreeNode node) {
+    final Node userObject = (Node) node.getUserObject();
+    userObject.setSelected(false);
+    for (int i = 0; i < node.getChildCount(); i++) {
+      final DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
+      resetSelection(child);
+    }
+  }
+
+  public static String getSelectedNodes(final DefaultMutableTreeNode treeNode) {
+    final StringBuilder stringBuilder = new StringBuilder();
+    buildSelectedNodesString(stringBuilder, treeNode);
+    if (stringBuilder.length() > 2) {
+      return stringBuilder.substring(2); // Remove ', '.
+    } else {
+      return "";
+    }
+  }
+
+  private static void buildSelectedNodesString(final StringBuilder stringBuilder, final DefaultMutableTreeNode node) {
+    final Node userObject = (Node) node.getUserObject();
+    if (userObject.isSelected()) {
+      stringBuilder.append(", ");
+      stringBuilder.append(userObject.getName());
+    }
+    for (int i = 0; i < node.getChildCount(); i++) {
+      final DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
+      buildSelectedNodesString(stringBuilder, child);
+    }
+  }
+
+}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/tree-listbox.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/tree-listbox.xhtml
index 0f22085..ca65e3e 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/tree-listbox.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/tree-listbox.xhtml
@@ -28,12 +28,16 @@
            link="#{demoBundle.tagDocUrl}/#{info.stableVersion}/tld/tc/treeListbox.html"/>
 
   <tc:section label="Example">
-    <pre><code class="language-markup">&lt;tc:treeListbox value="\#{treeController.sample}" ...></code></pre>
-    <tc:treeListbox value="#{treeController.sample}" var="node">
+    <pre><code class="language-markup">&lt;tc:treeListbox value="\#{treeListboxController.sample}" ...></code></pre>
+    <tc:treeListbox value="#{treeListboxController.sample}" var="node" state="#{treeListboxController.state}">
       <tc:treeNode>
-        <tc:treeIndent/>
         <tc:treeLabel value="#{node.userObject.name}"/>
       </tc:treeNode>
     </tc:treeListbox>
+
+    <tc:button label="Submit" action="#{treeListboxController.submit}"/>
+
+    <tc:in readonly="true" label="Selection" tip="as set of tree pathes" value="#{treeListboxController.state.selectedState}"/>
+<!--    <tc:in readonly="true" label="Selection" value="#{treeListboxController.selectedNodes}"/>-->
   </tc:section>
 </ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/90000-attic/treeListbox/treeListbox.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/90000-attic/treeListbox/treeListbox.xhtml
deleted file mode 100644
index fb4720d..0000000
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/90000-attic/treeListbox/treeListbox.xhtml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
--->
-
-<!-- XXX This is an old page. Content might not be up to date. Needs to be refactored, or just deleted. -->
-<f:view
-    xmlns:tc="http://myfaces.apache.org/tobago/component"
-    xmlns:ui="http://java.sun.com/jsf/facelets"
-    xmlns:f="http://java.sun.com/jsf/core">
-
-  <tc:page>
-    <!-- <tc:gridLayoutConstraint width="600px" height="300px"/> -->
-
-    <tc:treeListbox id="tree" value="#{treeTestController.tree}" var="node">
-      <tc:treeNode id="template">
-        <tc:treeLabel value="#{node.userObject.name}"/>
-      </tc:treeNode>
-    </tc:treeListbox>
-
-  </tc:page>
-</f:view>
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css
index 2fc4e38..ec38967 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css
@@ -11493,6 +11493,16 @@ th.tobago-sheet-headerCell-markup-filler > .tobago-sheet-header {
   margin-left: 20rem;
 }
 
+/* treeListbox ---------------------------------------------------------------------- */
+.tobago-treeListbox-level {
+  display: inline-block;
+  min-width: 10rem;
+}
+
+.tobago-treeListbox-select {
+  width: 100%;
+}
+
 /* textarea --------------------------------------------------------- */
 .tobago-textarea:disabled {
   color: rgba(160, 160, 160, 0.5);
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css.map b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css.map
index 3e6b643..276a27f 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css.map
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.css.map
@@ -1 +1 @@
-{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
+{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css
index 0fbe35f..5d51fa8 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css
@@ -3,5 +3,5 @@
  * Copyright 2011-2019 The Bootstrap Authors
  * Copyright 2011-2019 Twitter, Inc.
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#529696;--secondary:#b2a76d;--success:#abf5ff;--info:#389c30;--warning:#ff00be;--danger:#ff00be;--light:#ffffff;--dark:#529696;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-s [...]
+ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#529696;--secondary:#b2a76d;--success:#abf5ff;--info:#389c30;--warning:#ff00be;--danger:#ff00be;--light:#ffffff;--dark:#529696;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-s [...]
 /*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css.map b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css.map
index e397931..2a5358e 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css.map
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/resources/META-INF/resources/tobago/charlotteville/tobago-bootstrap/_version/css/bootstrap.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixi [...]
\ No newline at end of file
+{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixi [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css
index 290c896..3424476 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css
+++ b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css
@@ -11524,6 +11524,16 @@ th.tobago-sheet-headerCell-markup-filler > .tobago-sheet-header {
   margin-left: 20rem;
 }
 
+/* treeListbox ---------------------------------------------------------------------- */
+.tobago-treeListbox-level {
+  display: inline-block;
+  min-width: 10rem;
+}
+
+.tobago-treeListbox-select {
+  width: 100%;
+}
+
 /* textarea --------------------------------------------------------- */
 .tobago-textarea:disabled {
   color: rgba(160, 160, 160, 0.5);
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css.map b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css.map
index 6f34cf9..ec35115 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css.map
+++ b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.css.map
@@ -1 +1 @@
-{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
+{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css
index 6a0c949..217da53 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css
+++ b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css
@@ -3,5 +3,5 @@
  * Copyright 2011-2019 The Bootstrap Authors
  * Copyright 2011-2019 Twitter, Inc.
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */@font-face{font-family:Amaranth;font-style:normal;font-weight:400;src:url(../fonts/Amaranth-Regular.otf) format("opentype")}@font-face{font-family:Amaranth;font-style:normal;font-weight:700;src:url(../fonts/Amaranth-Bold.otf) format("opentype")}@font-face{font-family:Amaranth;font-style:italic;src:url(../fonts/Amaranth-Italic.otf) format("opentype")}@font-face{font-family:Amaranth;font-style:italic;font-weight:700;src:url(../fonts/Amaranth-BoldItalic.otf) format("opentype")}.tobago-bo [...]
+ */@font-face{font-family:Amaranth;font-style:normal;font-weight:400;src:url(../fonts/Amaranth-Regular.otf) format("opentype")}@font-face{font-family:Amaranth;font-style:normal;font-weight:700;src:url(../fonts/Amaranth-Bold.otf) format("opentype")}@font-face{font-family:Amaranth;font-style:italic;src:url(../fonts/Amaranth-Italic.otf) format("opentype")}@font-face{font-family:Amaranth;font-style:italic;font-weight:700;src:url(../fonts/Amaranth-BoldItalic.otf) format("opentype")}.tobago-bo [...]
 /*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css.map b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css.map
index 540037f..b1e6b31 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css.map
+++ b/tobago-theme/tobago-theme-roxborough/src/main/resources/META-INF/resources/tobago/roxborough/tobago-bootstrap/_version/css/bootstrap.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoi [...]
\ No newline at end of file
+{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoi [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css
index 8cf6901..c90916f 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css
+++ b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css
@@ -11527,6 +11527,16 @@ th.tobago-sheet-headerCell-markup-filler > .tobago-sheet-header {
   margin-left: 20rem;
 }
 
+/* treeListbox ---------------------------------------------------------------------- */
+.tobago-treeListbox-level {
+  display: inline-block;
+  min-width: 10rem;
+}
+
+.tobago-treeListbox-select {
+  width: 100%;
+}
+
 /* textarea --------------------------------------------------------- */
 .tobago-textarea:disabled {
   color: rgba(73, 80, 87, 0.5);
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css.map b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css.map
index 942d838..723909f 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css.map
+++ b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.css.map
@@ -1 +1 @@
-{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
+{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css
index f3b0708..b269484 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css
+++ b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css
@@ -3,5 +3,5 @@
  * Copyright 2011-2019 The Bootstrap Authors
  * Copyright 2011-2019 Twitter, Inc.
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-s [...]
+ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-s [...]
 /*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css.map b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css.map
index e35de08..aa0da4d 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css.map
+++ b/tobago-theme/tobago-theme-scarborough/src/main/resources/META-INF/resources/tobago/scarborough/tobago-bootstrap/_version/css/bootstrap.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixi [...]
\ No newline at end of file
+{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixi [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css
index ffd0f1c..c64b1f9 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css
+++ b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css
@@ -11241,6 +11241,16 @@ th.tobago-sheet-headerCell-markup-filler > .tobago-sheet-header {
   margin-left: 20rem;
 }
 
+/* treeListbox ---------------------------------------------------------------------- */
+.tobago-treeListbox-level {
+  display: inline-block;
+  min-width: 10rem;
+}
+
+.tobago-treeListbox-select {
+  width: 100%;
+}
+
 /* textarea --------------------------------------------------------- */
 .tobago-textarea:disabled {
   color: rgba(73, 80, 87, 0.5);
diff --git a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css.map b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css.map
index dbee45d..7efa8c9 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css.map
+++ b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.css.map
@@ -1 +1 @@
-{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-f [...]
\ No newline at end of file
+{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-f [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css
index a5fae40..51a70db 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css
+++ b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css
@@ -3,5 +3,5 @@
  * Copyright 2011-2019 The Bootstrap Authors
  * Copyright 2011-2019 Twitter, Inc.
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#185722;--secondary:#d7d7d7;--success:#1da332;--info:#5bc0de;--warning:#f0ad4e;--danger:#d30040;--light:#f7f7f7;--dark:#323232;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:verdana, [...]
+ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#185722;--secondary:#d7d7d7;--success:#1da332;--info:#5bc0de;--warning:#f0ad4e;--danger:#d30040;--light:#f7f7f7;--dark:#323232;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:verdana, [...]
 /*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css.map b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css.map
index ec6d0a8..a9420e9 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css.map
+++ b/tobago-theme/tobago-theme-speyside/src/main/resources/META-INF/resources/tobago/speyside/tobago-bootstrap/_version/css/bootstrap.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-framework.scss","../../scss/_ta [...]
\ No newline at end of file
+{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-framework.scss","../../scss/_ta [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css
index 7eb63d4..b752e2d 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css
@@ -11493,6 +11493,16 @@ th.tobago-sheet-headerCell-markup-filler > .tobago-sheet-header {
   margin-left: 20rem;
 }
 
+/* treeListbox ---------------------------------------------------------------------- */
+.tobago-treeListbox-level {
+  display: inline-block;
+  min-width: 10rem;
+}
+
+.tobago-treeListbox-select {
+  width: 100%;
+}
+
 /* textarea --------------------------------------------------------- */
 .tobago-textarea:disabled {
   color: rgba(73, 80, 87, 0.5);
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css.map b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css.map
index 670cc08..953979e 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css.map
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.css.map
@@ -1 +1 @@
-{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
+{"version":3,"sources":["bootstrap.css","../../scss/bootstrap.scss","../../scss/_custom.scss","../../scss/_root.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/vendor/_rfs.scss","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_brea [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css
index 38bc025..a4b2fa6 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css
@@ -3,5 +3,5 @@
  * Copyright 2011-2019 The Bootstrap Authors
  * Copyright 2011-2019 Twitter, Inc.
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-s [...]
+ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-s [...]
 /*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css.map b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css.map
index 42869b6..8b66dd9 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css.map
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/css/bootstrap.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixi [...]
\ No newline at end of file
+{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixi [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-tree.js b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-tree.js
index ba0bba9..c56bf02 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-tree.js
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-tree.js
@@ -307,10 +307,10 @@ Tobago.TreeListbox.onChange = function() {
 
 Tobago.TreeListbox.setSelected = function(listbox) {
   var hidden = listbox.closest(".tobago-treeListbox").children("[data-tobago-selection-mode]");
-  if (hidden.length == 1){
+  if (hidden.length === 1){
     var selectedValue = ";";
     listbox.children("option:selected").each(function() {
-      selectedValue += jQuery(this).attr("id") + ";";
+      selectedValue += jQuery(this).data("tobago-row-index") + ";";
     });
     hidden.val(selectedValue);
   }