cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sylv...@apache.org
Subject svn commit: r190962 [2/2] - in /cocoon: blocks/forms/trunk/WEB-INF/xconf/ blocks/forms/trunk/java/org/apache/cocoon/forms/ blocks/forms/trunk/java/org/apache/cocoon/forms/event/ blocks/forms/trunk/java/org/apache/cocoon/forms/event/impl/ blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/ blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/ blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/ blocks/forms/trunk/java/org/apache/cocoon/forms/generation/ blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/ blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/ blocks/forms/trunk/java/org/apache/cocoon/forms/resources/js/ blocks/forms/trunk/samples/ blocks/forms/trunk/samples/flow/ blocks/forms/trunk/samples/forms/ trunk/
Date Thu, 16 Jun 2005 17:17:02 GMT
Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeSelectionListener.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeSelectionListener.java?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeSelectionListener.java (added)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeSelectionListener.java Thu Jun 16 10:17:00 2005
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.cocoon.forms.formmodel.tree;
+
+import org.apache.cocoon.forms.event.WidgetListener;
+
+/**
+ * Listener for changes in a {@link Tree}'s selection.
+ * 
+ * @version $Id$
+ */
+public interface TreeSelectionListener extends WidgetListener {
+
+    public void selectionChanged(TreeSelectionEvent event);
+
+}

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeSelectionListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeSelectionListener.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeWalker.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeWalker.java?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeWalker.java (added)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeWalker.java Thu Jun 16 10:17:00 2005
@@ -0,0 +1,222 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.cocoon.forms.formmodel.tree;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.apache.commons.collections.ArrayStack;
+
+/**
+ * A helper to crawl a tree and quickly access important node-related information.
+ * <p>
+ * It's an <code>Iterator</code> on the "current level" of the tree. This level starts
+ * at the root node (and therefore obviously contains only one element), and can then
+ * be changed to children of the current node using {@link #enterChildren()} or popped
+ * back to the parent level using {@link #leave()}.
+ * <p>
+ * The {@link #next()} method will return the next node in the iteration,
+ * and set the current node used by many convenience methods giving information about
+ * that node.
+ * <p>
+ * This class was primarily written for page templates containing {@link Tree}s (see
+ * <code>org/apache/cocoon/forms/generation/jx-macros.xml</code>) but can of course be
+ * used in other places as well.
+ * 
+ * @version $Id$
+ */
+public class TreeWalker implements Iterator {
+    ArrayStack stack = new ArrayStack();
+    Tree tree;
+    Object node;
+    TreePath path;
+    Iterator iter;
+
+    public TreeWalker(Tree tree) {
+        // Root node has no siblings
+        this.iter = Collections.EMPTY_LIST.iterator();
+        this.tree = tree;
+        this.node = tree.getModel().getRoot();
+        this.path = TreePath.ROOT_PATH;
+        
+        stack.push(this.iter);
+        stack.push(this.node);
+    }
+    
+    /**
+     * Starts iterating the children of the current node. The current iterator is pushed
+     * on a stack and will be restored on {@link #leave()}.
+     * <p>
+     * Right after calling this method, there is no current node. Calling {@link #next()}
+     * will move to the first child, if any.
+     * 
+     * @return the current tree walker (i.e. <code>this</code>).
+     */
+    public TreeWalker enterChildren() {
+        Iterator newIter;
+        if (isLeaf()) {
+            newIter = Collections.EMPTY_LIST.iterator();
+        } else {
+            newIter = tree.getModel().getChildren(node).iterator();
+        }
+        this.stack.push(this.iter);
+        this.stack.push(this.path);
+        this.stack.push(this.node);
+        this.iter = newIter;
+        this.node = null;
+        this.path = null;
+        
+        return this;
+    }
+    
+    /**
+     * Go back to the parent node, restoring the iterator at this node.
+     */
+    public void leave() {
+        this.node = this.stack.pop();
+        this.path = (TreePath)this.stack.pop();
+        this.iter = (Iterator)this.stack.pop();
+        this.path = this.path.getParentPath();
+    }
+    
+    /**
+     * Are there more nodes to iterate on at this level?
+     */
+    public boolean hasNext() {
+        return this.iter.hasNext();
+    }
+    
+    /**
+     * Get the next node in the current iteration level.
+     */
+    public Object next() {
+        this.node = iter.next();
+        
+        this.path = new TreePath(
+             (TreePath)this.stack.peek(1),
+             tree.getModel().getChildKey(stack.peek(), this.node));
+        return this.node;
+    }
+    
+    /**
+     * Required by the <code>Iterator</code> interface, but not supported here.
+     * 
+     * @throws UnsupportedOperationException whenever called.
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * Get the current depth of this walker (can be used e.g. to compute indentation margins
+     * or CSS styles). If root node is visible (see {@link Tree#isRootVisible()), depth 0 is
+     * for the root. Otherwise, children of the root node are at depth 0.
+     * 
+     * @return the current depth
+     */
+    public int getDepth() {
+        return path.getPathCount() - (this.tree.isRootVisible() ? 1 : 2);
+    }
+    
+    /**
+     * Get the current node, which is the result of the last call to {@link #next()} (except if
+     * {@link #enterChildren()} or {@link #leave()} where called inbetween.
+     * 
+     * @return the current node.
+     */
+    public Object getNode() {
+        return this.node;
+    }
+    
+    /**
+     * Get the path of the current node.
+     * 
+     * @return the path
+     */
+    public TreePath getPath() {
+        return this.path;
+    }
+    
+    /**
+     * Is the current node a leaf?
+     */
+    public boolean isLeaf() {
+        return this.tree.getModel().isLeaf(this.node);
+    }
+    
+    /**
+     * Is the current node expanded?
+     */
+    public boolean isExpanded() {
+        return this.tree.isExpanded(this.path);
+    }
+    
+    /**
+     * Is the current node collapsed?
+     */
+    public boolean isCollapsed() {
+        return this.tree.isCollapsed(this.path);
+    }
+    
+    /**
+     * Is the current node visible (i.e. its parent is expanded)?
+     */
+    public boolean isVisible() {
+        return this.tree.isVisible(this.path);
+    }
+    
+    /**
+     * Is the current node selected?
+     */
+    public boolean isSelected() {
+        return this.tree.isPathSelected(this.path);
+    }
+    
+    /**
+     * Get the "icon type" that should be used for this node, according to the common
+     * visual paradigms used to render trees:
+     * <ul>
+     * <li>"<code>leaf</code>" for leaf nodes (will be e.g. a file icon),</li>
+     * <li>"<code>expanded</code>" for non-leaf expanded nodes (will be e.g. a "minus" icon)</li>
+     * <li>"<code>collapsed</code>" for non-leaf collapsed nodes (will be e.g. a "plus" icon)</li>
+     * </ul>
+     * 
+     * @return the icon type
+     */
+    public String getIconType() {
+        if (isLeaf()) {
+            return "leaf";
+        } else if (isExpanded()) {
+            return "expanded";
+        } else {
+            return "collapsed";
+        }
+    }
+    
+    /**
+     * Get the "selection type" that should be used for this node, that can be used e.g. as
+     * a CSS class name:
+     * <ul>
+     * <li>"<code>selected</code>" for selected nodes,</li>
+     * <li>"<code>unselected</code>" for unselected nodes.</li>
+     * </ul>
+     * 
+     * @return the selection type
+     */
+    public String getSelectionType() {
+        return this.tree.isPathSelected(this.path) ? "selected" : "unselected";
+    }
+}

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeWalker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/TreeWalker.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/JavaTreeModelDefinitionBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/JavaTreeModelDefinitionBuilder.java?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/JavaTreeModelDefinitionBuilder.java (added)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/JavaTreeModelDefinitionBuilder.java Thu Jun 16 10:17:00 2005
@@ -0,0 +1,78 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.cocoon.forms.formmodel.tree.builder;
+
+import org.apache.avalon.framework.CascadingException;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.components.LifecycleHelper;
+import org.apache.cocoon.forms.formmodel.tree.JavaTreeModelDefinition;
+import org.apache.cocoon.forms.formmodel.tree.TreeModel;
+import org.apache.cocoon.forms.formmodel.tree.TreeModelDefinition;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.w3c.dom.Element;
+
+/**
+ * Builds a {@link TreeModelDefinition} based on an arbitrary Java class.
+ * Avalon lifecycle will be run on the target class when instanciated.
+ * 
+ * @version $Id$
+ */
+public class JavaTreeModelDefinitionBuilder extends AbstractLogEnabled
+    implements TreeModelDefinitionBuilder, Contextualizable, Serviceable {
+
+    Context ctx;
+    ServiceManager manager;
+    
+    public void contextualize(Context context) throws ContextException {
+        this.ctx = context;
+    }
+
+    public void service(ServiceManager manager) throws ServiceException {
+        this.manager = manager;
+    }
+
+    public TreeModelDefinition build(Element treeModelElement) throws Exception {
+        String className = DomHelper.getAttribute(treeModelElement, "class");
+        
+        Class modelClass;
+        try {
+            modelClass = Thread.currentThread().getContextClassLoader().loadClass(className);
+        } catch(Exception e) {
+            throw new CascadingException("Cannot load class '" + className + "', at " +
+                    DomHelper.getLocation(treeModelElement), e);
+        }
+        
+        if (!TreeModel.class.isAssignableFrom(modelClass)) {
+            throw new Exception("Class '" + className + "' doesn't implement TreeModel, at " +
+                    DomHelper.getLocation(treeModelElement));
+        }
+        
+        JavaTreeModelDefinition definition = new JavaTreeModelDefinition();
+        
+        LifecycleHelper.setupComponent(definition, getLogger(), ctx, manager, null);
+        
+        definition.setModelClass(modelClass);
+        
+        return definition;
+    }
+
+}

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/JavaTreeModelDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/JavaTreeModelDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/SourceTreeModelDefinitionBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/SourceTreeModelDefinitionBuilder.java?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/SourceTreeModelDefinitionBuilder.java (added)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/SourceTreeModelDefinitionBuilder.java Thu Jun 16 10:17:00 2005
@@ -0,0 +1,82 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.cocoon.forms.formmodel.tree.builder;
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.forms.Constants;
+import org.apache.cocoon.forms.formmodel.tree.SourceTreeModelDefinition;
+import org.apache.cocoon.forms.formmodel.tree.TreeModelDefinition;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.apache.cocoon.matching.helpers.WildcardHelper;
+import org.apache.excalibur.source.SourceResolver;
+import org.w3c.dom.Element;
+
+/**
+ * Builds a {@link org.apache.cocoon.forms.formmodel.tree.SourceTreeModel}.
+ * 
+ * @version $Id$
+ */
+public class SourceTreeModelDefinitionBuilder extends AbstractLogEnabled
+    implements Serviceable, TreeModelDefinitionBuilder {
+
+    private ServiceManager manager;
+
+    public void service(ServiceManager manager) throws ServiceException {
+        this.manager = manager;
+    }
+
+    public TreeModelDefinition build(Element modelElt) throws Exception {
+        
+        SourceTreeModelDefinition definition = new SourceTreeModelDefinition();
+        
+        definition.setURL(DomHelper.getAttribute(modelElt, "src"));
+        
+        Element fileSet = DomHelper.getChildElement(modelElt, Constants.DEFINITION_NS, "fileset");
+        if (fileSet != null) {
+            definition.setFilePatterns(getPatterns(fileSet, "include"),
+                    getPatterns(fileSet, "exclude"));
+        }
+        
+        Element dirSet = DomHelper.getChildElement(modelElt, Constants.DEFINITION_NS, "dirset");
+        if (dirSet != null) {
+            definition.setDirectoryPatterns(getPatterns(dirSet, "include"),
+                    getPatterns(dirSet, "exclude"));
+        }
+        
+        definition.setSourceResolver((SourceResolver)this.manager.lookup(SourceResolver.ROLE));
+        
+        return definition;
+    }
+    
+    int[][] getPatterns(Element parent, String name) throws Exception {
+        Element[] children = DomHelper.getChildElements(parent, Constants.DEFINITION_NS, name);
+
+        if (children.length == 0) {
+            return null;
+        }
+
+        int[][] result = new int[children.length][];
+        for (int i = 0; i < children.length; i++) {
+            String pattern = DomHelper.getAttribute(children[i], "pattern");
+            result[i] = WildcardHelper.compilePattern(pattern);
+        }
+        return result;
+    }
+
+}

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/SourceTreeModelDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/SourceTreeModelDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeDefinitionBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeDefinitionBuilder.java?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeDefinitionBuilder.java (added)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeDefinitionBuilder.java Thu Jun 16 10:17:00 2005
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.cocoon.forms.formmodel.tree.builder;
+
+import java.util.Iterator;
+
+import org.apache.avalon.framework.service.ServiceSelector;
+import org.apache.cocoon.forms.Constants;
+import org.apache.cocoon.forms.event.ValueChangedListener;
+import org.apache.cocoon.forms.formmodel.AbstractWidgetDefinitionBuilder;
+import org.apache.cocoon.forms.formmodel.WidgetDefinition;
+import org.apache.cocoon.forms.formmodel.tree.Tree;
+import org.apache.cocoon.forms.formmodel.tree.TreeDefinition;
+import org.apache.cocoon.forms.formmodel.tree.TreeSelectionListener;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.w3c.dom.Element;
+
+/**
+ * Builds a {@link org.apache.cocoon.forms.formmodel.tree.Tree}.
+ * 
+ * @version $Id$
+ */
+public class TreeDefinitionBuilder extends AbstractWidgetDefinitionBuilder {
+
+    public WidgetDefinition buildWidgetDefinition(Element widgetElement) throws Exception {
+        TreeDefinition definition = new TreeDefinition();
+        setupDefinition(widgetElement, definition);
+        definition.makeImmutable();
+        return definition;
+    }
+    
+    protected void setupDefinition(Element widgetElement, TreeDefinition definition) throws Exception {
+        super.setupDefinition(widgetElement, definition);
+        
+        // Get the optional "root-visible" attribute
+        definition.setRootVisible(DomHelper.getAttributeAsBoolean(widgetElement, "root-visible", true));
+        
+        // Get the optional "selection" attribute
+        String selection = DomHelper.getAttribute(widgetElement, "selection", null);
+        if (selection == null) {
+            // Nothing
+        } else if ("multiple".equals(selection)) {
+            definition.setSelectionModel(Tree.MULTIPLE_SELECTION);
+        } else if ("single".equals(selection)) {
+            definition.setSelectionModel(Tree.SINGLE_SELECTION);
+        } else {
+            throw new Exception("Invalid value selection value '" + selection + "' at " +
+                    DomHelper.getLocation(widgetElement));
+        }
+        
+        // Get the model optional element
+        Element modelElt = DomHelper.getChildElement(widgetElement, Constants.DEFINITION_NS, "tree-model", false);
+        if (modelElt != null) {
+            String type = DomHelper.getAttribute(modelElt, "type");
+            ServiceSelector selector =
+                (ServiceSelector)this.serviceManager.lookup(TreeModelDefinitionBuilder.ROLE + "Selector");
+            
+            TreeModelDefinitionBuilder builder = (TreeModelDefinitionBuilder)selector.select(type);
+            try {
+                definition.setModelDefinition(builder.build(modelElt));
+            } finally {
+                selector.release(builder);
+                serviceManager.release(selector);
+            }
+        }
+        
+        // parse "on-selection-changed"
+        Iterator iter = buildEventListeners(widgetElement, "on-selection-changed", TreeSelectionListener.class).iterator();
+        while (iter.hasNext()) {
+            definition.addSelectionListener((TreeSelectionListener)iter.next());
+        }
+        //TODO: allow child widgets, that will be attached to each node of the tree
+        //It may be useful to add TreeModel.getNodeType(Object) so that the container holding child
+        //widgets can have a value used by a union widget.
+    }
+}

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeModelDefinitionBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeModelDefinitionBuilder.java?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeModelDefinitionBuilder.java (added)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeModelDefinitionBuilder.java Thu Jun 16 10:17:00 2005
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.cocoon.forms.formmodel.tree.builder;
+
+import org.apache.cocoon.forms.formmodel.tree.TreeModel;
+import org.apache.cocoon.forms.formmodel.tree.TreeModelDefinition;
+import org.w3c.dom.Element;
+
+/**
+ * Builds {@link TreeModel}s from an XML description.
+ * 
+ * @version $Id$
+ */
+public interface TreeModelDefinitionBuilder {
+    
+    static final String ROLE = TreeModelDefinitionBuilder.class.getName();
+
+    TreeModelDefinition build(Element treeModelElement) throws Exception;
+}

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeModelDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/formmodel/tree/builder/TreeModelDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/forms.roles
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/forms.roles?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/forms.roles (original)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/forms.roles Thu Jun 16 10:17:00 2005
@@ -48,4 +48,8 @@
         shorthand="forms-widgetlisteners"
         default-class="org.apache.cocoon.core.container.DefaultServiceSelector"/>
 
+  <role name="org.apache.cocoon.forms.formmodel.tree.builder.TreeModelDefinitionBuilderSelector"
+        shorthand="forms-treemodels"
+        default-class="org.apache.cocoon.core.container.DefaultServiceSelector"/>
+
 </role-list>

Modified: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java (original)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/JXMacrosHelper.java Thu Jun 16 10:17:00 2005
@@ -17,6 +17,7 @@
 package org.apache.cocoon.forms.generation;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
@@ -27,6 +28,8 @@
 import org.apache.cocoon.forms.formmodel.Form;
 import org.apache.cocoon.forms.formmodel.Repeater;
 import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.cocoon.forms.formmodel.tree.Tree;
+import org.apache.cocoon.forms.formmodel.tree.TreeWalker;
 import org.apache.cocoon.forms.validation.ValidationError;
 import org.apache.cocoon.transformation.BrowserUpdateTransformer;
 import org.apache.cocoon.xml.AbstractXMLPipe;
@@ -34,6 +37,7 @@
 import org.apache.cocoon.xml.XMLConsumer;
 import org.apache.cocoon.xml.XMLUtils;
 import org.apache.commons.collections.ArrayStack;
+import org.apache.commons.lang.BooleanUtils;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 
@@ -46,10 +50,14 @@
 
     private XMLConsumer cocoonConsumer;
     private Request request;
-    private ArrayStack stack = new ArrayStack();
+    private ArrayStack widgetStack = new ArrayStack();
+    private ArrayStack pipeStack = new ArrayStack();
     private Map classes; // lazily created
-    private boolean ajaxMode;
+    private boolean ajaxRequest;
+    private boolean ajaxTemplate;
     private Set updatedWidgets;
+    private Set childUpdatedWidgets;
+    private Set parentUpdatedWidgets;
 
     /**
      * Builds and helper object, given the generator's consumer.
@@ -64,7 +72,7 @@
     public JXMacrosHelper(XMLConsumer consumer, Request request) {
         this.cocoonConsumer = consumer;
         this.request = request;
-        this.ajaxMode = request.getParameter("cocoon-ajax") != null;
+        this.ajaxRequest = request.getParameter("cocoon-ajax") != null;
     }
     
 
@@ -77,12 +85,14 @@
         if(returnForm != null) {
             return returnForm;
         }
-        throw new NullPointerException("There hasn't been passed a form object to the template!");
+        throw new NullPointerException("The template cannot find a form object");
     }
 
     public void startForm(Form form, Map attributes) throws SAXException {
         
-        this.updatedWidgets = form.getUpdatedWidgets();
+        this.updatedWidgets = form.getUpdatedWidgetIds();
+        this.childUpdatedWidgets = form.getChildUpdatedWidgetIds();
+        this.parentUpdatedWidgets = new HashSet();
         
         // build attributes
         AttributesImpl attrs = new AttributesImpl();
@@ -92,25 +102,144 @@
             attrs.addCDATAAttribute((String)entry.getKey(), (String)entry.getValue());
         }
         
-        this.ajaxMode = this.ajaxMode && "true".equals(attributes.get("ajax"));
+        this.ajaxTemplate = "true".equals(attributes.get("ajax"));
 
         this.cocoonConsumer.startPrefixMapping(Constants.INSTANCE_PREFIX, Constants.INSTANCE_NS);
         this.cocoonConsumer.startElement(Constants.INSTANCE_NS,
                                          "form-template",
                                          Constants.INSTANCE_PREFIX_COLON + "form-template",
                                          attrs);
+        // Push the form at the top of the stack
+        this.widgetStack.push(Boolean.FALSE); // Not in an updated template
+        this.widgetStack.push(form);
     }
 
     public void endForm() throws SAXException {
+        this.widgetStack.pop();
+        this.widgetStack.pop();
         this.cocoonConsumer.endElement(Constants.INSTANCE_NS,
                                        "form-template",
                                        Constants.INSTANCE_PREFIX_COLON + "form-template");
         this.cocoonConsumer.endPrefixMapping(Constants.INSTANCE_PREFIX);
         
-        this.ajaxMode = false;
+        this.ajaxTemplate = false;
         this.updatedWidgets = null;
     }
+    
+    private void startBuReplace(String id) throws SAXException {
+        AttributesImpl attr = new AttributesImpl();
+        attr.addCDATAAttribute("id", id);
+        this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attr);
+    }
+    
+    private void endBuReplace(String id) throws SAXException {
+        this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace");
+    }
+    
+    protected boolean pushWidget(String path, boolean unused) throws SAXException {
+        Widget parent = peekWidget();
+        // Is there an updated widget at a higher level in the template?
+        boolean inUpdatedTemplate = ((Boolean)widgetStack.peek(1)).booleanValue();
+        Widget widget = parent.lookupWidget(path);
+        if (widget == null) {
+            throw new IllegalArgumentException("Widget '" + parent + "' has no child named '" + path + "'");
+        }
+
+        String id = widget.getFullName();
+        boolean display;
+
+        if (widget.getState().isDisplayingValues()) {
+            //-----------------------------------------------------------------
+            // Widget is visible
+            //-----------------------------------------------------------------
+            if (ajaxRequest) {
+                // If an ajax request
+                if (this.updatedWidgets.contains(id)) {
+                    inUpdatedTemplate = true;
+                }
+                if (inUpdatedTemplate) {
+                    // Display the widget surrounded by a bu:replace
+                    startBuReplace(id);
+                    display = true;
+                } else if (this.childUpdatedWidgets.contains(id)) {
+                    // A child needs to be displayed
+                    display = true;
+                } else {
+                    // Doesn't need to be displayed
+                    display = false;
+                }
+            } else {
+                // Not an ajax request
+                if (ajaxTemplate) {
+                    // Start a bu:replace, which the bu tranformer will use to check structure
+                    // consistency and add an id attribute to its child element.
+                    startBuReplace(id);
+                }
+                // Display the widget
+                display = true;
+            }
+        } else {
+            //-----------------------------------------------------------------
+            // Widget is not visible
+            //-----------------------------------------------------------------
+            if (ajaxTemplate) {
+                // Generate a placeholder, so that the page can be updated later
+                AttributesImpl attrs = new AttributesImpl();
+                attrs.addCDATAAttribute("id", id);
+                this.cocoonConsumer.startElement(Constants.INSTANCE_NS, "placeholder", Constants.INSTANCE_PREFIX_COLON + "placeholder", attrs);
+                this.cocoonConsumer.endElement(Constants.INSTANCE_NS, "placeholder", Constants.INSTANCE_PREFIX_COLON + "placeholder");
+            }
+            // Don't display
+            display = false;
+        }
+
+        if (display) {
+            this.widgetStack.push(BooleanUtils.toBooleanObject(inUpdatedTemplate));
+            this.widgetStack.push(widget);
+        }
+        
+        return display;
+    }
+    
+    public Widget peekWidget() {
+        return (Widget)this.widgetStack.peek();
+    }
+    
+    public void popWidget() throws SAXException {
+        Widget widget = (Widget)this.widgetStack.pop();
+        boolean inUpdatedTemplate = ((Boolean)this.widgetStack.pop()).booleanValue();
+        // Note: the structure of if's below is the same as in enterWidget() above
+        // in order to more easily handle identical conditions
+        if (widget.getState().isDisplayingValues()) {
+            if (ajaxRequest) {
+                if (inUpdatedTemplate) {
+                    // Close the bu:replace
+                    endBuReplace(widget.getFullName());
+                }
+            } else {
+                if (ajaxTemplate) {
+                    endBuReplace(widget.getFullName());
+                }
+            }
+        }
+    }
+    
+    public boolean pushWidget(String path) throws SAXException {
+        return pushWidget(path, false);
+    }
+    
+    public boolean pushContainer(String path) throws SAXException {
+        return pushWidget(path, true);
+    }
 
+    public boolean pushRepeater(String path) throws SAXException {
+        boolean result = pushWidget(path, true);
+        if (!(peekWidget() instanceof Repeater)) {
+            throw new IllegalArgumentException("Widget " + peekWidget() + " is not a repeater");
+        }
+        return result;
+    }
+    
     /**
      * Get a child widget of a given widget, throwing an exception if no such child exists.
      *
@@ -127,7 +256,7 @@
                                            "' has no child named '" + path + "'");
     }
 
-    public Repeater getRepeater(Widget currentWidget, String id) {
+    private Repeater getRepeater(Widget currentWidget, String id) {
         Widget child = getWidget(currentWidget, id);
         if (child instanceof Repeater) {
             return (Repeater)child;
@@ -144,16 +273,9 @@
      * @throws SAXException
      */
     public void generateWidget(Widget widget, Locale locale) throws SAXException {
-        String id = widget.getRequestParameterName();
-        if (ajaxMode && this.updatedWidgets.contains(id)) {
-            AttributesImpl attr = new AttributesImpl();
-            attr.addCDATAAttribute("id", id);
-            this.cocoonConsumer.startElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace", attr);
-        }
         // Needs to be buffered
         RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer);
-        this.stack.push(pipe);
-        this.stack.push(widget);
+        this.pipeStack.push(pipe);
         widget.generateSaxFragment(pipe, locale);
     }
 
@@ -164,17 +286,13 @@
      * @param obj the object that is terminated (widget or validation error)
      * @throws SAXException
      */
-    public void flushRoot(Widget widget) throws SAXException {
-        Object stackObj = stack.pop();
-        if (stackObj != widget) {
-            throw new IllegalStateException("Flushing on wrong widget (expected " + stackObj +
-                                            ", got " + widget + ")");
-        }
-        ((RootBufferingPipe) stack.pop()).flushRoot();
-        String id = widget.getRequestParameterName();
-        if (ajaxMode && this.updatedWidgets.contains(id)) {
-            this.cocoonConsumer.endElement(BrowserUpdateTransformer.BU_NSURI, "replace", "bu:replace");
-        }
+    public void flushRootAndPop() throws SAXException {
+        ((RootBufferingPipe) pipeStack.pop()).flushRoot();
+        popWidget();
+    }
+
+    public void flushRoot() throws SAXException {
+        ((RootBufferingPipe) pipeStack.pop()).flushRoot();
     }
 
     public void generateWidgetLabel(Widget widget, String id) throws SAXException {
@@ -194,27 +312,11 @@
     public void generateValidationError(ValidationError error) throws SAXException {
         // Needs to be buffered
         RootBufferingPipe pipe = new RootBufferingPipe(this.cocoonConsumer);
-        this.stack.push(pipe);
-        this.stack.push(error);
+        this.pipeStack.push(pipe);
         pipe.startElement(Constants.INSTANCE_NS, VALIDATION_ERROR, Constants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR, XMLUtils.EMPTY_ATTRIBUTES);
         error.generateSaxFragment(pipe);
         pipe.endElement(Constants.INSTANCE_NS, VALIDATION_ERROR, Constants.INSTANCE_PREFIX_COLON + VALIDATION_ERROR);
     }
-    
-    /**
-     * Flush the validation error that has been stored.
-     *
-     * @param validation error that is terminated
-     * @throws SAXException
-     */
-    public void flushRoot(ValidationError error) throws SAXException {
-        Object stackObj = stack.pop();
-        if (stackObj != error) {
-            throw new IllegalStateException("Flushing on wrong validation error (expected " + stackObj +
-                                            ", got " + error + ")");
-        }
-        ((RootBufferingPipe) stack.pop()).flushRoot();
-    }    
 
     public boolean isValidationError(Object object) {
         return object instanceof ValidationError;
@@ -244,6 +346,10 @@
         return caseValue.equals(value != null ? value : "");
     }
 
+    public TreeWalker createWalker() {
+        return new TreeWalker((Tree)peekWidget());
+    }
+    
     public boolean isVisible(Widget widget) throws SAXException {
         boolean visible = widget.getCombinedState().isDisplayingValues();
         

Modified: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml (original)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/generation/jx-macros.xml Thu Jun 16 10:17:00 2005
@@ -18,6 +18,7 @@
 <!-- An implementation of the CForms template engine as a JXTemplate tag library -->
     
 <jx:template xmlns:jx="http://apache.org/cocoon/templates/jx/1.0"
+             xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
              xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
              xmlns:bu="http://apache.org/cocoon/browser-update/1.0">
     <!--
@@ -41,11 +42,11 @@
     <jx:macro name="widget" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
       <jx:parameter name="id"/>
       
-      <jx:set var="widget" value="${cformsHelper.getWidget(widget, id)}"/>
-      <jx:if test="${cformsHelper.isVisible(widget)}">
+      <jx:if test="${cformsHelper.pushWidget(id)}">
+        <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
         <jx:set var="cformsDummy" value="${cformsHelper.generateWidget(widget, locale)}"/>
         <jx:evalBody/>
-        <jx:set var="cformsDummy" value="${cformsHelper.flushRoot(widget)}"/>
+        <jx:set var="cformsDummy" value="${cformsHelper.flushRootAndPop()}"/>
       </jx:if>
     </jx:macro>
     
@@ -82,15 +83,17 @@
     -->
     <jx:macro name="repeater-widget" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
       <jx:parameter name="id"/>
-    
-      <jx:set var="repeater" value="${cformsHelper.getRepeater(widget, id)}"/>
-      <jx:if test="${cformsHelper.isVisible(repeater)}">
+
+      <jx:if test="${cformsHelper.pushRepeater(id)}">
+        <jx:set var="repeater" value="${cformsHelper.peekWidget()}"/>
         <jx:forEach varStatus="repeaterLoop" begin="0" end="${repeater.getSize() - 1}">
-          <jx:set var="widget" value="${repeater.getRow(repeaterLoop.index)}"/>
-          <jx:if test="${cformsHelper.isVisible(widget)}">
+          <jx:if test="${cformsHelper.pushContainer(java.lang.Integer.toString(repeaterLoop.index))}">
+            <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
             <jx:evalBody/>
+            <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
           </jx:if>
         </jx:forEach>
+        <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
       </jx:if>
     </jx:macro>
     
@@ -116,12 +119,13 @@
     <jx:macro name="group" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
       <jx:parameter name="id"/>
 
-      <jx:set var="widget" value="${cformsHelper.getWidget(widget, id)}"/>
-      <jx:if test="${cformsHelper.isVisible(widget)}">
+      <jx:if test="${cformsHelper.pushContainer(id)}">
+        <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
         <!--FIXME(SW): revisit fi:group
           fi:group id="${widget.getRequestParameterName()}"-->
           <jx:evalBody/>
         <!--/fi:group-->
+        <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
       </jx:if>
     </jx:macro>
 
@@ -140,11 +144,12 @@
     <jx:macro name="struct" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
       <jx:parameter name="id"/>
       
-      <jx:set var="widget" value="${cformsHelper.getWidget(widget, id)}"/>
-      <jx:if test="${cformsHelper.isVisible(widget)}">
-        <fi:struct id="${widget.getRequestParameterName()}">
+      <jx:if test="${cformsHelper.pushContainer(id)}">
+        <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
+        <fi:struct id="${widget.fullName}">
           <jx:evalBody/>
         </fi:struct>
+        <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
       </jx:if>
     </jx:macro>
     
@@ -154,22 +159,12 @@
     <jx:macro name="union" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
       <jx:parameter name="id"/>
       
-      <jx:set var="widget" value="${cformsHelper.getWidget(widget, id)}"/>
-      <jx:if test="${cformsHelper.isVisible(widget)}">
-        <jx:choose>
-          <jx:when test="${cformsHelper.isModified(widget)}">
-            <bu:replace id="${widget.getRequestParameterName()}">
-              <fi:union id="${widget.getRequestParameterName()}">
-                <jx:evalBody/>
-              </fi:union>
-            </bu:replace>
-          </jx:when>
-          <jx:otherwise>
-            <fi:union id="${widget.getRequestParameterName()}">
-              <jx:evalBody/>
-            </fi:union>
-          </jx:otherwise>
-        </jx:choose>
+      <jx:if test="${cformsHelper.pushContainer(id)}">
+        <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
+        <fi:union id="${widget.getRequestParameterName()}">
+          <jx:evalBody/>
+        </fi:union>
+        <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
       </jx:if>
     </jx:macro>
 
@@ -196,7 +191,7 @@
       <jx:if test="${cformsHelper.isValidationError(validationError)}">
           <jx:set var="cformsDummy" value="${cformsHelper.generateValidationError(validationError)}"/>
             <jx:evalBody/>
-          <jx:set var="cformsDummy" value="${cformsHelper.flushRoot(validationError)}"/>
+          <jx:set var="cformsDummy" value="${cformsHelper.flushRoot()}"/>
       </jx:if>
     </jx:macro>
 
@@ -207,41 +202,71 @@
     <jx:macro name="aggregate-widget" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
       <jx:parameter name="id"/>
       
-      <jx:set var="widget" value="${cformsHelper.getWidget(widget, id)}"/>
-      <jx:if test="${cformsHelper.isVisible(widget)}">
+      <jx:if test="${cformsHelper.pushContainer(id)}">
+        <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
         <jx:evalBody/>
+        <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
       </jx:if>
     </jx:macro>
     
     <jx:macro name="repeater" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
       <jx:parameter name="id"/>
-      <jx:set var="repeater" value="${cformsHelper.getWidget(widget, id)}"/>
-      <jx:if test="${cformsHelper.isVisible(widget)}">
-        <jx:choose>
-          <jx:when test="${cformsHelper.isModified(repeater)}">
-            <bu:replace id="${repeater.getRequestParameterName()}">
-		     <fi:repeater-template id="${repeater.getRequestParameterName()}">
-		       <jx:evalBody/>
-		     </fi:repeater-template>
-            </bu:replace>
-          </jx:when>
-          <jx:otherwise>
-	        <fi:repeater-template id="${repeater.getRequestParameterName()}">
-	          <jx:evalBody/>
-	        </fi:repeater-template>
-          </jx:otherwise>
-        </jx:choose>
-        
+      <jx:if test="${cformsHelper.pushRepeater(id)}">
+        <jx:set var="repeater" value="${cformsHelper.peekWidget()}"/>
+        <jx:evalBody/>
+        <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
       </jx:if>
     </jx:macro>
     
     <jx:macro name="repeater-rows" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
         <jx:forEach varStatus="repeaterLoop" begin="0" end="${repeater.getSize() - 1}">
-          <jx:set var="widget" value="${repeater.getRow(repeaterLoop.index)}"/>
-          <jx:if test="${cformsHelper.isVisible(widget)}">
+          <jx:if test="${cformsHelper.pushContainer(java.lang.Integer.toString(repeaterLoop.index))}">
+            <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
             <jx:evalBody/>
+            <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
           </jx:if>
         </jx:forEach>
     </jx:macro>
-        
+
+    <jx:macro name="tree" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
+      <jx:parameter name="id"/>
+      <jx:if test="${cformsHelper.pushContainer(id)}">
+        <jx:set var="widget" value="${cformsHelper.peekWidget()}"/>
+          <jx:set var="treeWidget" value="${widget}"/>
+	      <jx:set var="treeNode" value="${cformsHelper.createWalker()}"/>
+	      <jx:evalBody/>
+          <jx:set var="cformsDummy" value="${cformsHelper.popWidget()}"/>
+	  </jx:if>
+      <!-- Generate tree-management hidden fields here, i.e. inside the block element
+           that should have been produced by <ft:tree> (used by bu:replace) -->
+      <fi:field id="${widget.fullName}:action">
+        <fi:styling type="hidden"/>
+      </fi:field>
+      <fi:field id="${widget.fullName}:path">
+        <fi:styling type="hidden"/>
+      </fi:field>
+    </jx:macro>
+    
+    <jx:macro name="tree-nodes" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
+      <jx:set var="treeNodesBody" value="${macro.body}"/>
+      <jx:choose>
+        <jx:when test="${treeWidget.isRootVisible()}">
+          <!-- set current widget for root node -->
+          <jx:eval select="${treeNodesBody}"/>
+        </jx:when>
+        <jx:otherwise>
+          <ft:tree-children/>
+        </jx:otherwise>
+      </jx:choose>
+    </jx:macro>
+    
+    <jx:macro name="tree-children" targetNamespace="http://apache.org/cocoon/forms/1.0#template">
+      <jx:if test="${!treeNode.isLeaf() &amp;&amp; treeNode.isExpanded()}">
+        <jx:forEach varStatus="treeLoop" items="${treeNode.enterChildren()}">
+          <!-- also set widget when we'll have a container for each node -->
+          <jx:eval select="${treeNodesBody}"/>
+        </jx:forEach>
+        <jx:set var="cformsDummy" value="${treeNode.leave()}"/>
+      </jx:if>
+    </jx:macro>
 </jx:template>

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/collapsed.gif
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/collapsed.gif?rev=190962&view=auto
==============================================================================
Binary file - no diff available.

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/collapsed.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/expanded.gif
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/expanded.gif?rev=190962&view=auto
==============================================================================
Binary file - no diff available.

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/expanded.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/leaf.gif
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/leaf.gif?rev=190962&view=auto
==============================================================================
Binary file - no diff available.

Propchange: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/img/tree/win/leaf.gif
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js (original)
+++ cocoon/blocks/forms/trunk/java/org/apache/cocoon/forms/resources/js/cforms.js Thu Jun 16 10:17:00 2005
@@ -136,24 +136,14 @@
             // Handle browser update directives
             var doc = request.responseXML;
             if (!doc) {
-                alert("no xml answer");
+                BrowserUpdate.handleError("No xml answer", request);
                 return;
             }
         
-           BrowserUpdate.processResponse(doc);
+           BrowserUpdate.processResponse(doc, request);
        }
     } else {
-/*        var str = "";
-        for(prop in request) {
-           str += prop
-           str += " = " 
-           str += request[prop];
-           str += '\n';
-        }
-        alert(str);
-        alert(request.getAllResponseHeaders());
-*/
-        alert("request failed - status: " + request.status);
+        BrowserUpdate.handleError("Request failed - status=" + request.status, request);
     }
 }
 
@@ -244,7 +234,7 @@
 
 BrowserUpdate = {};
 
-BrowserUpdate.processResponse = function(doc) {
+BrowserUpdate.processResponse = function(doc, request) {
     var nodes = doc.documentElement.childNodes;
     for (var i = 0; i < nodes.length; i++) {
         var node = nodes[i];
@@ -260,7 +250,7 @@
             if (handlerFunc) {
                 handlerFunc(node);
             } else {
-                alert("no handler found for " + handler);
+                BrowserUpdate.handleError("No handler found for element " + handler, request);
             }
         }
     }
@@ -272,7 +262,7 @@
            alert("no id found on update element");
            return;
         }    
-        // Get the first child element (the first child not may be some text!)
+        // Get the first child element (the first child may be some text!)
         var firstChild = DOMUtils.firstChildElement(element);
     
         var oldElement = document.getElementById(id);
@@ -294,6 +284,21 @@
         }
     }
 }
+
+BrowserUpdate.handleError = function(message, request) {
+    if (confirm(message + "\nShow server response?")) {
+        var w = window.open(undefined, "Cocoon Error", "location=no");
+        if (w == undefined) {
+            alert("You must allow popups from this server to display the response.");
+        } else {
+	        var doc = w.document;
+	        doc.open();
+	        doc.write(request.responseText);
+	        doc.close();
+	    }
+    }
+}
+
 
 //-------------------------------------------------------------------------------------------------
 // Fader used to highlight page areas that have been updated

Modified: cocoon/blocks/forms/trunk/samples/flow/forms_flow_example.js
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/flow/forms_flow_example.js?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/samples/flow/forms_flow_example.js (original)
+++ cocoon/blocks/forms/trunk/samples/flow/forms_flow_example.js Thu Jun 16 10:17:00 2005
@@ -147,6 +147,23 @@
     );
 }
 
+function do_fileExplorer() {
+    var form = new Form("forms/file_explorer_model.xml");
+    form.showForm("file_explorer-display-pipeline.jx");
+    cocoon.sendPage("xmlresult-display-pipeline.jx",
+        {title: "Tree binding is not yet done", document: null}
+    );
+}
+function do_sampleTree() {
+    var form = new Form("forms/sampletree_model.xml");
+    var resolver = cocoon.getComponent(org.apache.excalibur.source.SourceResolver.ROLE);
+    var model = new org.apache.cocoon.forms.formmodel.tree.SourceTreeModel(resolver, "context://samples/blocks/forms");
+    form.getChild("files").setModel(model);
+    form.showForm("sampletree-display-pipeline.jx");
+    cocoon.sendPage("xmlresult-display-pipeline.jx",
+        {title: "Tree binding is not yet done", document: null}
+    );
+}
 function do_xdoceditor() {
     var form = new Form("forms/xdoceditor.xml");
     form.showForm("xdoceditor-display-pipeline.jx");

Modified: cocoon/blocks/forms/trunk/samples/forms/datasource_chooser_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/datasource_chooser_template.xml?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/datasource_chooser_template.xml (original)
+++ cocoon/blocks/forms/trunk/samples/forms/datasource_chooser_template.xml Thu Jun 16 10:17:00 2005
@@ -32,7 +32,7 @@
         </legend>
         <ft:union id="datasource">
           <ft:case id="">
-            Please choose a datasource type.
+            <span>Please choose a datasource type.</span>
           </ft:case>
           <ft:case id="SQL">
             <ft:group id="SQL">
@@ -48,7 +48,7 @@
           </ft:case>
           <ft:case id="file">
             <ft:group id="file">
-              File name: <ft:widget id="filename"/>
+              <div>File name: <ft:widget id="filename"/></div>
             </ft:group>
           </ft:case>
         </ft:union>

Modified: cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater.xml?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater.xml (original)
+++ cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater.xml Thu Jun 16 10:17:00 2005
@@ -76,7 +76,7 @@
       <fd:label>Remove selected contacts</fd:label>
     </fd:repeater-action>
 
-    <fd:submit id="submit">
+    <fd:submit id="ok">
       <fd:label>Submit</fd:label>
     </fd:submit>
   </fd:widgets>

Modified: cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater_template.xml?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater_template.xml (original)
+++ cocoon/blocks/forms/trunk/samples/forms/dynamicrepeater_template.xml Thu Jun 16 10:17:00 2005
@@ -48,6 +48,7 @@
       <li>when there are some contacts, the "move up" and "move down" buttons are not shown on the first and last lines, respectively.</li>
     </ul>
       <ft:repeater id="contacts">
+        <div id="contacts">
         <jx:choose>
           <jx:when test="${repeater.getSize() == 0}">
             <p><strong><em>There are no contacts to display</em></strong></p>
@@ -99,13 +100,14 @@
                 </jx:otherwise>
               </jx:choose>
              <p>
-                  <ft:widget id="addcontact"/>
+                  <ft:widget id="../addcontact"/>
                   <jx:if test="${widget.getChild('contacts').getSize() > 0}">
-                    <ft:widget id="removecontacts"/>
+                    <ft:widget id="../removecontacts"/>
                   </jx:if>
               </p>
+              </div>
             </ft:repeater>
-            <ft:widget id="submit"/><br/>
+            <ft:widget id="ok"/><br/>
             <a href="./do-dynaRepeater.flow">Restart this sample</a> - <a href="./">Back to samples</a>
 
     </ft:form-template>

Added: cocoon/blocks/forms/trunk/samples/forms/file_explorer_model.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/file_explorer_model.xml?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/file_explorer_model.xml (added)
+++ cocoon/blocks/forms/trunk/samples/forms/file_explorer_model.xml Thu Jun 16 10:17:00 2005
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!--
+  Copyright 1999-2005 The Apache Software Foundation
+
+  Licensed 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.
+-->
+
+<!--
+  @version $Id$
+-->
+
+<fd:form xmlns:fd="http://apache.org/cocoon/forms/1.0#definition">
+
+  <fd:widgets>    
+    <fd:tree id="directories" root-visible="true" selection="single">
+      <fd:tree-model type="source" src="context://samples/blocks/forms">
+        <!-- keep only directories -->
+        <fd:fileset>
+          <fd:exclude pattern="*"/>
+        </fd:fileset>
+	  </fd:tree-model>
+
+      <fd:on-selection-changed>
+         <fd:javascript>
+            if (event.isAddedPath()) {
+			   // Change the directory displayed in the file panel
+			   var source = event.tree.model.getNode(event.path);
+                var filesPanel = event.source.lookupWidget("../files");
+			   filesPanel.model.setRootSource(source);
+		    }
+		 </fd:javascript>
+	  </fd:on-selection-changed>
+    </fd:tree>
+
+    <fd:tree id="files" root-visible="false">
+	  <fd:tree-model type="source" src="context://samples/blocks/forms"/>
+      <fd:on-selection-changed>
+        <fd:javascript>
+          var files = event.source;
+          var model = files.model;
+          var messages = event.source.lookupWidget("../messages");
+          var count = event.source.selectionCount;
+          if (count == 0) {
+              messages.setValue("No selection");
+          } else if (count == 1) {
+              var src = model.getNode(files.selectionPath);
+              messages.setValue(src.name + " - " + src.contentLength + " bytes");
+          } else {
+              messages.setValue(count + " files selected");
+          }
+        </fd:javascript>
+      </fd:on-selection-changed>
+    </fd:tree>
+    
+    <fd:output id="messages">
+      <fd:initial-value>No selection</fd:initial-value>
+      <fd:datatype base="string"/>
+    </fd:output>
+  </fd:widgets>
+
+</fd:form>

Propchange: cocoon/blocks/forms/trunk/samples/forms/file_explorer_model.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/samples/forms/file_explorer_model.xml
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/samples/forms/file_explorer_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/file_explorer_template.xml?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/file_explorer_template.xml (added)
+++ cocoon/blocks/forms/trunk/samples/forms/file_explorer_template.xml Thu Jun 16 10:17:00 2005
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!--
+  Copyright 1999-2005 The Apache Software Foundation
+
+  Licensed 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.
+-->
+<page xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
+      xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
+
+  <!-- Import the macros that define CForms template elements -->
+  <jx:import uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>
+
+  <!-- A macro that displays the toggle icons (plus/minus) for the current treeNode -->
+  <jx:macro name="toggle-icon">
+	  <jx:choose>
+	    <jx:when test="${treeNode.isLeaf()}">
+	      <img src="resources/img/tree/win/${treeNode.iconType}.gif" border="0"/>
+	    </jx:when>
+	    <jx:otherwise>
+	       <a href="#" onclick="return TreeToggleCollapse('${widget.fullName}', '${treeNode.path}')"><img
+	         src="resources/img/tree/win/${treeNode.iconType}.gif" border="0"/></a>
+	    </jx:otherwise>
+	  </jx:choose>
+  </jx:macro>
+  
+  <title>File explorer</title>
+  <style type="text/css">
+    #files td { border: dotted black 1px; }
+    .selected { background: #D0D0D0; }
+  </style>
+  <content>
+    <script language="JavaScript">
+      function TreeAction(id, act, path) {
+        var elt = document.getElementById(id);
+        if (!elt) {
+            alert("Error: cannot find element with id '" + id + "'");
+            return;
+        }
+        var form = forms_getForm(elt);
+        var actInput = id + ":action";
+        var pathInput = id + ":path";
+        form[actInput].value = act
+        form[pathInput].value = path;
+        forms_submitForm(elt, id);
+        // Reset fields (this form may be reposted later when in Ajax mode)
+        form[actInput].value = "";
+        form[pathInput].value = "";
+        return false;
+      }
+      function TreeToggleCollapse(id, path) {
+        return TreeAction(id, "toggle-collapse", path);
+      }
+      
+      function TreeSelect(id, path) {
+        return TreeAction(id, "select", path);
+      }
+      
+      function TreeToggleSelect(id, path) {
+        return TreeAction(id, "toggle-select", path);
+      }
+      
+      // Trees are fully-refreshed for now, and highlighting them isn't nice
+      BrowserUpdate.highlight = null;
+    </script>
+    <ft:form-template action="continue" method="POST" ajax="true">
+      <ft:continuation-id/>
+      <table>
+          <tr valign="top">
+              
+	  <ft:tree id="directories">
+        <td>
+          <ft:tree-nodes>
+            <div style="margin-left: 15px">
+              <toggle-icon/>
+              <a class="${treeNode.selectionType}" href="#" onclick="return TreeSelect('${treeWidget.fullName}', '${treeNode.path}')">
+                ${treeNode.node.name}</a>
+                <ft:tree-children/>
+            </div>
+          </ft:tree-nodes>
+        </td>
+      </ft:tree>
+
+      <ft:tree id="files">
+        <td>
+          <table>
+            <ft:tree-nodes>
+              <tr class="${treeNode.selectionType}">
+                <td>
+                    <a class="${treeNode.selectionType}" href="#" onclick="return TreeToggleSelect('${treeWidget.fullName}', '${treeNode.path}')">${treeNode.node.name}</a>
+                </td>
+                <td><jx:formatDate value="${java.util.Date(treeNode.node.lastModified)}"
+                           pattern="yyyy-dd-MM hh:mm:ss"/></td>
+              </tr>
+            </ft:tree-nodes>
+          </table>
+        </td>
+      </ft:tree>
+
+           </tr>
+      </table>
+      <p>
+        <ft:widget id="messages"/>
+      </p>
+      <a href="./do-fileExplorer.flow">Restart this sample</a> -  <a href="./">Back to samples</a>
+
+    </ft:form-template>
+  </content>
+</page>

Propchange: cocoon/blocks/forms/trunk/samples/forms/file_explorer_template.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/samples/forms/file_explorer_template.xml
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/samples/forms/sampletree_model.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/sampletree_model.xml?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/sampletree_model.xml (added)
+++ cocoon/blocks/forms/trunk/samples/forms/sampletree_model.xml Thu Jun 16 10:17:00 2005
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+<!--
+  Copyright 1999-2005 The Apache Software Foundation
+
+  Licensed 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.
+-->
+
+<!--
+  @version $Id$
+-->
+
+<fd:form xmlns:fd="http://apache.org/cocoon/forms/1.0#definition"
+         xmlns:i18n="http://apache.org/cocoon/i18n/2.1">
+
+  <fd:widgets>
+    <fd:field id="name" required="true">
+      <fd:datatype base="string"/>
+    </fd:field>
+    
+    <fd:tree id="tree" root-visible="true">
+      <fd:tree-model type="java" class="org.apache.cocoon.forms.formmodel.tree.DefaultTreeModel$Sample"/>
+      <!-- no tree model given, to see the default one -->
+      <fd:ttree-model type="source" src="context://samples/">
+        <fd:fileset>
+          <!--fd:exclude pattern=".*"/-->
+        </fd:fileset>
+      </fd:ttree-model>
+    </fd:tree>
+
+    <fd:tree id="tree2">
+      <!-- sample tree model -->
+      <fd:tree-model type="java" class="org.apache.cocoon.forms.formmodel.tree.DefaultTreeModel$Sample"/>
+    </fd:tree>
+
+    <fd:tree id="files" root-visible="false">
+      <!-- model is set by the flowscript -->
+      <fd:on-selection-changed>
+		  <fd:javascript>
+			  print("Click click " + event.path);
+			  print("Target url = " + event.tree.model.getNode(event.path).URI);
+		  </fd:javascript>
+	  </fd:on-selection-changed>
+    </fd:tree>
+
+    <fd:submit id="ok">
+      <fd:label>OK</fd:label>
+    </fd:submit>
+    
+  </fd:widgets>
+
+</fd:form>

Propchange: cocoon/blocks/forms/trunk/samples/forms/sampletree_model.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/samples/forms/sampletree_model.xml
------------------------------------------------------------------------------
    svn:keywords = Id

Added: cocoon/blocks/forms/trunk/samples/forms/sampletree_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/sampletree_template.xml?rev=190962&view=auto
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/sampletree_template.xml (added)
+++ cocoon/blocks/forms/trunk/samples/forms/sampletree_template.xml Thu Jun 16 10:17:00 2005
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<!--
+  Copyright 1999-2005 The Apache Software Foundation
+
+  Licensed 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.
+-->
+<page xmlns:ft="http://apache.org/cocoon/forms/1.0#template"
+      xmlns:fi="http://apache.org/cocoon/forms/1.0#instance"
+      xmlns:jx="http://apache.org/cocoon/templates/jx/1.0">
+
+  <!-- Import the macros that define CForms template elements -->
+  <jx:import uri="resource://org/apache/cocoon/forms/generation/jx-macros.xml"/>
+  
+  <!-- A macro that displays the toggle icons (plus/minus) for the current treeNode -->
+  <jx:macro name="toggle-icon">
+	  <jx:choose>
+	    <jx:when test="${treeNode.isLeaf()}">
+	      <img src="resources/img/tree/win/${treeNode.iconType}.gif" border="0"/>
+	    </jx:when>
+	    <jx:otherwise>
+	       <a href="#" onclick="return TreeToggleCollapse('${widget.fullName}', '${treeNode.path}')"><img
+	         src="resources/img/tree/win/${treeNode.iconType}.gif" border="0"/></a>
+	    </jx:otherwise>
+	  </jx:choose>
+  </jx:macro>
+  
+  <title>Datasource chooser</title>
+  <style type="text/css">
+    #files td { border: dotted black 1px; }
+    .selected { background: #D0D0D0; }
+  </style>
+  <content>
+    <script language="JavaScript">
+      function TreeAction(id, act, path) {
+        var elt = document.getElementById(id);
+        if (!elt) {
+            alert("Error: cannot find element with id '" + id + "'");
+            return;
+        }
+        var form = forms_getForm(elt);
+        var actInput = id + ":action";
+        var pathInput = id + ":path";
+        form[actInput].value = act
+        form[pathInput].value = path;
+        forms_submitForm(elt, id);
+        // Reset fields (this form may be reposted later when in Ajax mode)
+        form[actInput].value = "";
+        form[pathInput].value = "";
+        return false;
+      }
+      function TreeToggleCollapse(id, path) {
+        return TreeAction(id, "toggle-collapse", path);
+      }
+      
+      function TreeToggleSelect(id, path) {
+        return TreeAction(id, "toggle-select", path);
+      }
+      
+      // Trees are fully-refreshed for now, and highlighting them isn't nice
+      BrowserUpdate.highlight = null;
+    </script>
+    <ft:form-template action="#{$cocoon/continuation/id}.continue" method="get" ajax="true">
+      Name: <ft:widget id="name"/>
+      <br/>
+      <ft:tree id="tree">
+      <div style="float: right; border: dotted black 1px">
+        Article list:
+        <ul>
+          <ft:tree-nodes>
+            <li>${treeNode.node.data}
+              <jx:choose>
+                <jx:when test="${treeNode.isLeaf()}"/>
+                <jx:otherwise>
+                  (<a href="#" onclick="return TreeToggleCollapse('${widget.fullName}', '${treeNode.path}')">
+                  <!-- Jexl is really too limited, and has no conditional expression. It would
+                       be so cleaner to write  ${treeNode.isExpanded() ? "close" : "details"}... -->
+                  <jx:choose>
+                    <jx:when test="${treeNode.isExpanded()}">close</jx:when>
+                    <jx:otherwise>details</jx:otherwise>
+                  </jx:choose>
+                  </a>)
+	              <ul>
+	                <ft:tree-children/>
+	              </ul>
+	            </jx:otherwise>
+	          </jx:choose>
+            </li>
+          </ft:tree-nodes>
+        </ul>
+      </div>
+      </ft:tree>
+      <br/>
+      <ft:tree id="tree2">
+      <div>
+        Tree sample:
+        ${java.lang.System.err.println("In tree2")}
+          <ft:tree-nodes>
+            <div style="margin-left: 15px"><toggle-icon/>${treeNode.node.data}
+              <ft:tree-children/>
+            </div>
+          </ft:tree-nodes>
+      </div>
+      </ft:tree>
+      
+      Example of a tree-table
+      <ft:tree id="files">
+        <table>
+        ${java.lang.System.err.println("In files")}
+          <tr><th></th><th>Name</th><th>Last modified</th></tr>
+          <ft:tree-nodes>
+            <tr>
+              <td>
+                <jx:choose>
+                  <jx:when test="${treeNode.selected}">
+                    <input type="checkbox" checked="checked" name="${widget.fullName}:select"/>
+                  </jx:when>
+                  <jx:otherwise>
+                    <input type="checkbox" name="${widget.fullId}$select"/>
+                  </jx:otherwise>
+                </jx:choose>
+              </td>
+              <td>
+                <span style="margin-left: ${treeNode.depth * 15}px"><toggle-icon/></span>
+                <a class="${treeNode.selectionType}" href="#" onclick="return TreeToggleSelect('${treeWidget.fullName}', '${treeNode.path}')">
+                  ${treeNode.node.name}</a>
+              </td>
+              <td><jx:formatDate value="${java.util.Date(treeNode.node.lastModified)}" pppattern="yyyy-dd-MM hh:mm:ss"/></td>
+            </tr>
+            <ft:tree-children/>
+          </ft:tree-nodes>
+        </table>
+      </ft:tree>
+      
+      <ft:widget id="ok"/>
+      <br/>
+      <a href="./do-sampleTree.flow">Restart this sample</a> -  <a href="./">Back to samples</a>
+
+    </ft:form-template>
+  </content>
+</page>

Propchange: cocoon/blocks/forms/trunk/samples/forms/sampletree_template.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/blocks/forms/trunk/samples/forms/sampletree_template.xml
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: cocoon/blocks/forms/trunk/samples/forms/tasktree_template.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/forms/tasktree_template.xml?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/samples/forms/tasktree_template.xml (original)
+++ cocoon/blocks/forms/trunk/samples/forms/tasktree_template.xml Thu Jun 16 10:17:00 2005
@@ -65,7 +65,7 @@
     
   </style>
   <content>
-    <ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST" ajax="true">
+    <ft:form-template action="#{$cocoon/continuation/id}.continue" method="POST" ajax="false">
     
       <ft:class id="task-class">
         <div class="section">
@@ -91,6 +91,7 @@
         <!-- Show the "Add subtask" button only if there are currently no subtasks.
              If there are some, more subtasks can be added using the "+" button -->
         <ft:repeater id="tasks">
+          <div>
           <jx:choose>
             <jx:when test="${repeater.getSize() > 0}">
               <ft:repeater-rows>
@@ -100,13 +101,14 @@
               </ft:repeater-rows>
             </jx:when>
             <jx:otherwise>
-            <ft:widget id="addsub">
-              <fi:styling type="link"/>
-            </ft:widget>
           </jx:otherwise>
         </jx:choose>
+          </div>
         </ft:repeater>
-      </ft:class>
+             <ft:widget id="addsub">
+              <fi:styling type="link"/>
+            </ft:widget>
+     </ft:class>
       
       Project name: <ft:widget id="name"/>
       

Modified: cocoon/blocks/forms/trunk/samples/welcome.xml
URL: http://svn.apache.org/viewcvs/cocoon/blocks/forms/trunk/samples/welcome.xml?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/blocks/forms/trunk/samples/welcome.xml (original)
+++ cocoon/blocks/forms/trunk/samples/welcome.xml Thu Jun 16 10:17:00 2005
@@ -112,6 +112,15 @@
    </sample>
  </group>
  
+ <group name="Tree widget samples">
+   <sample name="File explorer" href="do-fileExplorer.flow">
+     (Ajax) A file explorer built with two tree widgets.
+   </sample>
+   <sample name="Sample tree" href="do-sampleTree.flow">
+     (Ajax) Scratchpad for random samples of the Tree widgets.
+   </sample>
+ </group>
+ 
  <group name="Dynamic repeater template and event handling">
     <sample name="Dynamic repeater template" href="dreamteam">
       Create your Euro 2004 soccer dream team

Modified: cocoon/trunk/status.xml
URL: http://svn.apache.org/viewcvs/cocoon/trunk/status.xml?rev=190962&r1=190961&r2=190962&view=diff
==============================================================================
--- cocoon/trunk/status.xml (original)
+++ cocoon/trunk/status.xml Thu Jun 16 10:17:00 2005
@@ -197,6 +197,11 @@
 
   <changes>
   <release version="@version@" date="@date@">
+    <action dev="SW" type="add">
+  	   CForms block: new Tree widget, heavily inspired by Swing's JTree. Features Ajax, selection listeners,
+      and a lightweight data model with two implementations: a generic one, and a source-based one to build
+      file explorers.
+    </action>     
     <action dev="CZ" type="fix">
       Fix a huge memory leak in error pipelines that was caused by unreleased components.
     </action>     



Mime
View raw message