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 2007/05/03 12:30:39 UTC

svn commit: r534781 - in /myfaces/tobago/trunk: example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/ example/sandbox/src/main/webapp/ sandbox/src/main/java/org/apache/myfaces/tobago/component/ sandbox/src/main/java/org/apache/myface...

Author: lofwyr
Date: Thu May  3 03:30:36 2007
New Revision: 534781

URL: http://svn.apache.org/viewvc?view=rev&rev=534781
Log:
TOBAGO-377: Improved Tree
rendering on the server for better performance, less javascript
move expand state from TreeState to arbitrary position in model

Added:
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/TreeModelBuilder.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/MixedTreeModel.java
Modified:
    myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Controller.java
    myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Node.java
    myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-menu.jsp
    myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-normal.jsp
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITree.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNode.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNodeData.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/TreeModel.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeRenderer.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTag.java
    myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTagDeclaration.java
    myfaces/tobago/trunk/sandbox/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/script/tobago-tree.js
    myfaces/tobago/trunk/sandbox/src/test/java/org/apache/myfaces/tobago/model/TreeModelUnitTest.java

Modified: myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Controller.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Controller.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Controller.java (original)
+++ myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Controller.java Thu May  3 03:30:36 2007
@@ -37,20 +37,31 @@
 
   public Controller() {
     // tree
-    tree = new DefaultMutableTreeNode(new Node("Category"));
-    tree.insert(new DefaultMutableTreeNode(new Node("Sports")), 0);
-    tree.insert(new DefaultMutableTreeNode(new Node("Movies")), 0);
-    DefaultMutableTreeNode music = new DefaultMutableTreeNode(new Node("Music"));
-    tree.insert(music, 0);
-    tree.insert(new DefaultMutableTreeNode(new Node("Games")), 0);
-    DefaultMutableTreeNode temp = new DefaultMutableTreeNode(new Node("Science"));
-    temp.insert(new DefaultMutableTreeNode(new Node("Geography", STRONG)), 0);
-    temp.insert(new DefaultMutableTreeNode(new Node("Mathematics", STRONG)), 0);
-    DefaultMutableTreeNode temp2 = new DefaultMutableTreeNode(new Node("Astronomy"));
-    temp2.insert(new DefaultMutableTreeNode(new Node("Education")), 0);
-    temp2.insert(new DefaultMutableTreeNode(new Node("Pictures")), 0);
-    temp.insert(temp2, 2);
-    tree.insert(temp, 2);
+    tree = new DefaultMutableTreeNode(new Node("1 Category"));
+    tree.add(new DefaultMutableTreeNode(new Node("1.1 Sports")));
+    tree.add(new DefaultMutableTreeNode(new Node("1.2 Movies")));
+    DefaultMutableTreeNode temp = new DefaultMutableTreeNode(new Node("1.3 Science"));
+    tree.add(temp);
+    DefaultMutableTreeNode music = new DefaultMutableTreeNode(new Node("1.4 Music"));
+    tree.add(music);
+    tree.add(new DefaultMutableTreeNode(new Node("1.5 Games")));
+    temp.add(new DefaultMutableTreeNode(new Node("1.3.1 Geography", STRONG)));
+    temp.add(new DefaultMutableTreeNode(new Node("1.3.2 Mathematics", STRONG)));
+    DefaultMutableTreeNode temp2 = new DefaultMutableTreeNode(new Node("1.3.3 Pictures"));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.1 Education")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.2 Family")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.3 Comercial")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.4 Summer")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.5 Winter")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.6 Red")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.7 Black")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.8 White")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.9 Good")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.10 Evil")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.11 Flower")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.12 Animal")));
+    temp2.add(new DefaultMutableTreeNode(new Node("1.3.3.13 Personal")));
+    temp.add(temp2);
 
     // state
 

Modified: myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Node.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Node.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Node.java (original)
+++ myfaces/tobago/trunk/example/sandbox/src/main/java/org/apache/myfaces/tobago/example/sandbox/Node.java Thu May  3 03:30:36 2007
@@ -26,6 +26,7 @@
 
   private String name;
   private String markup;
+  private boolean expanded;
 
   public Node(String name) {
     this.name = name;
@@ -55,6 +56,14 @@
 
   public void setMarkup(String markup) {
     this.markup = markup;
+  }
+
+  public boolean isExpanded() {
+    return expanded;
+  }
+
+  public void setExpanded(boolean expanded) {
+    this.expanded = expanded;
   }
 
   public String getTip() {

Modified: myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-menu.jsp
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-menu.jsp?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-menu.jsp (original)
+++ myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-menu.jsp Thu May  3 03:30:36 2007
@@ -25,7 +25,7 @@
   <tc:page label="Sandbox - Tree" id="page"
            width="500px" height="800px">
     <f:facet name="layout">
-      <tc:gridLayout margin="10px" rows="300px;*"/>
+      <tc:gridLayout margin="10px" rows="600px;*"/>
     </f:facet>
 
     <tcs:tree state="#{controller.state}" id="menu"
@@ -34,23 +34,26 @@
               showRootJunction="false"
               showRoot="true"
               mode="menu">
-      <tcs:treeNode label="Root" id="root">
+      <tcs:treeNode label="Root" id="root" expanded="true">
         <tcs:treeNodeData value="#{controller.tree}" var="node" id="data">
-          <tcs:treeNode label="#{node.userObject.name}" id="template"
+          <tcs:treeNode label="#{node.userObject.name}"
+                        id="template"
+                        expanded="#{node.userObject.expanded}"
                         markup="#{node.userObject.markup}"
                         tip="#{node.userObject.tip}"
-                        action="#{node.userObject.action}" value="#{node}"/>
+                        action="#{node.userObject.action}"
+                        value="#{node}"/>
         </tcs:treeNodeData>
-        <tcs:treeNode label="Action 1" action="#{controller.action1}" id="action1"/>
-        <tcs:treeNode label="Action 2" action="#{controller.action2}" id="action2"/>
-        <tcs:treeNode label="Action 3" action="#{controller.action3}" id="action3">
-          <tcs:treeNode label="On Click 1" onclick="alert('On Click 1');" id="click1"/>
-          <tcs:treeNode label="On Click 2" onclick="alert('On Click 2');" id="click2">
-            <tcs:treeNode label="On Click 3" onclick="alert('On Click 3');" id="click3"/>
+        <tcs:treeNode label="2 Action 1" action="#{controller.action1}" id="action1"/>
+        <tcs:treeNode label="3 Action 2" action="#{controller.action2}" id="action2"/>
+        <tcs:treeNode label="4 Action 3" action="#{controller.action3}" id="action3">
+          <tcs:treeNode label="4.1 On Click 1" onclick="alert('On Click 1');" id="click1"/>
+          <tcs:treeNode label="4.2 On Click 2" onclick="alert('On Click 2');" id="click2">
+            <tcs:treeNode label="4.2.1 On Click 3" onclick="alert('On Click 3');" id="click3"/>
           </tcs:treeNode>
         </tcs:treeNode>
-        <tcs:treeNode label="Link" link="http://myfaces.apache.org/tobago/" id="link" tip="Subnode Link"/>
-        <tcs:treeNode label="Target" action="#{controller.action2}" target="Target Window"/>
+        <tcs:treeNode label="5 Link" link="http://myfaces.apache.org/tobago/" id="link" tip="Subnode Link"/>
+        <tcs:treeNode label="6 Target" action="#{controller.action2}" target="Target Window"/>
       </tcs:treeNode>
     </tcs:tree>
 

Modified: myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-normal.jsp
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-normal.jsp?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-normal.jsp (original)
+++ myfaces/tobago/trunk/example/sandbox/src/main/webapp/tree-normal.jsp Thu May  3 03:30:36 2007
@@ -25,13 +25,18 @@
   <tc:page label="Sandbox - Tree" id="page"
            width="500px" height="800px">
     <f:facet name="layout">
-      <tc:gridLayout margin="10px" rows="300px;*"/>
+      <tc:gridLayout margin="10px" rows="600px;*"/>
     </f:facet>
 
-    <tcs:tree state="#{controller.state}" id="tree">
-      <tcs:treeNode label="Root">
+    <tcs:tree state="#{controller.state}" id="tree"
+        showIcons="true"
+        showJunctions="true"
+        showRoot="true"
+        showRootJunction="true"
+        >
+      <tcs:treeNode label="Root" expanded="true">
         <tcs:treeNodeData value="#{controller.tree}" var="node" id="data">
-          <tcs:treeNode label="#{node.userObject.name}" id="template"
+          <tcs:treeNode label="#{node.userObject.name}" id="template" expanded="#{node.userObject.expanded}"
                         markup="#{node.userObject.markup}"
                         tip="#{node.userObject.tip}"
                         action="#{node.userObject.action}" value="#{node}"/>

Added: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/TreeModelBuilder.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/TreeModelBuilder.java?view=auto&rev=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/TreeModelBuilder.java (added)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/TreeModelBuilder.java Thu May  3 03:30:36 2007
@@ -0,0 +1,17 @@
+package org.apache.myfaces.tobago.component;
+
+import org.apache.myfaces.tobago.model.MixedTreeModel;
+
+/**
+ * User: lofwyr
+ * Date: 23.04.2007 18:08:08
+ */
+public interface TreeModelBuilder {
+
+  void buildBegin(MixedTreeModel model);
+
+  void buildChildren(MixedTreeModel model);
+
+  void buildEnd(MixedTreeModel model);
+
+}

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITree.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITree.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITree.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITree.java Thu May  3 03:30:36 2007
@@ -27,6 +27,7 @@
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SHOW_ROOT_JUNCTION;
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STATE;
 import org.apache.myfaces.tobago.model.TreeState;
+import org.apache.myfaces.tobago.model.MixedTreeModel;
 import org.apache.myfaces.tobago.util.MessageFactory;
 
 import javax.faces.application.FacesMessage;
@@ -90,6 +91,7 @@
   private boolean showRootJunctionSet = false;
 
   private String mode;
+  private MixedTreeModel model;
 
   public UITree() {
     treeCommands = new Command[]{
@@ -178,6 +180,24 @@
 //     will be called from end.jsp
   }
 
+  public void encodeEnd(FacesContext context) throws IOException {
+    model = new MixedTreeModel();
+    
+    buildModel();
+    super.encodeEnd(context);
+  }
+
+  private void buildModel() {
+    for (Object child : getChildren()) {
+      if (child instanceof TreeModelBuilder) {
+        TreeModelBuilder builder = (TreeModelBuilder) child;
+        builder.buildBegin(model);
+        builder.buildChildren(model);
+        builder.buildEnd(model);
+      }
+    }
+  }
+
   public boolean getRendersChildren() {
     return true;
   }
@@ -392,5 +412,8 @@
       return command;
     }
   }
-}
 
+  public MixedTreeModel getModel() {
+    return model;
+  }
+}

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNode.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNode.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNode.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNode.java Thu May  3 03:30:36 2007
@@ -19,12 +19,13 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.tobago.model.MixedTreeModel;
 
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.faces.context.FacesContext;
 import javax.faces.component.UIComponent;
 
-public class UITreeNode extends UICommand implements SupportsMarkup {
+public class UITreeNode extends UICommand implements SupportsMarkup, TreeModelBuilder {
 
   private static final Log LOG = LogFactory.getLog(UITreeNode.class);
 
@@ -46,6 +47,25 @@
   @Override
   public boolean getRendersChildren() {
     return true;
+  }
+
+  public void buildBegin(MixedTreeModel model) {
+    model.beginBuildNode(this);
+  }
+
+  public void buildChildren(MixedTreeModel model) {
+    for (Object child : getChildren()) {
+      if (child instanceof TreeModelBuilder) {
+        TreeModelBuilder builder = (TreeModelBuilder) child;
+        builder.buildBegin(model);
+        builder.buildChildren(model);
+        builder.buildEnd(model);
+      }
+    }
+  }
+
+  public void buildEnd(MixedTreeModel model) {
+    model.endBuildNode(this);
   }
 
   @Override

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNodeData.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNodeData.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNodeData.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/component/UITreeNodeData.java Thu May  3 03:30:36 2007
@@ -21,7 +21,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.myfaces.tobago.model.TreeModel;
-import org.apache.myfaces.tobago.renderkit.RenderUtil;
+import org.apache.myfaces.tobago.model.MixedTreeModel;
 
 import javax.faces.component.NamingContainer;
 import javax.faces.context.FacesContext;
@@ -33,7 +33,7 @@
 import java.io.IOException;
 
 public class UITreeNodeData extends javax.faces.component.UIInput
-    implements NamingContainer {
+    implements NamingContainer, TreeModelBuilder {
 
   private static final Log LOG = LogFactory.getLog(UITreeNodeData.class);
 
@@ -57,8 +57,9 @@
 //    super.processDecodes(facesContext);
 //  }
 
+
   @Override
-  public void decode(FacesContext facesContext) {
+  public void processDecodes(FacesContext facesContext) {
 
     // todo: does treeModel should be stored in the state?
     // todo: what is, when the value has been changed since last rendering?
@@ -71,6 +72,11 @@
     }
   }
 
+  @Override
+  public void decode(FacesContext facesContext) {
+
+  }
+
   public String getPathIndex() {
     return pathIndex;
   }
@@ -101,6 +107,25 @@
     }
   }
 
+  public void buildBegin(MixedTreeModel model) {
+    model.beginBuildNodeData(this);
+  }
+
+  public void buildChildren(MixedTreeModel model) {
+    for (Object child : getChildren()) {
+      if (child instanceof TreeModelBuilder) {
+        TreeModelBuilder builder = (TreeModelBuilder) child;
+        builder.buildBegin(model);
+        builder.buildChildren(model);
+        builder.buildEnd(model);
+      }
+    }
+  }
+
+  public void buildEnd(MixedTreeModel model) {
+    model.endBuildNodeData(this);
+  }
+
   @Override
   public void encodeChildren(FacesContext context)
       throws IOException {
@@ -112,9 +137,14 @@
     // todo: does treeModel should be stored in the state?
     treeModel = new TreeModel((DefaultMutableTreeNode) getValue());
 
-    for (String pathIndex : treeModel.getPathIndexList()) {
-      setPathIndex(pathIndex);
-      RenderUtil.encode(facesContext, getTemplateComponent());
+    for (TreeModel.Tag pathIndex : treeModel.getDoublePathIndexList()) {
+      setPathIndex(pathIndex.getName());
+      if (pathIndex.isStart()) {
+        getTemplateComponent().encodeBegin(facesContext);
+      } else {
+        getTemplateComponent().encodeEnd(facesContext);
+      }
+//      RenderUtil.encode(facesContext, getTemplateComponent());
       setPathIndex(null);
     }
 

Added: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/MixedTreeModel.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/MixedTreeModel.java?view=auto&rev=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/MixedTreeModel.java (added)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/MixedTreeModel.java Thu May  3 03:30:36 2007
@@ -0,0 +1,132 @@
+package org.apache.myfaces.tobago.model;
+
+/*
+ * 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.
+ */
+
+import org.apache.myfaces.tobago.component.UITreeNode;
+import org.apache.myfaces.tobago.component.UITreeNodeData;
+import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LABEL;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import java.util.Stack;
+import java.util.List;
+import java.util.Collections;
+import java.util.ArrayList;
+
+/**
+ * User: lofwyr
+ * Date: 23.04.2007 16:10:22
+ */
+public class MixedTreeModel {
+
+  private DefaultMutableTreeNode root;
+  private DefaultMutableTreeNode current;
+  private Integer nextChildIndex;
+  private DefaultMutableTreeNode dataRoot;
+  private boolean isInData;
+  private Stack<Boolean> junctions = new Stack<Boolean>();
+
+  public void beginBuildNode(UITreeNode node) {
+    if (!isInData) {
+      DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(node.getAttributes().get(ATTR_LABEL));
+      if (root == null) {
+        root = newNode;
+      } else {
+        current.add(newNode);
+        current = newNode;
+      }
+    }
+  }
+
+  public void endBuildNode(UITreeNode node) {
+    if (!isInData) {
+      current = (DefaultMutableTreeNode) current.getParent();
+    }
+  }
+
+  public void beginBuildNodeData(UITreeNodeData data) {
+    current = new DefaultMutableTreeNode(data.getValue());
+    if (root == null) {
+      root = current;
+    } else {
+      root.add(current);
+    }
+    isInData = true;
+  }
+
+  public void endBuildNodeData(UITreeNodeData data) {
+    current = (DefaultMutableTreeNode) current.getParent();
+    isInData = false;
+  }
+
+  public void onEncodeBegin() {
+    if (current == null) {
+      current = root;
+    } else {
+      current = (DefaultMutableTreeNode) current.getChildAt(nextChildIndex);
+      if (!isInData && current.getUserObject() instanceof DefaultMutableTreeNode) {
+        isInData = true;
+        dataRoot = current;
+        current = (DefaultMutableTreeNode) current.getUserObject();
+      }
+    }
+    nextChildIndex = 0;
+
+    junctions.push(hasCurrentNodeNextSibling());
+  }
+
+  public void onEncodeEnd() {
+    if (isInData && current.isRoot()) {
+      current = dataRoot;
+      isInData = false;
+    }
+    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) current.getParent();
+    if (parent != null) {
+      nextChildIndex = parent.getIndex(current) + 1;
+      current = parent;
+    } else {
+      nextChildIndex = null;
+      current = null;
+    }
+
+    junctions.pop();
+  }
+
+  public boolean hasCurrentNodeNextSibling() {
+    if (isInData && current.isRoot()) {
+      return dataRoot.getNextSibling() != null;
+    } else {
+      return current.getNextSibling() != null;
+    }
+  }
+
+  public boolean isFolder() {
+    return current.getChildCount() > 0;
+  }
+
+  public int getDepth() {
+    return junctions.size();
+  }
+
+  public List<Boolean> getJunctions() {
+    Boolean top = junctions.pop();
+    List<Boolean> result = Collections.unmodifiableList(new ArrayList<Boolean>(junctions));
+    junctions.push(top);
+    return result;
+  }
+
+}

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/TreeModel.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/TreeModel.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/TreeModel.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/model/TreeModel.java Thu May  3 03:30:36 2007
@@ -29,6 +29,7 @@
 import java.util.Collections;
 
 // todo: make more general (e.g. support other trees)
+// todo: Should be the model for the whole tree, (also for tc:treeNode on the JSP)
 public class TreeModel {
 
   private static final Log LOG = LogFactory.getLog(TreeModel.class);
@@ -36,6 +37,9 @@
   private Map<String, DefaultMutableTreeNode> nodes = new HashMap<String, DefaultMutableTreeNode>();
   private List<String> keys = new ArrayList<String>();
 
+  // XXX not nice
+  private List<Tag> doubleKeys = new ArrayList<Tag>(); // with "begin tags" and "end tags"
+
   public TreeModel(DefaultMutableTreeNode node) {
     putNodes(node, "", 0);
   }
@@ -51,6 +55,7 @@
     position += "_" + index;
 
     keys.add(position);
+    doubleKeys.add(new Tag(position, true));
     nodes.put(position, node);
 
     index = 0;
@@ -59,6 +64,8 @@
       putNodes(subNode, position, index);
       index++;
     }
+
+    doubleKeys.add(new Tag(position, false));
   }
 
   public DefaultMutableTreeNode getNode(String pathIndex) {
@@ -69,6 +76,10 @@
     return Collections.unmodifiableList(keys);
   }
 
+  public List<Tag> getDoublePathIndexList() {
+    return Collections.unmodifiableList(doubleKeys);
+  }
+
   public String getParentPathIndex(String pathIndex) {
     int lastUnderscore = pathIndex.lastIndexOf('_');
     switch (lastUnderscore) {
@@ -78,6 +89,34 @@
         return null;
       default:
         return pathIndex.substring(0, lastUnderscore);
+    }
+  }
+
+  public static class Tag {
+
+    private String name;
+
+    private boolean start;
+
+    public Tag(String name, boolean start) {
+      this.name = name;
+      this.start = start;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public void setName(String name) {
+      this.name = name;
+    }
+
+    public boolean isStart() {
+      return start;
+    }
+
+    public void setStart(boolean start) {
+      this.start = start;
     }
   }
 }

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeNodeRenderer.java Thu May  3 03:30:36 2007
@@ -22,34 +22,37 @@
  * $Id$
  */
 
-import org.apache.commons.lang.StringEscapeUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ACTION_LINK;
-import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DISABLED;
+import static org.apache.myfaces.tobago.TobagoConstants.ATTR_EXPANDED;
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LABEL;
-import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ONCLICK;
-import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SELECTABLE;
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE;
-import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE_CLASS;
-import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TARGET;
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TIP;
 import org.apache.myfaces.tobago.component.ComponentUtil;
 import org.apache.myfaces.tobago.component.UITree;
 import org.apache.myfaces.tobago.component.UITreeNode;
-import org.apache.myfaces.tobago.component.UITreeNodeData;
+import org.apache.myfaces.tobago.context.ResourceManagerUtil;
+import org.apache.myfaces.tobago.model.MixedTreeModel;
 import org.apache.myfaces.tobago.model.TreeState;
 import org.apache.myfaces.tobago.renderkit.CommandRendererBase;
-import org.apache.myfaces.tobago.renderkit.html.HtmlRendererUtil;
+import org.apache.myfaces.tobago.renderkit.html.CommandRendererHelper;
+import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
+import org.apache.myfaces.tobago.renderkit.html.HtmlConstants;
+import static org.apache.myfaces.tobago.renderkit.html.HtmlConstants.A;
+import static org.apache.myfaces.tobago.renderkit.html.HtmlConstants.DIV;
+import static org.apache.myfaces.tobago.renderkit.html.HtmlConstants.IMG;
 import org.apache.myfaces.tobago.renderkit.html.HtmlStyleMap;
+import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
+import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
 
 import javax.faces.component.NamingContainer;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
-import javax.faces.context.ResponseWriter;
+import javax.faces.el.ValueBinding;
 import javax.swing.tree.DefaultMutableTreeNode;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 
 public class TreeNodeRenderer extends CommandRendererBase {
@@ -70,17 +73,24 @@
     TreeState state = tree.getState();
     String treeId = tree.getClientId(facesContext);
     String nodeStateId = node.nodeStateId(facesContext);
-    Map requestParameterMap
-        = facesContext.getExternalContext().getRequestParameterMap();
+    Map requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
+    String id = node.getClientId(facesContext);
 
     // expand state
-    String expandState = (String) requestParameterMap.get(treeId);
-    String searchString = ";" + nodeStateId + ";";
-    if (StringUtils.contains(expandState, searchString)) {
-      state.addExpandState((DefaultMutableTreeNode) node.getValue());
-    }
+    boolean expanded = Boolean.parseBoolean((String) requestParameterMap.get(id + "-expanded"));
 
+//    String expandState = (String) requestParameterMap.get(treeId);
+//    String searchString = ";" + nodeStateId + ";";
+//    if (StringUtils.contains(expandState, searchString)) {
+//    if (expanded) {
+//      state.addExpandState((DefaultMutableTreeNode) node.getValue());
+      ValueBinding binding = node.getValueBinding(ATTR_EXPANDED);
+      if (binding != null) {
+        binding.setValue(facesContext, expanded);
+      }
+//    }
 
+    String searchString;
     if (TreeRenderer.isSelectable(tree)) { // selection
       String selected = (String) requestParameterMap.get(treeId + UITree.SELECT_STATE);
       searchString = ";" + nodeStateId + ";";
@@ -101,278 +111,404 @@
   }
 
   @Override
-  public void encodeBegin(FacesContext facesContext, UIComponent component)
-      throws IOException {
+  public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException {
 
-    UITreeNode treeNode = (UITreeNode) component;
+    UITreeNode node = (UITreeNode) component;
+    UITree root = node.findTree();
+    MixedTreeModel mixedModel = root.getModel();
 
-    UIComponent parent = treeNode.getParent();
+    mixedModel.onEncodeBegin();
 
-    boolean isFolder = treeNode.getChildCount() > 0;
+    TobagoResponseWriter writer = (TobagoResponseWriter) facesContext.getResponseWriter();
 
-    String parentClientId = null;
-    if (parent != null && parent instanceof UITreeNode) { // if not the root node
-      parentClientId = treeNode.getParent().getClientId(facesContext);
-    } else if (parent != null && parent instanceof UITreeNodeData) {
-      String pci = parent.getClientId(facesContext);
-      if (pci.endsWith(":_0")) {
-        UIComponent superParent = parent.getParent();
-        parentClientId = superParent.getClientId(facesContext);
-      } else {
-        parentClientId = pci.substring(0, pci.length() - 2) // fixme 2 is not correct for bitter trees
-             + NamingContainer.SEPARATOR_CHAR + treeNode.getId();
-      }
-      DefaultMutableTreeNode currentNode =
-          ((UITreeNodeData) parent).getCurrentNode();
-      if (currentNode != null) {
-        isFolder = currentNode.getChildCount() > 0;
+    TreeState treeState = root.getState();
+    String treeId = root.getClientId(facesContext);
+
+    DefaultMutableTreeNode modelNode = (DefaultMutableTreeNode) node.getValue();
+
+    boolean isFolder = mixedModel.isFolder();
+
+    boolean marked = treeState.isMarked(modelNode);
+    String id = node.getClientId(facesContext);
+    boolean expanded = ComponentUtil.getBooleanAttribute(node, ATTR_EXPANDED);
+    boolean menuMode = root.getMode().equals("menu");
+
+    boolean showIcons = root.isShowIcons();
+    boolean showJunctions = root.isShowJunctions();
+    boolean showRootJunction = root.isShowRootJunction();
+    boolean showRoot = root.isShowRoot();
+    int depth = mixedModel.getDepth();
+    boolean hasNextSibling = mixedModel.hasCurrentNodeNextSibling();
+    List<Boolean> junctions = mixedModel.getJunctions();
+
+    CommandRendererHelper helper = new CommandRendererHelper(facesContext, node);
+
+    writer.startElement(DIV);
+
+    // div id
+    writer.writeIdAttribute(id);
+
+    // div class (css)
+    StyleClasses styleClasses = StyleClasses.ensureStyleClasses(node);
+    styleClasses.removeTobagoClasses("treeNode");
+    styleClasses.addAspectClass("treeNode", StyleClasses.Aspect.DEFAULT);
+    if ("menu".equals(root.getMode())) {
+      styleClasses.addClass("treeNode", "menu");
+      if (marked) {
+        styleClasses.addClass("treeNode", "marker");
       }
     }
+    styleClasses.addMarkupClass(node, "treeNode");
+    writer.writeClassAttribute(styleClasses);
 
-    UITree root = treeNode.findTree();
-    String rootId = root.getClientId(facesContext);
+    // div style (width)
+    Integer width = null;
+    HtmlStyleMap style = (HtmlStyleMap) root.getAttributes().get(ATTR_STYLE);
+    if (style != null) {
+      width = style.getInt("width");
+    }
+    String widthString;
+    if (width != null) {
+      widthString = "width: " + Integer.toString(width - 4); // fixme: 4
+    } else {
+      widthString = "100%";
+    }
+    writer.writeAttribute("style", widthString, null);
 
-    String clientId = treeNode.getClientId(facesContext);
-//    clientId += pos != null ? pos : "";
+    if (isFolder) {
+      encodeExpandedHidden(writer, node, id, expanded);
+    }
 
-    String jsClientId = TreeRenderer.createJavascriptVariable(clientId);
-    String jsParentClientId = TreeRenderer.createJavascriptVariable(
-        parentClientId);
-//  rootId = HtmlUtils.createJavascriptVariable(rootId);
+    if (isFolder && menuMode) {
+      encodeMenuIcon(facesContext, writer, treeId, id, expanded);
+    }
 
-    TreeState treeState = root.getState();
-    if (treeState == null) {
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("No treeState found. clientId=" + clientId);
-      }
-    } else {
+    encodeIndent(facesContext, writer, menuMode, junctions);
 
-      DefaultMutableTreeNode modelNode = (DefaultMutableTreeNode) treeNode.getValue();
+    encodeTreeJunction(facesContext, writer, id, treeId, showJunctions, showRootJunction, showRoot, expanded, isFolder,
+        depth, hasNextSibling);
 
-      ResponseWriter writer = facesContext.getResponseWriter();
+    encodeTreeIcons(facesContext, writer, id, treeId, showIcons, expanded, isFolder);
 
-      String debuging = "";
+    encodeLabel(writer, helper, node, marked, treeId);
 
-      writer.writeText("  var ", null);
-      writer.writeText(jsClientId, null);
-      writer.writeText(" = new TreeNode('", null);
-      // label
-      Object label = treeNode.getAttributes().get(ATTR_LABEL);
-      if (LOG.isDebugEnabled()) {
-        debuging += label + " : ";
-      }
-      if (label != null) {
-        writer.writeText(StringEscapeUtils.escapeJavaScript(label.toString()), null);
-      } else {
-        LOG.warn("label = null");
-      }
-      writer.writeText("',", null);
+    writer.endElement(DIV);
 
-      // tip
-      String tip = (String) treeNode.getAttributes().get(ATTR_TIP);
-      if (tip != null) {
-        tip = StringEscapeUtils.escapeJavaScript(tip);
-        writer.writeText("'", null);
-        writer.writeText(tip, null);
-        writer.writeText("','", null);
-      } else {
-        writer.writeText("null,'", null);
-      }
+    if (isFolder) {
+      String contentStyle = "display: " + (expanded ? "block" : "none") + ";";
+      writer.startElement(DIV);
+      writer.writeIdAttribute(id + "-cont");
+      writer.writeAttribute("style", contentStyle, null);
+    }
 
-      // id
-      writer.writeText(clientId, null);
-      writer.writeText("','", null);
-
-      // mode
-      writer.writeText(root.getMode(), null);
-      writer.writeText("',", null);
-
-      // is folder
-      writer.writeText(isFolder, null);
-      writer.writeText(",", null);
-
-      // show icons
-      writer.writeText(Boolean.toString(!root.isShowIcons()), null);
-      writer.writeText(",", null);
-
-      // show junctions
-      writer.writeText(Boolean.toString(!root.isShowJunctions()), null);
-      writer.writeText(",", null);
-
-      // show root junction
-      writer.writeText(Boolean.toString(!root.isShowRootJunction()), null);
-      writer.writeText(",", null);
-
-      // show root
-      writer.writeText(Boolean.toString(!root.isShowRoot()), null);
-      writer.writeText(",'", null);
-
-      // tree id
-      writer.writeText(rootId, null);
-      writer.writeText("',", null);
-
-      //
-      String selectable = ComponentUtil.getStringAttribute(root, ATTR_SELECTABLE);
-      if (selectable != null
-          && !("multi".equals(selectable) || "multiLeafOnly".equals(selectable)
-          || "single".equals(selectable) || "singleLeafOnly".equals(selectable)
-          || "sibling".equals(selectable) || "siblingLeafOnly".equals(selectable))) {
-        selectable = null;
-      }
-      if (selectable != null) {
-        writer.writeText("'", null);
-        writer.writeText(selectable, null);
-        writer.writeText("'", null);
-      } else {
-        writer.writeText("false", null);
-      }
-      writer.writeText(",", null);
-      // mutable = false
-      writer.writeText("false", null);
-      writer.writeText(",'", null);
-      writer.writeText(ComponentUtil.findPage(treeNode).getFormId(facesContext), null);
-      writer.writeText("',", null);
-      if (treeNode.getChildCount() == 0
-          || (selectable != null && !selectable.endsWith("LeafOnly"))) {
-        boolean selected = treeState.isSelected(modelNode);
-        writer.writeText(Boolean.toString(selected), null);
-        if (LOG.isDebugEnabled()) {
-          debuging += selected ? "S" : "-";
-        }
-      } else {
-        writer.writeText("false", null);
-        if (LOG.isDebugEnabled()) {
-          debuging += "-";
-        }
-        if (treeState.isSelected(modelNode)) {
-          LOG.warn("Ignore selected FolderNode in LeafOnly selection tree!");
-        }
-      }
-      writer.writeText(",", null);
+    String label = (String) node.getAttributes().get(ATTR_LABEL);
+    int level = modelNode.getLevel();
+    StringBuilder builder = new StringBuilder();
+    for (int i = 0; i < level; i++) {
+      builder.append("    ");
 
-      // marked
-      boolean marked = treeState.isMarked(modelNode);
-      writer.writeText(Boolean.toString(marked), null);
-      writer.writeText(",", null);
-
-      // expanded
-      boolean expanded = treeState.isExpanded(modelNode);
-      writer.writeText(Boolean.toString(expanded), null);
-      if (LOG.isDebugEnabled()) {
-        debuging += expanded ? "E" : "-";
-      }
-
-      writer.writeText(",", null);
-
-      // required
-      writer.writeText(Boolean.toString(root.isRequired()), null);
-      writer.writeText(",", null);
-
-      // disabled
-      writer.writeText(ComponentUtil.getBooleanAttribute(treeNode, ATTR_DISABLED), null);
-      writer.writeText(",", null);
-
-      // resources
-      writer.writeText("treeResourcesHelp,", null);
-
-      // action link
-      String actionLink =
-          (String) treeNode.getAttributes().get(ATTR_ACTION_LINK);
-      if (actionLink != null) {
-        writer.writeText("'", null);
-        writer.writeText(actionLink, null);
-        writer.writeText("',", null);
-      } else {
-        writer.writeText("null,", null);
-      }
+    }
+    LOG.debug(builder + "<div name=" + label + ">");
+  }
 
-      // target
-      String target = (String) treeNode.getAttributes().get(ATTR_TARGET);
-      if (target != null) {
-        writer.writeText("'", null);
-        writer.writeText(target, null);
-        writer.writeText("',", null);
-      } else {
-        writer.writeText("null,", null);
-      }
+  private void encodeExpandedHidden(TobagoResponseWriter writer, UITreeNode node, String clientId, boolean expanded)
+      throws IOException {
+    writer.startElement(HtmlConstants.INPUT, node);
+    writer.writeAttribute(HtmlAttributes.TYPE, "hidden", null);
+    writer.writeNameAttribute(clientId + "-expanded");
+    writer.writeIdAttribute(clientId + "-expanded");
+    writer.writeAttribute(HtmlAttributes.VALUE, expanded, null);
+    writer.endElement(HtmlConstants.INPUT);
+  }
 
-      // onclick
-      String onclick = (String) treeNode.getAttributes().get(ATTR_ONCLICK);
-      if (onclick != null) {
-        writer.writeText("'", null);
-        onclick = onclick.replaceAll("\\'", "\\\\'");
-        writer.writeText(onclick, null);
-        writer.writeText("',", null);
-      } else {
-        writer.writeText("null,", null);
-      }
+  private void encodeMenuIcon(
+      FacesContext facesContext, TobagoResponseWriter writer, String treeId, String id, boolean expanded)
+      throws IOException {
+    String menuOpen = ResourceManagerUtil.getImageWithPath(facesContext, "image/treeMenuOpen.gif");
+    String menuClose = ResourceManagerUtil.getImageWithPath(facesContext, "image/treeMenuClose.gif");
+    String onclick = "new_tobagoTreeNodeToggle(this.parentNode, '" + treeId + "', null, null, '" + menuOpen + "', '" + menuClose + "')";
+    String src = expanded ? menuOpen : menuClose;
+    writer.startElement(IMG);
+    writer.writeClassAttribute("tobago-tree-menu-icon");
+    writer.writeIdAttribute(id + "-menuIcon");
+    writer.writeAttribute("src", src, null);
+    writer.writeAttribute("onclick", onclick, null);
+    writer.writeAttribute("alt", "", null);
+    writer.endElement(IMG);
+  }
 
-      // parent
-      if (jsParentClientId != null) {
-        writer.writeText(jsParentClientId, null);
-      } else {
-        writer.writeText("null", null);
-      }
-      writer.writeText(",", null);
+  private void encodeIndent(
+      FacesContext facesContext, TobagoResponseWriter writer, boolean menuMode, List<Boolean> junctions)
+      throws IOException {
 
-      // icon (not implemented)
-      writer.writeText("null", null);
-      writer.writeText(",", null);
-
-      // open folder icon (not implemented)
-      writer.writeText("null", null);
-      writer.writeText(", ", null);
-
-      // width
-      writer.writeText("'", null);
-      Integer width = null;
-      HtmlStyleMap style = (HtmlStyleMap) root.getAttributes().get(ATTR_STYLE);
-      if (style != null) {
-        width = style.getInt("width");
-      }
-      if (width != null) {
-        writer.writeText(width - 4, null); // fixme: 4
+    String blank = ResourceManagerUtil.getImageWithPath(facesContext, "image/blank.gif");
+    String perpendicular = ResourceManagerUtil.getImageWithPath(facesContext, "image/I.gif");
+
+    for (Boolean junction : junctions) {
+      writer.startElement(IMG);
+      writer.writeClassAttribute("tree-junction");
+      if (junction && !menuMode) {
+        writer.writeAttribute("src", perpendicular, null);
       } else {
-        writer.writeText("100%", null);
+        writer.writeAttribute("src", blank, null);
       }
-      writer.writeText("', ", null);
+      writer.endElement(IMG);
+    }
+  }
 
-      // css class
-      writer.writeText("'", null);
-      if ("menu".equals(root.getMode())) { // todo: clean up: think about composition of the style-class names
-        HtmlRendererUtil.addCssClass(treeNode, "tobago-treeNode-menu");
-        if (marked) {
-          HtmlRendererUtil.addCssClass(treeNode, "tobago-treeNode-marker");
-        }
-      }
-      StringBuilder treeNodeClass = new StringBuilder((String) treeNode.getAttributes().get(ATTR_STYLE_CLASS));
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("styleClass='" + treeNodeClass + "'");
-      }
-      writer.writeText(treeNodeClass, null);
-      writer.writeText("', ", null);
+  private void encodeTreeJunction(
+      FacesContext facesContext, TobagoResponseWriter writer, String id, String treeId,
+      boolean showJunctions, boolean showRootJunction, boolean showRoot,
+      boolean expanded, boolean isFolder, int depth, boolean hasNextSibling) throws IOException {
+    if (!(!showJunctions
+        || !showRootJunction && depth == 0
+        || !showRootJunction && !showRoot && depth == 1)) {
+      writer.startElement(IMG);
+      writer.writeClassAttribute("tree-junction");
+      writer.writeIdAttribute(id + "-junction");
+
+      String gif = expanded
+          ? (depth == 0
+            ? "Rminus.gif"
+            : (hasNextSibling ? "Tminus.gif" : "Lminus.gif"))
+          : ((depth == 0)
+            ? "Rplus.gif"
+            : (hasNextSibling)
+              ? (isFolder ? "Tplus.gif" : "T.gif")
+              : (isFolder ? "Lplus.gif" : "L.gif")
+      );
+
+      String src = ResourceManagerUtil.getImageWithPath(facesContext, "image/" + gif);
+      writer.writeAttribute("src", src, null);
+      if (isFolder) {
+        writer.writeAttribute("onclick", createOnclickForToggle(facesContext, treeId), null);
+      }
+      writer.writeAttribute("alt", "", null);
+//    } else if (( !this.hideRoot && depth >0 ) || (this.hideRoot && depth > 1)) {
+//      str += '<img class="tree-junction" id="' + this.id
+//          + '-junction" src="' + this.treeResources.getImage("blank.gif")
+//          + '" alt="">';
+      writer.endElement(IMG);
+    }
+  }
 
-      // css class label
-      writer.writeText("'", null);
-      if (marked) {
-        writer.writeText("tobago-treeNode-marker", null);
+  private void encodeTreeIcons(
+      FacesContext facesContext, TobagoResponseWriter writer, String id, String treeId,
+      boolean showIcons, boolean expanded, boolean isFolder)
+      throws IOException {
+
+    if (showIcons) {
+      writer.startElement(IMG);
+      writer.writeClassAttribute("tree-icon");
+      writer.writeIdAttribute(id + "-icon");
+
+      String gif = isFolder
+          ? (expanded ? "openfoldericon.gif" : "foldericon.gif")
+          : "new.gif";
+
+      String src = ResourceManagerUtil.getImageWithPath(facesContext, "image/" + gif);
+      writer.writeAttribute("src", src, null);
+      if (isFolder) {
+        writer.writeAttribute("onclick", createOnclickForToggle(facesContext, treeId), null);
       }
-      writer.writeText("'", null);
+      writer.writeAttribute("alt", "", null);
+      writer.endElement(IMG);
+    }
+  }
+
+  private String createOnclickForToggle(FacesContext facesContext, String treeId) {
+    return "new_tobagoTreeNodeToggle(this.parentNode, '" + treeId + "', '"
+          + ResourceManagerUtil.getImageWithPath(facesContext, "image/openfoldericon.gif") + "', '"
+          + ResourceManagerUtil.getImageWithPath(facesContext, "image/foldericon.gif") + "', null, null)";
+  }
+
 
-      writer.writeText(");\n", null);
 
 /*
-      if (jsParentClientId != null) { // if not the root node
-        writer.writeText("  ", null);
-        writer.writeText(jsParentClientId, null);
-        writer.writeText(".add(", null);
-        writer.writeText(jsClientId, null);
-        writer.writeText(");\n", null);
-      }
+  if (this.isFolder) {
+    str += '<img class="tree-icon" id="' + this.id + '-icon" '
+        + 'src="' + (this.expanded ? this.openIcon : this.icon) + ' " '
+        + 'onclick="toggle(this.parentNode, \'' + this.treeHiddenId
+        + '\', \'' + this.treeResources.getImage("openfoldericon.gif")
+        + '\', \'' + this.treeResources.getImage("foldericon.gif")
+        + '\')"'
+        + ' alt="">';
+  } else {
+    str += '<img class="tree-icon" id="' + this.id
+        + '-icon" src="' + this.treeResources.getImage("new.gif") + '" alt="">';
+  }
 */
-      if (LOG.isDebugEnabled()) {
-        LOG.debug(debuging);
+
+
+  private void encodeLabel(
+      TobagoResponseWriter writer, CommandRendererHelper helper, UITreeNode node, boolean marked, String treeId)
+      throws IOException {
+
+    writer.startElement(A);
+    if (marked) {
+      StyleClasses classes = new StyleClasses();
+      classes.addClass("treeNode", "marker");
+      writer.writeClassAttribute(classes);
+    }
+    String tip = (String) node.getAttributes().get(ATTR_TIP);
+    if (tip != null) {
+//XXX is needed?      tip = StringEscapeUtils.escapeJavaScript(tip);
+      writer.writeAttribute("title", tip, null);
+    }
+    writer.writeAttribute("href", helper.getHref(), null);
+    writer.writeAttribute("onclick", helper.getOnclick(), null);
+    writer.writeAttribute("onfocus", "storeMarker(this.parentNode, '" + treeId + "')", null);
+    String label = (String) node.getAttributes().get(ATTR_LABEL);
+    if (label == null) {
+      LOG.warn("label == null");
+    }
+    writer.writeText(label, null);
+    writer.endElement(A);
+  }
+
+  @Override
+  public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException {
+
+    UITreeNode node = (UITreeNode) component;
+    UITree root = node.findTree();
+    MixedTreeModel mixedModel = root.getModel();
+    boolean isFolder = mixedModel.isFolder();
+
+    mixedModel.onEncodeEnd();
+
+    String id = node.getClientId(facesContext);
+
+    TobagoResponseWriter writer = (TobagoResponseWriter) facesContext.getResponseWriter();
+
+    if (isFolder) {
+      writer.endElement(DIV);
+      writer.writeComment("\nend of " + id + "-cont ");
+    }
+
+    if (LOG.isDebugEnabled()) {
+      String label = (String) node.getAttributes().get(ATTR_LABEL);
+      int level = mixedModel.getDepth();
+      StringBuilder builder = new StringBuilder();
+      for (int i = 0; i < level; i++) {
+        builder.append("    ");
+
       }
+      LOG.debug(builder + "</div> <!-- " + label + " -->");
     }
   }
+
 }
+/*
+
+TreeNode.prototype.toString = function (depth, last) {
+    if (!depth) depth = 0;
+
+    var str = '';
+    if (! this.hideRoot || depth > 0) {
+      str += '<div id="' + this.id + '" class="' + this.cssClass + '" '
+          + 'style="width: ' + this.width + ';">';
+      if (this.mode == "menu") {
+        if (this.isFolder) {
+          // FIXME: change the icons when klick on the icon
+          str += '<img class="tobago-tree-menu-icon" id="' + this.id + '-menuIcon"'
+              + 'src="' + (this.expanded ? this.treeResources.getImage("treeMenuOpen.gif") : this.treeResources.getImage("treeMenuClose.gif")) + ' " '
+              + 'onclick="toggle(this.parentNode, \'' + this.treeHiddenId
+              + '\', null, null, \'' + this.treeResources.getImage("treeMenuOpen.gif")
+              + '\', \'' + this.treeResources.getImage("treeMenuClose.gif")
+              + '\')"'
+              + ' alt="">';
+        }
+      }
+      str += this.indent(depth, last);
+      if (!(   this.hideJunctions
+            || this.hideRootJunction && depth == 0
+            || this.hideRootJunction && this.hideRoot && depth == 1)) {
+        str += '<img class="tree-junction" id="' + this.id
+            + '-junction" src="' + (this.expanded
+              ? ((depth == 0)
+                ? this.treeResources.getImage("Rminus.gif")
+                : (last)
+                  ? this.treeResources.getImage("Lminus.gif")
+                  : this.treeResources.getImage("Tminus.gif"))
+              : ((depth == 0)
+                ? this.treeResources.getImage("Rplus.gif")
+                : (last)
+                  ? this.treeResources.getImage(this.isFolder ? "Lplus.gif" : "L.gif")
+                  : this.treeResources.getImage(this.isFolder ? "Tplus.gif" : "T.gif"))
+              )
+            + '" onclick="toggle(this.parentNode, \'' + this.treeHiddenId
+            + '\', \'' + this.treeResources.getImage("openfoldericon.gif")
+            + '\', \'' + this.treeResources.getImage("foldericon.gif")
+            + '\')"'
+            + ' alt="">';
+      } else if (( !this.hideRoot && depth >0 ) || (this.hideRoot && depth > 1)) {
+        str += '<img class="tree-junction" id="' + this.id
+            + '-junction" src="' + this.treeResources.getImage("blank.gif")
+            + '" alt="">';
+      }
+      if (! this.hideIcons) {
+        if (this.isFolder) {
+          str += '<img class="tree-icon" id="' + this.id + '-icon" '
+              + 'src="' + (this.expanded ? this.openIcon : this.icon) + ' " '
+              + 'onclick="toggle(this.parentNode, \'' + this.treeHiddenId
+              + '\', \'' + this.treeResources.getImage("openfoldericon.gif")
+              + '\', \'' + this.treeResources.getImage("foldericon.gif")
+              + '\')"'
+              + ' alt="">';
+        } else {
+          str += '<img class="tree-icon" id="' + this.id
+              + '-icon" src="' + this.treeResources.getImage("new.gif") + '" alt="">';
+        }
+      }
+      if (this.selectable) {
+        var markIcon = '';
+        var markIconOnClickFunction = '';
+        if (this.selectable.match(/LeafOnly$/) && this.isFolder) {
+          markIcon = this.treeResources.getImage("1x1.gif");
+        } else {
+          if (this.selected) {
+            markIcon = this.treeResources.getImage("checked" + (this.disabled ? "Disabled" : "") + ".gif");
+          } else {
+            markIcon = this.treeResources.getImage("unchecked" + (this.disabled ? "Disabled" : "") + ".gif");
+          }
+          if (!this.disabled) {
+            markIconOnClickFunction
+                = 'onclick="toggleSelect(this.parentNode, \'' + this.treeHiddenId
+                + '\', \'' + this.treeResources.getImage("unchecked.gif")
+                + '\', \'' + this.treeResources.getImage("checked.gif")
+                + '\')"';
+          }
+        }
+
+        str += '<img class="tree-icon" id="' + this.id
+            + '-markIcon" src="' + markIcon + '" ' + markIconOnClickFunction + ' alt="">';
+      }
+      str += '<a class="' + this.cssClassLabel + '"';
+      if (this.tip) {
+        str += ' title="' + this.tip + '"';
+      }
+      if (!this.disabled) {
+        str += ' href="' + Tobago.EMPTY_HREF +  '"'
+            + ' onclick="Tobago.Tree.onClick(this)"'
+            + ' ondblclick="Tobago.Tree.onDblClick(this)"'
+            + ' onfocus="' + this.onfocus + '"';
+      }
+      str += '>'
+          + this.label + '</a>';
+      str += '</div>';
+    }
+    if (this.isFolder) {
+      str += '<div id="' + this.id
+          + '-cont" style="display: '
+          + (this.expanded ? 'block' : 'none') + ';">';
+      for (var i=0; i<this.childNodes.length; ++i) {
+        var lastChild = i+1 == this.childNodes.length;
+        var n = this.childNodes[i];
+        str += n.toString(depth+1, lastChild);
+      }
+      str += '</div>';
+    }
+
+    return str;
+  };
+*/
\ No newline at end of file

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeRenderer.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeRenderer.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/tag/TreeRenderer.java Thu May  3 03:30:36 2007
@@ -174,6 +174,8 @@
       HtmlRendererUtil.writeScriptLoader(facesContext, scripts, scriptTexts);
     }
 
+    RenderUtil.encode(facesContext, root);
+
     writer.endElement(HtmlConstants.DIV);
   }
 
@@ -204,7 +206,9 @@
     sb.append("    }\n");
     sb.append("  };\n \n");
 
-    sb.append(getNodesAsJavascript(facesContext, root));
+    sb.append("/* disabled!!! \n");
+
+//    sb.append(getNodesAsJavascript(facesContext, root));
 
     sb.append("  var treeDiv = document.getElementById('");
     sb.append(clientId);
@@ -216,6 +220,8 @@
 
     sb.append(rootNode);
     sb.append(".initSelection();\n");
+
+    sb.append("disabled!!! */");
 
     sb.append("}");
 //    return sb.toString();

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTag.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTag.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTag.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTag.java Thu May  3 03:30:36 2007
@@ -17,6 +17,7 @@
  * limitations under the License.
  */
 
+import static org.apache.myfaces.tobago.TobagoConstants.ATTR_EXPANDED;
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TIP;
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TARGET;
 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_VALUE;
@@ -32,6 +33,7 @@
   private String markup;
   private String tip;
   private String target;
+  private String expanded;
 
   @Override
   public String getComponentType() {
@@ -46,6 +48,7 @@
     ComponentUtil.setMarkup(component, markup);
     ComponentUtil.setStringProperty(component, ATTR_TIP, tip);
     ComponentUtil.setStringProperty(component, ATTR_TARGET, target);
+    ComponentUtil.setBooleanProperty(component, ATTR_EXPANDED, expanded);
   }
 
   @Override
@@ -55,6 +58,7 @@
     markup = null;
     tip = null;
     target = null;
+    expanded = null;
   }
 
   public String getValue() {
@@ -79,5 +83,13 @@
 
   public void setTarget(String target) {
     this.target = target;
+  }
+
+  public String getExpanded() {
+    return expanded;
+  }
+
+  public void setExpanded(String expanded) {
+    this.expanded = expanded;
   }
 }

Modified: myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTagDeclaration.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTagDeclaration.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTagDeclaration.java (original)
+++ myfaces/tobago/trunk/sandbox/src/main/java/org/apache/myfaces/tobago/taglib/sandbox/TreeNodeTagDeclaration.java Thu May  3 03:30:36 2007
@@ -20,6 +20,8 @@
 import org.apache.myfaces.tobago.apt.annotation.BodyContentDescription;
 import org.apache.myfaces.tobago.apt.annotation.Tag;
 import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
 import org.apache.myfaces.tobago.taglib.decl.HasIdBindingAndRendered;
 import org.apache.myfaces.tobago.taglib.decl.HasLabel;
 import org.apache.myfaces.tobago.taglib.decl.HasValue;
@@ -31,6 +33,7 @@
 /**
  * Creates a tree node.
  */
+@SuppressWarnings({"ALL"})
 @Tag(name = "treeNode")
 @BodyContentDescription(anyTagOf = "<tcs:treeNode>* <tcs:treeNodeData>*")
 @UIComponentTag(
@@ -38,4 +41,11 @@
     rendererType = "TreeNode")
 public interface TreeNodeTagDeclaration
     extends HasIdBindingAndRendered, HasLabel, HasValue, HasMarkup, AbstractCommandTagDeclaration, HasTip, HasTarget {
+
+  /**
+   * Flag indicating if the subnodes are to be displayed.
+   */
+  @TagAttribute(type = String.class)
+  @UIComponentTagAttribute(type = "java.lang.Boolean")
+  void setExpanded(String expanded);
 }

Modified: myfaces/tobago/trunk/sandbox/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/script/tobago-tree.js
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/script/tobago-tree.js?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/script/tobago-tree.js (original)
+++ myfaces/tobago/trunk/sandbox/src/main/resources/org/apache/myfaces/tobago/renderkit/html/scarborough/standard/script/tobago-tree.js Thu May  3 03:30:36 2007
@@ -843,6 +843,53 @@
   }
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////
 
+function new_tobagoTreeNodeOnclick() {
 
+}
+
+function new_tobagoTreeNodeToggle(node, treeHiddenId, openFolderIcon, folderIcon, openMenuIcon, closeMenuIcon) {
+  LOG.debug("toggle("+node+", "+treeHiddenId+", " + openFolderIcon + ", " + folderIcon + ", " + openMenuIcon + ", " + closeMenuIcon + ")");
+  var content = document.getElementById(node.id + "-cont");
+  if (content) {
+    var expandedState = document.getElementById(node.id + '-expanded');
+    var icon = document.getElementById(node.id + '-icon');
+    var menuIcon = document.getElementById(node.id + '-menuIcon');
+    var junction = document.getElementById(node.id + '-junction');
+    var hidden = document.getElementById(treeHiddenId);
+    if (content.style.display == 'none') {
+      content.style.display = 'block';
+      if (icon) {
+        icon.src = openFolderIcon;
+      }
+      if (menuIcon) {
+        menuIcon.src = openMenuIcon;
+      }
+      if (junction) {
+        junction.src = junction.src.replace(/plus\./, "minus.");
+      }
+      hidden.value = hidden.value + new_nodeStateId(node) + ";" ;
+      expandedState.value = "true";
+    } else {
+      content.style.display = 'none';
+      if (icon) {
+        icon.src = folderIcon;
+      }
+      if (menuIcon) {
+        menuIcon.src = closeMenuIcon;
+      }
+      if (junction) {
+        junction.src = junction.src.replace(/minus\./, "plus.");
+      }
+      hidden.value = hidden.value.replace(";" + new_nodeStateId(node) + ";" , ";");
+      expandedState.value = "false";
+    }
+  }
+}
+
+function new_nodeStateId(node) {
+  // this must do the same as nodeStateId() in TreeRenderer.java
+  return node.id.substring(node.id.lastIndexOf(':') + 1);
+}
 

Modified: myfaces/tobago/trunk/sandbox/src/test/java/org/apache/myfaces/tobago/model/TreeModelUnitTest.java
URL: http://svn.apache.org/viewvc/myfaces/tobago/trunk/sandbox/src/test/java/org/apache/myfaces/tobago/model/TreeModelUnitTest.java?view=diff&rev=534781&r1=534780&r2=534781
==============================================================================
--- myfaces/tobago/trunk/sandbox/src/test/java/org/apache/myfaces/tobago/model/TreeModelUnitTest.java (original)
+++ myfaces/tobago/trunk/sandbox/src/test/java/org/apache/myfaces/tobago/model/TreeModelUnitTest.java Thu May  3 03:30:36 2007
@@ -22,8 +22,6 @@
 import javax.swing.tree.DefaultMutableTreeNode;
 import java.util.List;
 
-import org.apache.myfaces.tobago.model.TreeModel;
-
 public class TreeModelUnitTest extends TestCase {
 
   private DefaultMutableTreeNode tree;
@@ -64,6 +62,46 @@
     assertEquals("Sports", "_0_0", pathIndexList.get(1));
     assertEquals("Astronomy", "_0_2_2", pathIndexList.get(6));
     assertEquals("Games", "_0_4", pathIndexList.get(10));
+  }
+
+  /*
+  cat
+    sport
+    /sport
+    movies
+    /movies
+    science
+      geo
+      /geo
+      math
+      /math
+      astro
+        edu
+        /edu
+        pict
+        /pict
+      /astro
+    music
+    /music
+    games
+    /games
+  /cat
+    */
+  public void testDoublePathIndexList() {
+    TreeModel model = new TreeModel(tree);
+    List<TreeModel.Tag> list = model.getDoublePathIndexList();
+    assertEquals("Count", 22, list.size());
+    assertEquals("Root", "_0", list.get(0).getName());
+    assertEquals("Root", "_0", list.get(21).getName());
+    assertEquals("Sports", "_0_0", list.get(1).getName());
+    assertEquals("Sports", "_0_0", list.get(2).getName());
+    assertEquals("Astronomy", "_0_2_2", list.get(10).getName());
+    assertEquals("Astronomy", "_0_2_2", list.get(15).getName());
+    assertEquals("Games", "_0_4", list.get(19).getName());
+    assertEquals("Games", "_0_4", list.get(20).getName());
+
+    assertTrue("Astronomy", list.get(10).isStart());
+    assertFalse("Astronomy", list.get(15).isStart());
   }
 
   public void testParentPathIndex() {