You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2012/02/05 23:02:55 UTC

[4/9] moving old tree examples away, merged recursive panel example into panel compref

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-examples/src/main/java/org/apache/wicket/examples/tree/content/SelectableFolderContent.java
----------------------------------------------------------------------
diff --cc wicket-examples/src/main/java/org/apache/wicket/examples/tree/content/SelectableFolderContent.java
index 0000000,0000000..b2605d5
new file mode 100644
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/tree/content/SelectableFolderContent.java
@@@ -1,0 -1,0 +1,111 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.examples.tree.content;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.examples.tree.Foo;
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.extensions.markup.html.repeater.tree.content.Folder;
++import org.apache.wicket.model.IModel;
++
++/**
++ * @author Sven Meier
++ */
++public class SelectableFolderContent extends Content
++{
++
++	private static final long serialVersionUID = 1L;
++
++	private ITreeProvider<Foo> provider;
++
++	private IModel<Foo> selected;
++
++	public SelectableFolderContent(ITreeProvider<Foo> provider)
++	{
++		this.provider = provider;
++	}
++
++	@Override
++	public void detach()
++	{
++		if (selected != null)
++		{
++			selected.detach();
++		}
++	}
++
++	protected boolean isSelected(Foo foo)
++	{
++		IModel<Foo> model = provider.model(foo);
++
++		try
++		{
++			return selected != null && selected.equals(model);
++		}
++		finally
++		{
++			model.detach();
++		}
++	}
++
++	protected void select(Foo foo, AbstractTree<Foo> tree, final AjaxRequestTarget target)
++	{
++		if (selected != null)
++		{
++			tree.updateNode(selected.getObject(), target);
++
++			selected.detach();
++			selected = null;
++		}
++
++		selected = provider.model(foo);
++
++		tree.updateNode(foo, target);
++	}
++
++	@Override
++	public Component newContentComponent(String id, final AbstractTree<Foo> tree, IModel<Foo> model)
++	{
++		return new Folder<Foo>(id, tree, model)
++		{
++			private static final long serialVersionUID = 1L;
++
++			/**
++			 * Always clickable.
++			 */
++			@Override
++			protected boolean isClickable()
++			{
++				return true;
++			}
++
++			@Override
++			protected void onClick(AjaxRequestTarget target)
++			{
++				SelectableFolderContent.this.select(getModelObject(), tree, target);
++			}
++
++			@Override
++			protected boolean isSelected()
++			{
++				return SelectableFolderContent.this.isSelected(getModelObject());
++			}
++		};
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
----------------------------------------------------------------------
diff --cc wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
index 5cc23cc,5cc23cc..165389f
--- a/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
+++ b/wicket-examples/src/main/resources/org/apache/wicket/examples/homepage/HomePage.html
@@@ -30,8 -30,8 +30,8 @@@
  		<tr><td align="right"><a href="unicodeconverter">unicode converter</a></td><td> - Converts input using some model magic.</td></tr>
          <tr><td align="right"><a href="niceurl">niceurl</a></td><td> - Demonstrates the use of "nice" URLs.</td></tr>
  		<tr><td align="right"><a href="ajax">ajax</a></td><td> - Examples using wicket's built-in AJAX.</td></tr>
--        <tr><td align="right"><a href="nested">nested</a></td><td> - Trees and nested lists.</td></tr>
          <tr><td align="right"><a href="repeater">repeaters</a></td><td> - DataView, DataTable, GridView component examples.</td></tr>
++        <tr><td align="right"><a href="tree">tree</a></td><td> - Trees.</td></tr>
          <tr><td align="right"><a href="authentication1">authentication-1</a></td><td> - A very simple authentication example.</td></tr>
          <tr><td align="right"><a href="authentication2">authentication-2</a></td><td> - A little bit more advanced authentication example (using cookies).</td></tr>
  		<tr><td align="right"><a href="authentication3">authentication-3</a></td><td> - Demonstrates authentication for pages.</td></tr>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-examples/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --cc wicket-examples/src/main/webapp/WEB-INF/web.xml
index 3b9c848,3b9c848..f52ca6a
--- a/wicket-examples/src/main/webapp/WEB-INF/web.xml
+++ b/wicket-examples/src/main/webapp/WEB-INF/web.xml
@@@ -144,15 -144,15 +144,6 @@@
  	</filter>
  
  	<filter>
--		<filter-name>NestedApplication</filter-name>
--		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
--		<init-param>
--		  <param-name>applicationClassName</param-name>
--		  <param-value>org.apache.wicket.examples.nested.NestedApplication</param-value>
--		</init-param>
--	</filter>
--
--	<filter>
  		<filter-name>GuestBookApplication</filter-name>
  		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
  		<init-param>
@@@ -261,6 -261,6 +252,15 @@@
  	</filter>
  
  	<filter>
++		<filter-name>TreeApplication</filter-name>
++		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
++		<init-param>
++			<param-name>applicationClassName</param-name>
++			<param-value>org.apache.wicket.examples.tree.TreeApplication</param-value>
++		</init-param>
++	</filter>
++
++	<filter>
  		<filter-name>NiceUrlApplication</filter-name>
  		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
  		<init-param>
@@@ -474,6 -474,6 +474,13 @@@
  	</filter-mapping>
  
  	<filter-mapping>
++		<filter-name>TreeApplication</filter-name>
++		<url-pattern>/tree/*</url-pattern>
++        <dispatcher>REQUEST</dispatcher>
++        <dispatcher>INCLUDE</dispatcher>
++	</filter-mapping>
++
++	<filter-mapping>
  		<filter-name>NiceUrlApplication</filter-name>
  		<url-pattern>/niceurl/*</url-pattern>
          <dispatcher>REQUEST</dispatcher>
@@@ -495,13 -495,13 +502,6 @@@
  	</filter-mapping>
  
  	<filter-mapping>
--		<filter-name>NestedApplication</filter-name>
--		<url-pattern>/nested/*</url-pattern>
--        <dispatcher>REQUEST</dispatcher>
--        <dispatcher>INCLUDE</dispatcher>
--	</filter-mapping>
--	 
--	<filter-mapping>
  		<filter-name>PubApplication</filter-name>
  		<url-pattern>/pub/*</url-pattern>
          <dispatcher>REQUEST</dispatcher>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-examples/src/main/webapp/style.css
----------------------------------------------------------------------
diff --cc wicket-examples/src/main/webapp/style.css
index 5e48de1,5e48de1..ea81568
--- a/wicket-examples/src/main/webapp/style.css
+++ b/wicket-examples/src/main/webapp/style.css
@@@ -43,6 -43,6 +43,11 @@@ h1,h2 
  	padding: 10px 10px 10px 10px;
  }
  
++.panel {
++  border: 2px dotted #fc0;
++  padding: 5px;
++}
++
  .mark {
  	background-color: yellow;
  	margin: 5px;	

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/data/table/DataTable.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/data/table/DataTable.java
index 53753e0,53753e0..8843090
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/data/table/DataTable.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/data/table/DataTable.java
@@@ -92,15 -92,15 +92,7 @@@ public class DataTable<T> extends Pane
  			String className = getCssClass();
  			if (!Strings.isEmpty(className))
  			{
--				CharSequence oldClassName = tag.getAttribute("class");
--				if (Strings.isEmpty(oldClassName))
--				{
--					tag.put("class", className);
--				}
--				else
--				{
--					tag.put("class", oldClassName + " " + className);
--				}
++				tag.append("class", className, " ");
  			}
  		}
  	}
@@@ -155,7 -155,7 +147,7 @@@
  				final IColumn<T> column = DataTable.this.columns.get(index);
  				if (column instanceof IStyledColumn)
  				{
--					item.add(new DataTable.CssAttributeBehavior()
++					item.add(new CssAttributeBehavior()
  					{
  						private static final long serialVersionUID = 1L;
  

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/AbstractTree.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/AbstractTree.java
index 0000000,0000000..2dd8425
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/AbstractTree.java
@@@ -1,0 -1,0 +1,343 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import java.util.Set;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.extensions.markup.html.repeater.tree.util.ProviderSubset;
++import org.apache.wicket.markup.html.panel.Panel;
++import org.apache.wicket.markup.repeater.DefaultItemReuseStrategy;
++import org.apache.wicket.markup.repeater.IItemReuseStrategy;
++import org.apache.wicket.markup.repeater.Item;
++import org.apache.wicket.model.IModel;
++import org.apache.wicket.util.visit.IVisit;
++import org.apache.wicket.util.visit.IVisitor;
++
++/**
++ * Abstract base class for {@link NestedTree} and {@link TableTree}. Uses its model for storing the
++ * {@link State} of its nodes.
++ * 
++ * Note that a tree has no notion of a <em>selection</em>. Handling state of nodes besides
++ * expanse/collapse is irrelevant to a tree implementation.
++ * 
++ * @see #newContentComponent(String, IModel)
++ * 
++ * @author svenmeier
++ * @param <T>
++ *            the model object type
++ */
++public abstract class AbstractTree<T> extends Panel
++{
++	private static final long serialVersionUID = 1L;
++
++	private ITreeProvider<T> provider;
++
++	private IItemReuseStrategy itemReuseStrategy;
++
++	protected AbstractTree(String id, ITreeProvider<T> provider)
++	{
++		this(id, provider, null);
++	}
++
++	protected AbstractTree(String id, ITreeProvider<T> provider, IModel<Set<T>> state)
++	{
++		super(id, state);
++
++		if (provider == null)
++		{
++			throw new IllegalArgumentException("argument [provider] cannot be null");
++		}
++		this.provider = provider;
++
++		// see #updateBranch(Object, AjaxRequestTarget)
++		setOutputMarkupId(true);
++	}
++
++	/**
++	 * Sets the item reuse strategy. This strategy controls the creation of {@link Item}s.
++	 * 
++	 * @see IItemReuseStrategy
++	 * 
++	 * @param strategy
++	 *            item reuse strategy
++	 * @return this for chaining
++	 */
++	public AbstractTree<T> setItemReuseStrategy(IItemReuseStrategy strategy)
++	{
++		this.itemReuseStrategy = strategy;
++
++		return this;
++	}
++
++	/**
++	 * @return currently set item reuse strategy. Defaults to <code>DefaultItemReuseStrategy</code>
++	 *         if none was set.
++	 * 
++	 * @see DefaultItemReuseStrategy
++	 */
++	public IItemReuseStrategy getItemReuseStrategy()
++	{
++		if (itemReuseStrategy == null)
++		{
++			return DefaultItemReuseStrategy.getInstance();
++		}
++		return itemReuseStrategy;
++	}
++
++	/**
++	 * Get the provider of the tree nodes.
++	 * 
++	 * @return provider
++	 */
++	public ITreeProvider<T> getProvider()
++	{
++		return provider;
++	}
++
++	/**
++	 * Delegate to {@link #newModel()} if none is inited in super implementation.
++	 */
++	@Override
++	protected IModel<?> initModel()
++	{
++		IModel<?> model = super.initModel();
++
++		if (model == null)
++		{
++			model = newModel();
++		}
++
++		return model;
++	}
++
++	/**
++	 * Factory method for a model, by default creates a model containing a {@link ProviderSubset}.
++	 * 
++	 * @return model for this tree
++	 */
++	protected IModel<Set<T>> newModel()
++	{
++		return new ProviderSubset<T>(provider).createModel();
++	}
++
++	/**
++	 * Get the model of this tree.
++	 * 
++	 * @return model
++	 */
++	@SuppressWarnings("unchecked")
++	public IModel<Set<T>> getModel()
++	{
++		return (IModel<Set<T>>)getDefaultModel();
++	}
++
++	/**
++	 * Get the model object of this tree.
++	 * 
++	 * @return the model object
++	 */
++	public Set<T> getModelObject()
++	{
++		return getModel().getObject();
++	}
++
++	/**
++	 * Set the model.
++	 * 
++	 * @param model
++	 *            the model
++	 */
++	public void setModel(IModel<Set<T>> model)
++	{
++		setDefaultModel(model);
++	}
++
++	/**
++	 * Set the model object.
++	 * 
++	 * @param state
++	 *            the model object
++	 */
++	public void setModelObject(Set<T> state)
++	{
++		setDefaultModelObject(state);
++	}
++
++	/**
++	 * Expand the given node, tries to update the affected branch if the change happens on an
++	 * {@link AjaxRequestTarget}.
++	 * 
++	 * @param t
++	 *            the node to expand
++	 * 
++	 * @see #getModelObject()
++	 * @see Set#add(Object)
++	 * @see #updateBranch(Object, AjaxRequestTarget)
++	 */
++	public void expand(T t)
++	{
++		getModelObject().add(t);
++
++		updateBranch(t, AjaxRequestTarget.get());
++	}
++
++	/**
++	 * Collapse the given node, tries to update the affected branch if the change happens on an
++	 * {@link AjaxRequestTarget}.
++	 * 
++	 * @param t
++	 *            the object to collapse
++	 * 
++	 * @see #getModelObject()
++	 * @see Set#remove(Object)
++	 * @see #updateBranch(Object, AjaxRequestTarget)
++	 */
++	public void collapse(T t)
++	{
++		getModelObject().remove(t);
++
++		updateBranch(t, AjaxRequestTarget.get());
++	}
++
++	/**
++	 * Get the given node's {@link State}.
++	 * 
++	 * @param t
++	 *            the node to get state for
++	 * @return state
++	 * 
++	 * @see #getModelObject()
++	 * @see Set#contains(Object)
++	 */
++	public State getState(T t)
++	{
++		if (getModelObject().contains(t))
++		{
++			return State.EXPANDED;
++		}
++		else
++		{
++			return State.COLLAPSED;
++		}
++	}
++
++	/**
++	 * Overriden to detach the {@link ITreeProvider}.
++	 */
++	@Override
++	protected void onDetach()
++	{
++		provider.detach();
++
++		super.onDetach();
++	}
++
++	/**
++	 * Create a new component for a node.
++	 * 
++	 * @param id
++	 *            the component id
++	 * @param model
++	 *            the model containing the node
++	 * @return created component
++	 */
++	public Component newNodeComponent(String id, final IModel<T> model)
++	{
++		return new Node<T>(id, this, model)
++		{
++			private static final long serialVersionUID = 1L;
++
++			@Override
++			protected Component createContent(String id, IModel<T> model)
++			{
++				return AbstractTree.this.newContentComponent(id, model);
++			}
++		};
++	}
++
++	/**
++	 * Create a new component for the content of a node.
++	 * 
++	 * @param id
++	 *            the component id
++	 * @param model
++	 *            the model containing the node
++	 * @return created component
++	 */
++	protected abstract Component newContentComponent(String id, IModel<T> model);
++
++	/**
++	 * Convenience method to update a single branch on an {@link AjaxRequestTarget}. Does nothing if
++	 * the given node is currently not visible or target is <code>null</code>.
++	 * 
++	 * This default implementation adds this whole component for rendering.
++	 * 
++	 * @param t
++	 * @param target
++	 */
++	public void updateBranch(T t, final AjaxRequestTarget target)
++	{
++		if (target != null)
++		{
++			target.add(this);
++		}
++	}
++
++	/**
++	 * Convenience method to update a single node on an {@link AjaxRequestTarget}. Does nothing if
++	 * the given node is currently not visible or target is <code>null</code>.
++	 * 
++	 * @param t
++	 * @param target
++	 */
++	public void updateNode(T t, final AjaxRequestTarget target)
++	{
++		if (target != null)
++		{
++			final IModel<T> model = getProvider().model(t);
++			visitChildren(Node.class, new IVisitor<Node<T>, Void>()
++			{
++				public void component(Node<T> node, IVisit<Void> visit)
++				{
++					if (model.equals(node.getModel()))
++					{
++						target.add(node);
++						visit.stop();
++					}
++					visit.dontGoDeeper();
++				}
++			});
++			model.detach();
++		}
++	}
++
++	/**
++	 * The state of a node.
++	 */
++	public static enum State {
++		/**
++		 * The node is collapsed, i.e. its children are not iterated.
++		 */
++		COLLAPSED,
++		/**
++		 * The node is expanded, i.e. its children are iterated.
++		 */
++		EXPANDED
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/DefaultNestedTree.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/DefaultNestedTree.java
index 0000000,0000000..f2d04ba
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/DefaultNestedTree.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import java.util.Set;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.extensions.markup.html.repeater.tree.content.Folder;
++import org.apache.wicket.extensions.markup.html.repeater.tree.theme.WindowsTheme;
++import org.apache.wicket.model.IModel;
++
++/**
++ * An implementation of the NestedTree that aims to solve the 90% usecase by using {@link Folder}s
++ * on a standard {@link NestedTree}.
++ * 
++ * @param <T>
++ *            The model object type
++ * @author svenmeier
++ */
++public class DefaultNestedTree<T> extends NestedTree<T>
++{
++	private static final long serialVersionUID = 1L;
++
++	public DefaultNestedTree(String id, ITreeProvider<T> provider)
++	{
++		this(id, provider, null);
++	}
++
++	public DefaultNestedTree(String id, ITreeProvider<T> provider, IModel<Set<T>> state)
++	{
++		super(id, provider, state);
++
++		add(new WindowsTheme());
++	}
++
++	@Override
++	protected Component newContentComponent(String id, IModel<T> model)
++	{
++		return new Folder<T>(id, this, model);
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/DefaultTableTree.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/DefaultTableTree.java
index 0000000,0000000..838d2b6
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/DefaultTableTree.java
@@@ -1,0 -1,0 +1,75 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import java.util.List;
++import java.util.Set;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
++import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
++import org.apache.wicket.extensions.markup.html.repeater.data.table.NavigationToolbar;
++import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
++import org.apache.wicket.extensions.markup.html.repeater.tree.content.Folder;
++import org.apache.wicket.extensions.markup.html.repeater.tree.theme.WindowsTheme;
++import org.apache.wicket.markup.repeater.Item;
++import org.apache.wicket.markup.repeater.OddEvenItem;
++import org.apache.wicket.model.IModel;
++
++/**
++ * An implementation of the TableTree that aims to solve the 90% usecase by using {@link Folder}s
++ * and by adding navigation, headers and no-records-found toolbars to a standard {@link TableTree}.
++ * 
++ * @param <T>
++ *            The model object type
++ * @author svenmeier
++ */
++public class DefaultTableTree<T> extends TableTree<T>
++{
++
++	private static final long serialVersionUID = 1L;
++
++	public DefaultTableTree(String id, List<IColumn<T>> columns, ISortableTreeProvider<T> provider,
++		int rowsPerPage)
++	{
++		this(id, columns, provider, rowsPerPage, null);
++	}
++
++	public DefaultTableTree(String id, List<IColumn<T>> columns, ISortableTreeProvider<T> provider,
++		int rowsPerPage, IModel<Set<T>> state)
++	{
++		super(id, columns, provider, rowsPerPage, state);
++
++		getTable().addTopToolbar(new NavigationToolbar(getTable()));
++		getTable().addTopToolbar(new HeadersToolbar(getTable(), provider));
++		getTable().addBottomToolbar(new NoRecordsToolbar(getTable()));
++
++		add(new WindowsTheme());
++	}
++
++	@Override
++	protected Component newContentComponent(String id, IModel<T> model)
++	{
++		return new Folder<T>(id, this, model);
++	}
++
++	@Override
++	protected Item<T> newRowItem(String id, int index, IModel<T> model)
++	{
++		return new OddEvenItem<T>(id, index, model);
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/ISortableTreeProvider.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/ISortableTreeProvider.java
index 0000000,0000000..e0b5095
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/ISortableTreeProvider.java
@@@ -1,0 -1,0 +1,30 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortStateLocator;
++
++/**
++ * Tree provider that can hold sort state
++ * 
++ * @author svenmeier
++ * @param <T>
++ *            node type
++ */
++public interface ISortableTreeProvider<T> extends ITreeProvider<T>, ISortStateLocator
++{
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/ITreeProvider.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/ITreeProvider.java
index 0000000,0000000..a86098a
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/ITreeProvider.java
@@@ -1,0 -1,0 +1,78 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import java.util.Iterator;
++
++import org.apache.wicket.model.IDetachable;
++import org.apache.wicket.model.IModel;
++
++/**
++ * Provider of a tree.
++ * 
++ * You can use the {@link IDetachable#detach()} method for cleaning up your ITreeProvider instance.
++ * 
++ * @see IDetachable
++ * @see AbstractTree
++ * 
++ * @author svenmeier
++ * @param <T>
++ *            the node type
++ */
++public interface ITreeProvider<T> extends IDetachable
++{
++
++	/**
++	 * Get the roots of the tree.
++	 * 
++	 * @return roots
++	 */
++	Iterator<? extends T> getRoots();
++
++	/**
++	 * Does the given object have children - note that this method may return <code>true</code> even
++	 * if {@link #getChildren(Object)} returns an empty iterator.
++	 * 
++	 * @param node
++	 *            the node to check for children
++	 * @return {@code true} if node has children
++	 */
++	boolean hasChildren(T node);
++
++	/**
++	 * Get the children of the given node.
++	 * 
++	 * @param node
++	 *            node to get children for
++	 * @return children of node
++	 */
++	Iterator<? extends T> getChildren(T node);
++
++	/**
++	 * Callback used by the consumer of this tree provider to wrap objects retrieved from
++	 * {@link #getRoots()} or {@link #getChildren(Object)} with a model (usually a detachable one).
++	 * <p>
++	 * Important note: The model must implement {@link Object#equals(Object)} and
++	 * {@link Object#hashCode()} !
++	 * 
++	 * @param object
++	 *            the object that needs to be wrapped
++	 * 
++	 * @return the model representation of the object
++	 */
++	IModel<T> model(T object);
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/NestedTree.html
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/NestedTree.html
index 0000000,0000000..5bb6b64
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/NestedTree.html
@@@ -1,0 -1,0 +1,20 @@@
++<?xml version="1.0" encoding="UTF-8" ?>
++<!--
++   Licensed to the Apache Software Foundation (ASF) under one or more
++   contributor license agreements.  See the NOTICE file distributed with
++   this work for additional information regarding copyright ownership.
++   The ASF licenses this file to You under the Apache License, Version 2.0
++   (the "License"); you may not use this file except in compliance with
++   the License.  You may obtain a copy of the License at
++
++        http://www.apache.org/licenses/LICENSE-2.0
++
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++-->
++<wicket:panel xmlns:wicket="http://wicket.apache.org">
++	<div wicket:id="subtree">[subtree]</div>
++</wicket:panel>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/NestedTree.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/NestedTree.java
index 0000000,0000000..c0b1974
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/NestedTree.java
@@@ -1,0 -1,0 +1,122 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import java.util.Set;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.extensions.markup.html.repeater.tree.nested.BranchItem;
++import org.apache.wicket.extensions.markup.html.repeater.tree.nested.Subtree;
++import org.apache.wicket.model.AbstractReadOnlyModel;
++import org.apache.wicket.model.IModel;
++import org.apache.wicket.util.visit.IVisit;
++import org.apache.wicket.util.visit.IVisitor;
++
++/**
++ * A tree with nested markup.
++ * 
++ * @author svenmeier
++ * @param <T>
++ *            the model object type
++ */
++public abstract class NestedTree<T> extends AbstractTree<T>
++{
++
++	private static final long serialVersionUID = 1L;
++
++	/**
++	 * Construct.
++	 * 
++	 * @param id
++	 *            the component id
++	 * @param provider
++	 *            the provider of the tree
++	 */
++	public NestedTree(String id, ITreeProvider<T> provider)
++	{
++		this(id, provider, null);
++	}
++
++	/**
++	 * Construct.
++	 * 
++	 * @param id
++	 *            the component id
++	 * @param provider
++	 *            the provider of the tree
++	 * @param state
++	 *            the expansion state
++	 * 
++	 * @see State
++	 */
++	public NestedTree(String id, ITreeProvider<T> provider, IModel<Set<T>> state)
++	{
++		super(id, provider, state);
++
++		add(newSubtree("subtree", new RootsModel()));
++	}
++
++	/**
++	 * Create a new subtree.
++	 * 
++	 * @param id
++	 *            component id
++	 * @param model
++	 *            the model of the new subtree
++	 * @return the created component
++	 */
++	public Component newSubtree(String id, IModel<T> model)
++	{
++		return new Subtree<T>(id, this, model);
++	}
++
++	/**
++	 * Overriden to update the affected {@link BranchItem} only.
++	 */
++	@Override
++	public void updateBranch(T t, final AjaxRequestTarget target)
++	{
++		if (target != null)
++		{
++			final IModel<T> model = getProvider().model(t);
++			visitChildren(BranchItem.class, new IVisitor<BranchItem<T>, Void>()
++			{
++				public void component(BranchItem<T> branch, IVisit<Void> visit)
++				{
++					if (model.equals(branch.getModel()))
++					{
++						target.add(branch);
++						visit.stop();
++					}
++				}
++			});
++			model.detach();
++		}
++	}
++
++	private class RootsModel extends AbstractReadOnlyModel<T>
++	{
++		private static final long serialVersionUID = 1L;
++
++		@Override
++		public T getObject()
++		{
++			return null;
++		}
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/Node.html
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/Node.html
index 0000000,0000000..7c30dcd
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/Node.html
@@@ -1,0 -1,0 +1,27 @@@
++<?xml version="1.0" encoding="UTF-8" ?>
++<!--
++   Licensed to the Apache Software Foundation (ASF) under one or more
++   contributor license agreements.  See the NOTICE file distributed with
++   this work for additional information regarding copyright ownership.
++   The ASF licenses this file to You under the Apache License, Version 2.0
++   (the "License"); you may not use this file except in compliance with
++   the License.  You may obtain a copy of the License at
++
++        http://www.apache.org/licenses/LICENSE-2.0
++
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++-->
++<html>
++<body>
++
++<wicket:panel xmlns:wicket="http://wicket.apache.org">
++	<a wicket:id="junction">&#160;</a>
++	<span wicket:id="content" class="tree-content">[content]</span>
++</wicket:panel>
++
++</body>
++</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/Node.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/Node.java
index 0000000,0000000..79c7eb7
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/Node.java
@@@ -1,0 -1,0 +1,213 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.MarkupContainer;
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
++import org.apache.wicket.behavior.Behavior;
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree.State;
++import org.apache.wicket.markup.ComponentTag;
++import org.apache.wicket.markup.html.panel.Panel;
++import org.apache.wicket.model.IModel;
++
++/**
++ * Representation of a single node in the tree. By default uses an {@link AjaxFallbackLink} for its
++ * junction component.
++ * 
++ * @see #createJunctionComponent(String)
++ * 
++ * @author svenmeier
++ * @param <T>
++ *            The model object type
++ */
++public abstract class Node<T> extends Panel
++{
++
++	private static final long serialVersionUID = 1L;
++
++	/**
++	 * The component id for the content component.
++	 */
++	public static final String CONTENT_ID = "content";
++
++	private AbstractTree<T> tree;
++
++	/**
++	 * Constructor.
++	 * 
++	 * @param id
++	 *            component id
++	 * @param tree
++	 *            the owning tree
++	 * @param model
++	 *            the model for this node
++	 */
++	public Node(String id, AbstractTree<T> tree, IModel<T> model)
++	{
++		super(id, model);
++
++		this.tree = tree;
++
++		setOutputMarkupId(true);
++
++		MarkupContainer junction = createJunctionComponent("junction");
++		junction.add(new StyleBehavior());
++		add(junction);
++
++		Component content = createContent(CONTENT_ID, model);
++		if (!content.getId().equals(CONTENT_ID))
++		{
++			throw new IllegalArgumentException(
++				"content must have component id equal to Node.CONTENT_ID");
++		}
++		add(content);
++	}
++
++	/**
++	 * @return the model
++	 */
++	@SuppressWarnings("unchecked")
++	public IModel<T> getModel()
++	{
++		return (IModel<T>)getDefaultModel();
++	}
++
++	/**
++	 * @return the model object
++	 */
++	public T getModelObject()
++	{
++		return getModel().getObject();
++	}
++
++	/**
++	 * The junction component expands and collapses this node.
++	 * 
++	 * @param id
++	 *            the component id
++	 * @return component representing the junction
++	 */
++	protected MarkupContainer createJunctionComponent(String id)
++	{
++		return new AjaxFallbackLink<Void>(id)
++		{
++			private static final long serialVersionUID = 1L;
++
++			@Override
++			public void onClick(AjaxRequestTarget target)
++			{
++				toggle();
++			}
++
++			@Override
++			public boolean isEnabled()
++			{
++				return tree.getProvider().hasChildren(Node.this.getModelObject());
++			}
++		};
++	}
++
++	private void toggle()
++	{
++		T t = getModelObject();
++
++		if (tree.getState(t) == State.EXPANDED)
++		{
++			tree.collapse(t);
++		}
++		else
++		{
++			tree.expand(t);
++		}
++	}
++
++	/**
++	 * Create the component to display the actual node's content.
++	 * 
++	 * @param id
++	 *            the component id
++	 * @param model
++	 *            the node's model
++	 * @return the component representing the content
++	 */
++	protected abstract Component createContent(String id, IModel<T> model);
++
++	/**
++	 * Get the style class depending on the current {@link State} of this node.
++	 * 
++	 * @see #getExpandedStyleClass(Object)
++	 * @see #getCollapsedStyleClass()
++	 * @see #getOtherStyleClass()
++	 * @return the style class
++	 */
++	protected String getStyleClass()
++	{
++		T t = getModelObject();
++
++		if (tree.getProvider().hasChildren(t))
++		{
++			if (tree.getState(t) == State.EXPANDED)
++			{
++				return getExpandedStyleClass(t);
++			}
++			else
++			{
++				return getCollapsedStyleClass();
++			}
++		}
++		return getOtherStyleClass();
++	}
++
++	protected String getExpandedStyleClass(T t)
++	{
++		return "tree-junction-expanded";
++	}
++
++	protected String getCollapsedStyleClass()
++	{
++		return "tree-junction-collapsed";
++	}
++
++	protected String getOtherStyleClass()
++	{
++		return "tree-junction";
++	}
++
++	/**
++	 * Behavior to add the style class attribute.
++	 * 
++	 * @see Node#getStyleClass()
++	 */
++	private static class StyleBehavior extends Behavior
++	{
++		private static final long serialVersionUID = 1L;
++
++		@Override
++		public void onComponentTag(Component component, ComponentTag tag)
++		{
++			Node<?> node = (Node<?>)component.getParent();
++
++			String styleClass = node.getStyleClass();
++			if (styleClass != null)
++			{
++				tag.put("class", styleClass);
++			}
++		}
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/TableTree.html
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/TableTree.html
index 0000000,0000000..9609da0
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/TableTree.html
@@@ -1,0 -1,0 +1,20 @@@
++<?xml version="1.0" encoding="UTF-8" ?>
++<!--
++   Licensed to the Apache Software Foundation (ASF) under one or more
++   contributor license agreements.  See the NOTICE file distributed with
++   this work for additional information regarding copyright ownership.
++   The ASF licenses this file to You under the Apache License, Version 2.0
++   (the "License"); you may not use this file except in compliance with
++   the License.  You may obtain a copy of the License at
++
++        http://www.apache.org/licenses/LICENSE-2.0
++
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++-->
++<wicket:panel xmlns:wicket="http://wicket.apache.org">
++	<table wicket:id="table">[table]</table>
++</wicket:panel>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/TableTree.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/TableTree.java
index 0000000,0000000..d37630e
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/TableTree.java
@@@ -1,0 -1,0 +1,226 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree;
++
++import java.util.List;
++import java.util.Set;
++
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
++import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
++import org.apache.wicket.extensions.markup.html.repeater.tree.table.ITreeColumn;
++import org.apache.wicket.extensions.markup.html.repeater.tree.table.ITreeDataProvider;
++import org.apache.wicket.extensions.markup.html.repeater.tree.table.NodeModel;
++import org.apache.wicket.extensions.markup.html.repeater.tree.table.TreeDataProvider;
++import org.apache.wicket.markup.repeater.IItemReuseStrategy;
++import org.apache.wicket.markup.repeater.Item;
++import org.apache.wicket.markup.repeater.RefreshingView;
++import org.apache.wicket.markup.repeater.data.IDataProvider;
++import org.apache.wicket.model.IModel;
++import org.apache.wicket.util.lang.Args;
++import org.apache.wicket.util.visit.IVisit;
++import org.apache.wicket.util.visit.IVisitor;
++
++/**
++ * A tree with tabular markup.
++ * 
++ * @author svenmeier
++ * 
++ * @param <T>
++ *            The model object type
++ */
++public abstract class TableTree<T> extends AbstractTree<T>
++{
++	private static final long serialVersionUID = 1L;
++
++	private final DataTable<T> table;
++
++	/**
++	 * Constructor
++	 * 
++	 * @param id
++	 *            component id
++	 * @param columns
++	 *            list of IColumn objects
++	 * @param dataProvider
++	 *            imodel for data provider
++	 * @param rowsPerPage
++	 *            number of rows per page
++	 */
++	public TableTree(final String id, final List<IColumn<T>> columns,
++		final ITreeProvider<T> dataProvider, final long rowsPerPage)
++	{
++		this(id, columns, dataProvider, rowsPerPage, null);
++	}
++
++	/**
++	 * Constructor
++	 * 
++	 * @param id
++	 *            component id
++	 * @param columns
++	 *            list of IColumn objects
++	 * @param provider
++	 *            provider of the tree
++	 * @param rowsPerPage
++	 *            number of rows per page
++	 * @param state
++	 *            the expansion state
++	 */
++	public TableTree(final String id, final List<IColumn<T>> columns,
++		final ITreeProvider<T> provider, final long rowsPerPage, IModel<Set<T>> state)
++	{
++		super(id, provider, state);
++
++		Args.notEmpty(columns, "columns");
++		for (IColumn<T> column : columns)
++		{
++			if (column instanceof ITreeColumn<?>)
++			{
++				((ITreeColumn<T>)column).setTree(this);
++			}
++		}
++
++		this.table = newDataTable("table", columns, newDataProvider(provider), rowsPerPage);
++		add(table);
++	}
++
++	/**
++	 * Factory method for the wrapped {@link DataTable}.
++	 * 
++	 * @param id
++	 * @param columns
++	 * @param dataProvider
++	 * @param rowsPerPage
++	 * @return nested data table
++	 */
++	protected DataTable<T> newDataTable(String id, List<IColumn<T>> columns,
++		IDataProvider<T> dataProvider, long rowsPerPage)
++	{
++		return new DataTable<T>(id, columns, dataProvider, rowsPerPage)
++		{
++			private static final long serialVersionUID = 1L;
++
++			@Override
++			protected Item<T> newRowItem(String id, int index, IModel<T> model)
++			{
++				Item<T> item = TableTree.this.newRowItem(id, index, model);
++
++				// see #update(Node);
++				item.setOutputMarkupId(true);
++
++				return item;
++			}
++		};
++	}
++
++	/**
++	 * Get the nested table.
++	 * 
++	 * @return the nested table
++	 */
++	public DataTable<T> getTable()
++	{
++		return table;
++	}
++
++	/**
++	 * Sets the item reuse strategy. This strategy controls the creation of {@link Item}s.
++	 * 
++	 * @see RefreshingView#setItemReuseStrategy(IItemReuseStrategy)
++	 * @see IItemReuseStrategy
++	 * 
++	 * @param strategy
++	 *            item reuse strategy
++	 * @return this for chaining
++	 */
++	@Override
++	public final TableTree<T> setItemReuseStrategy(final IItemReuseStrategy strategy)
++	{
++		table.setItemReuseStrategy(strategy);
++
++		super.setItemReuseStrategy(strategy);
++
++		return this;
++	}
++
++	/**
++	 * Overriden to update the complete row item of the node.
++	 */
++	@Override
++	public void updateNode(T t, final AjaxRequestTarget target)
++	{
++		if (target != null)
++		{
++			final IModel<T> model = getProvider().model(t);
++			visitChildren(Item.class, new IVisitor<Item<T>, Void>()
++			{
++				public void component(Item<T> item, IVisit<Void> visit)
++				{
++					NodeModel<T> nodeModel = (NodeModel<T>)item.getModel();
++
++					if (model.equals(nodeModel.getWrappedModel()))
++					{
++						target.add(item);
++						visit.stop();
++					}
++					visit.dontGoDeeper();
++				}
++			});
++			model.detach();
++		}
++	}
++
++	/**
++	 * Hook method to create an {@link ITreeDataProvider}.
++	 * 
++	 * @param provider
++	 *            the tree provider
++	 * @return the data provider
++	 */
++	protected ITreeDataProvider<T> newDataProvider(ITreeProvider<T> provider)
++	{
++		return new TreeDataProvider<T>(provider)
++		{
++			private static final long serialVersionUID = 1L;
++
++			@Override
++			protected boolean iterateChildren(T object)
++			{
++				return TableTree.this.getState(object) == State.EXPANDED;
++			}
++		};
++	}
++
++	/**
++	 * Create a row item for the nested {@link DataTable}.
++	 * 
++	 * @param id
++	 *            component id
++	 * @param index
++	 *            index of row
++	 * @param model
++	 *            model for row
++	 * @return row item
++	 */
++	protected Item<T> newRowItem(String id, int index, IModel<T> model)
++	{
++		Item<T> item = new Item<T>(id, index, model);
++
++		return item;
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckFolder.html
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckFolder.html
index 0000000,0000000..e55210f
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckFolder.html
@@@ -1,0 -1,0 +1,35 @@@
++<?xml version="1.0" encoding="UTF-8" ?>
++<!--
++   Licensed to the Apache Software Foundation (ASF) under one or more
++   contributor license agreements.  See the NOTICE file distributed with
++   this work for additional information regarding copyright ownership.
++   The ASF licenses this file to You under the Apache License, Version 2.0
++   (the "License"); you may not use this file except in compliance with
++   the License.  You may obtain a copy of the License at
++
++        http://www.apache.org/licenses/LICENSE-2.0
++
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++-->
++<html>
++
++<head>
++</head>
++
++<body>
++
++<wicket:panel xmlns:wicket="http://wicket.apache.org">
++	<input type="checkbox" wicket:id="check" />
++	
++	<a wicket:id="link">
++		<span class="tree-label" wicket:id="label">[label]</span>
++	</a>
++</wicket:panel>
++
++</body>
++
++</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckFolder.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckFolder.java
index 0000000,0000000..6a61593
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckFolder.java
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.content;
++
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
++import org.apache.wicket.markup.html.form.Check;
++import org.apache.wicket.markup.html.form.CheckGroup;
++import org.apache.wicket.model.IModel;
++
++/**
++ * This class adds a {@link Check} to a {@link Folder}. Remeber to wrap your tree in a
++ * {@link CheckGroup} for all checks to work correctly.
++ * 
++ * @author svenmeier
++ */
++public class CheckFolder<T> extends Folder<T>
++{
++	private static final long serialVersionUID = 1L;
++
++	public CheckFolder(String id, AbstractTree<T> tree, IModel<T> model)
++	{
++		super(id, tree, model);
++
++		add(new Check<T>("check", model));
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckedFolder.html
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckedFolder.html
index 0000000,0000000..48433b6
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckedFolder.html
@@@ -1,0 -1,0 +1,35 @@@
++<?xml version="1.0" encoding="UTF-8" ?>
++<!--
++   Licensed to the Apache Software Foundation (ASF) under one or more
++   contributor license agreements.  See the NOTICE file distributed with
++   this work for additional information regarding copyright ownership.
++   The ASF licenses this file to You under the Apache License, Version 2.0
++   (the "License"); you may not use this file except in compliance with
++   the License.  You may obtain a copy of the License at
++
++        http://www.apache.org/licenses/LICENSE-2.0
++
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++-->
++<html>
++
++<head>
++</head>
++
++<body>
++
++<wicket:panel xmlns:wicket="http://wicket.apache.org">
++	<input type="checkbox" wicket:id="checkbox" />
++	
++	<a wicket:id="link">
++		<span class="tree-label" wicket:id="label">[label]</span>
++	</a>
++</wicket:panel>
++
++</body>
++
++</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckedFolder.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckedFolder.java
index 0000000,0000000..cf0f1ed
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/CheckedFolder.java
@@@ -1,0 -1,0 +1,92 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.content;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
++import org.apache.wicket.markup.html.form.CheckBox;
++import org.apache.wicket.model.IModel;
++import org.apache.wicket.model.Model;
++
++/**
++ * This class adds a {@link CheckBox} to a {@link Folder}. Subclasses have to override
++ * {@link #newCheckBoxModel(IModel)} to do anything useful with the checkbox state.
++ * 
++ * @see #newCheckBoxModel(IModel)
++ * 
++ * @author svenmeier
++ */
++public class CheckedFolder<T> extends Folder<T>
++{
++
++	private static final long serialVersionUID = 1L;
++
++	public CheckedFolder(String id, AbstractTree<T> tree, IModel<T> model)
++	{
++		super(id, tree, model);
++
++		add(newCheckBox("checkbox", model));
++	}
++
++	/**
++	 * Hook method to create a new checkbox component. This default implementation uses an
++	 * {@link AjaxCheckBox}.
++	 * 
++	 * @param id
++	 * @param model
++	 * @return created component
++	 * 
++	 * @see #newCheckBoxModel(IModel)
++	 * @see #onUpdate(AjaxRequestTarget)
++	 */
++	protected Component newCheckBox(String id, IModel<T> model)
++	{
++		return new AjaxCheckBox(id, newCheckBoxModel(model))
++		{
++			private static final long serialVersionUID = 1L;
++
++			@Override
++			protected void onUpdate(AjaxRequestTarget target)
++			{
++				CheckedFolder.this.onUpdate(target);
++			}
++		};
++	}
++
++	/**
++	 * Create the model for the checkbox, defaults to {@link Boolean#FALSE}.
++	 * 
++	 * @param model
++	 * @return wrapping model
++	 */
++	protected IModel<Boolean> newCheckBoxModel(IModel<T> model)
++	{
++		return Model.of(Boolean.FALSE);
++	}
++
++	/**
++	 * Hook method to be notified of an update of the checkbox.
++	 * 
++	 * @param target
++	 * @see #newCheckBox(String, IModel)
++	 */
++	protected void onUpdate(AjaxRequestTarget target)
++	{
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/Folder.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/Folder.java
index 0000000,0000000..bfba16e
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/Folder.java
@@@ -1,0 -1,0 +1,157 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.content;
++
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree.State;
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A typical folder representation of nodes in a tree.
++ * 
++ * The link is used to expand/collapse the tree depending on the {@link State} of the current node.
++ * Nodes without children are not clickable. Subclasses may change this behavior by overriding
++ * {@link #isClickable()} and {@link #onClick(AjaxRequestTarget)}.
++ * 
++ * @author svenmeier
++ */
++public class Folder<T> extends StyledLinkLabel<T>
++{
++
++	private static final long serialVersionUID = 1L;
++
++	private AbstractTree<T> tree;
++
++	public Folder(String id, AbstractTree<T> tree, IModel<T> model)
++	{
++		super(id, model);
++
++		this.tree = tree;
++	}
++
++	/**
++	 * Clickable if node can be expanded/collapsed, i.e. has children.
++	 * 
++	 * @see ITreeProvider#hasChildren(Object)
++	 */
++	@Override
++	protected boolean isClickable()
++	{
++		T t = getModelObject();
++
++		return tree.getProvider().hasChildren(t);
++	}
++
++	/**
++	 * Toggle the node's {@link State} on click.
++	 */
++	@Override
++	protected void onClick(AjaxRequestTarget target)
++	{
++		T t = getModelObject();
++		if (tree.getState(t) == State.EXPANDED)
++		{
++			tree.collapse(t);
++		}
++		else
++		{
++			tree.expand(t);
++		}
++	}
++
++	/**
++	 * Delegates to others methods depending wether the given model is a folder, expanded, collapsed
++	 * or selected.
++	 * 
++	 * @see ITreeProvider#hasChildren(Object)
++	 * @see AbstractTree#getState(Object)
++	 * @see #isSelected()
++	 * @see #getOpenStyleClass()
++	 * @see #getClosedStyleClass()
++	 * @see #getOtherStyleClass(Object)
++	 * @see #getSelectedStyleClass()
++	 */
++	@Override
++	protected String getStyleClass()
++	{
++		T t = getModelObject();
++
++		String styleClass;
++		if (tree.getProvider().hasChildren(t))
++		{
++			if (tree.getState(t) == State.EXPANDED)
++			{
++				styleClass = getOpenStyleClass();
++			}
++			else
++			{
++				styleClass = getClosedStyleClass();
++			}
++		}
++		else
++		{
++			styleClass = getOtherStyleClass(t);
++		}
++
++		if (isSelected())
++		{
++			styleClass += " " + getSelectedStyleClass();
++		}
++
++		return styleClass;
++	}
++
++	/**
++	 * Optional attribute which decides if an additional "selected" style class should be rendered.
++	 * 
++	 * @return defaults to <code>false</code>
++	 */
++	protected boolean isSelected()
++	{
++		return false;
++	}
++
++	/**
++	 * Get a style class for anything other than closed or open folders.
++	 */
++	protected String getOtherStyleClass(T t)
++	{
++		return "tree-folder-other";
++	}
++
++	protected String getClosedStyleClass()
++	{
++		return "tree-folder-closed";
++	}
++
++	protected String getOpenStyleClass()
++	{
++		return "tree-folder-open";
++	}
++
++	/**
++	 * Get a style class to render for a selected folder.
++	 * 
++	 * @see #isSelected()
++	 */
++	protected String getSelectedStyleClass()
++	{
++		return "selected";
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/StyledLinkLabel.html
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/StyledLinkLabel.html
index 0000000,0000000..efe3a32
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/StyledLinkLabel.html
@@@ -1,0 -1,0 +1,33 @@@
++<?xml version="1.0" encoding="UTF-8" ?>
++<!--
++   Licensed to the Apache Software Foundation (ASF) under one or more
++   contributor license agreements.  See the NOTICE file distributed with
++   this work for additional information regarding copyright ownership.
++   The ASF licenses this file to You under the Apache License, Version 2.0
++   (the "License"); you may not use this file except in compliance with
++   the License.  You may obtain a copy of the License at
++
++        http://www.apache.org/licenses/LICENSE-2.0
++
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++-->
++<html>
++
++<head>
++</head>
++
++<body>
++
++<wicket:panel xmlns:wicket="http://wicket.apache.org">
++	<a wicket:id="link">
++		<span class="tree-label" wicket:id="label">[label]</span>
++	</a>
++</wicket:panel>
++
++</body>
++
++</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/StyledLinkLabel.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/StyledLinkLabel.java
index 0000000,0000000..a26c6d3
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/content/StyledLinkLabel.java
@@@ -1,0 -1,0 +1,166 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.content;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.MarkupContainer;
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
++import org.apache.wicket.behavior.Behavior;
++import org.apache.wicket.markup.ComponentTag;
++import org.apache.wicket.markup.html.basic.Label;
++import org.apache.wicket.markup.html.panel.Panel;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A styled link with a label.
++ * 
++ * @see #newLinkComponent(String, IModel)
++ * @see #getStyleClass()
++ * 
++ * @author svenmeier
++ */
++public abstract class StyledLinkLabel<T> extends Panel
++{
++	private static final StyleBehavior STYLE_CLASS = new StyleBehavior();
++
++	private static final long serialVersionUID = 1L;
++
++	public StyledLinkLabel(String id, IModel<T> model)
++	{
++		super(id, model);
++
++		MarkupContainer link = newLinkComponent("link", model);
++		link.add(STYLE_CLASS);
++		add(link);
++
++		link.add(newLabelComponent("label", model));
++	}
++
++	@SuppressWarnings("unchecked")
++	public IModel<T> getModel()
++	{
++		return (IModel<T>)getDefaultModel();
++	}
++
++	public T getModelObject()
++	{
++		return getModel().getObject();
++	}
++
++	/**
++	 * Hook method to create a new link component.
++	 * 
++	 * This default implementation returns an {@link AjaxFallbackLink} which invokes
++	 * {@link #onClick(AjaxRequestTarget)} only if {@link #isClickable()} returns <code>true</code>.
++	 * 
++	 * @see #isClickable()
++	 * @see #onClick(AjaxRequestTarget)
++	 */
++	protected MarkupContainer newLinkComponent(String id, IModel<T> model)
++	{
++		return new AjaxFallbackLink<Void>(id)
++		{
++			private static final long serialVersionUID = 1L;
++
++			@Override
++			public boolean isEnabled()
++			{
++				return StyledLinkLabel.this.isClickable();
++			}
++
++			@Override
++			public void onClick(AjaxRequestTarget target)
++			{
++				StyledLinkLabel.this.onClick(target);
++			}
++		};
++	}
++
++	/**
++	 * Hook method to create a new label component.
++	 * 
++	 * @param id
++	 * @param model
++	 * @return created component
++	 * 
++	 * @see #newLabelModel(IModel)
++	 */
++	protected Component newLabelComponent(String id, IModel<T> model)
++	{
++		return new Label(id, newLabelModel(model));
++	}
++
++	/**
++	 * Create the model for the label, defaults to the model itself.
++	 * 
++	 * @param model
++	 * @return wrapping model
++	 */
++	protected IModel<?> newLabelModel(IModel<T> model)
++	{
++		return model;
++	}
++
++	/**
++	 * Get a style class for the link.
++	 */
++	protected abstract String getStyleClass();
++
++	/**
++	 * Clicking is disabled by default, override this method if you want your link to be enabled.
++	 * 
++	 * @see #newLinkComponent(String, IModel)
++	 * @see #isClickable()
++	 */
++	protected boolean isClickable()
++	{
++		return false;
++	}
++
++	/**
++	 * Hook method to be notified of a click on the link.
++	 * 
++	 * @param target
++	 * 
++	 * @see #newLinkComponent(String, IModel)
++	 * @see #isClickable()
++	 */
++	protected void onClick(AjaxRequestTarget target)
++	{
++	}
++
++	/**
++	 * Behavior to add a style class attribute to a contained link.
++	 */
++	private static class StyleBehavior extends Behavior
++	{
++		private static final long serialVersionUID = 1L;
++
++		@Override
++		public void onComponentTag(Component component, ComponentTag tag)
++		{
++			StyledLinkLabel<?> parent = (StyledLinkLabel<?>)component.getParent();
++
++			String styleClass = parent.getStyleClass();
++			if (styleClass != null)
++			{
++				tag.put("class", styleClass);
++			}
++		}
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/BranchItem.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/BranchItem.java
index 0000000,0000000..d82f1df
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/BranchItem.java
@@@ -1,0 -1,0 +1,61 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.nested;
++
++import org.apache.wicket.markup.ComponentTag;
++import org.apache.wicket.markup.repeater.Item;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A branch is a container for a single node and its children inside a {@link Subtree}.
++ * 
++ * @see Subtree#newBranchItem(String, int, IModel)
++ * 
++ * @author svenmeier
++ */
++public final class BranchItem<T> extends Item<T>
++{
++
++	private static final long serialVersionUID = 1L;
++
++	public BranchItem(String id, int index, IModel<T> model)
++	{
++		super(id, index, model);
++
++		setOutputMarkupId(true);
++	}
++
++	@Override
++	protected void onComponentTag(ComponentTag tag)
++	{
++		super.onComponentTag(tag);
++
++		if (isLast())
++		{
++			tag.put("class", "tree-branch tree-branch-last");
++		}
++		else
++		{
++			tag.put("class", "tree-branch tree-branch-mid");
++		}
++	}
++
++	private boolean isLast()
++	{
++		return getIndex() == getParent().size() - 1;
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/Subtree.html
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/Subtree.html
index 0000000,0000000..e3d8986
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/Subtree.html
@@@ -1,0 -1,0 +1,34 @@@
++<?xml version="1.0" encoding="UTF-8" ?>
++<!--
++   Licensed to the Apache Software Foundation (ASF) under one or more
++   contributor license agreements.  See the NOTICE file distributed with
++   this work for additional information regarding copyright ownership.
++   The ASF licenses this file to You under the Apache License, Version 2.0
++   (the "License"); you may not use this file except in compliance with
++   the License.  You may obtain a copy of the License at
++
++        http://www.apache.org/licenses/LICENSE-2.0
++
++   Unless required by applicable law or agreed to in writing, software
++   distributed under the License is distributed on an "AS IS" BASIS,
++   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++   See the License for the specific language governing permissions and
++   limitations under the License.
++-->
++<html>
++
++<head>
++</head>
++
++<body>
++
++<wicket:panel xmlns:wicket="http://wicket.apache.org">
++	<div wicket:id="branches">
++		<div wicket:id="node" class="tree-node">[node]</div>
++		<div wicket:id="subtree" class="tree-subtree">[subtree]</div>
++	</div>
++</wicket:panel>
++
++</body>
++
++</html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/Subtree.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/Subtree.java
index 0000000,0000000..0152ccd
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/nested/Subtree.java
@@@ -1,0 -1,0 +1,171 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.nested;
++
++import java.util.Iterator;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree.State;
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.extensions.markup.html.repeater.tree.NestedTree;
++import org.apache.wicket.markup.html.panel.Panel;
++import org.apache.wicket.markup.repeater.IItemFactory;
++import org.apache.wicket.markup.repeater.IItemReuseStrategy;
++import org.apache.wicket.markup.repeater.Item;
++import org.apache.wicket.markup.repeater.RefreshingView;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A subtree handles all children of a single node (or the root nodes if a <code>null</code> node
++ * was given to the constructor).
++ * 
++ * @see ITreeProvider#getChildren(Object)
++ * @see ITreeProvider#getRoots()
++ * 
++ * @author svenmeier
++ */
++public class Subtree<T> extends Panel
++{
++
++	private static final long serialVersionUID = 1L;
++
++	private NestedTree<T> tree;
++
++	/**
++	 * Create a subtree for the children of the node contained in the given model or the root nodes
++	 * if the model contains <code>null</code>.
++	 * 
++	 * @param id
++	 *            component id
++	 * @param tree
++	 *            the containing tree
++	 * @param model
++	 */
++	public Subtree(String id, final NestedTree<T> tree, final IModel<T> model)
++	{
++		super(id, model);
++
++		if (tree == null)
++		{
++			throw new IllegalArgumentException("argument [tree] cannot be null");
++		}
++		this.tree = tree;
++
++		RefreshingView<T> branches = new RefreshingView<T>("branches")
++		{
++			private static final long serialVersionUID = 1L;
++
++			@Override
++			protected Iterator<IModel<T>> getItemModels()
++			{
++				return new ModelIterator();
++			}
++
++			@Override
++			protected Item<T> newItem(String id, int index, IModel<T> model)
++			{
++				return newBranchItem(id, index, model);
++			}
++
++			@Override
++			protected void populateItem(Item<T> item)
++			{
++				IModel<T> model = item.getModel();
++
++				Component node = tree.newNodeComponent("node", model);
++				item.add(node);
++
++				item.add(tree.newSubtree("subtree", model));
++			}
++		};
++		branches.setItemReuseStrategy(new IItemReuseStrategy()
++		{
++			private static final long serialVersionUID = 1L;
++
++			public <S> Iterator<Item<S>> getItems(IItemFactory<S> factory,
++				Iterator<IModel<S>> newModels, Iterator<Item<S>> existingItems)
++			{
++				return tree.getItemReuseStrategy().getItems(factory, newModels, existingItems);
++			}
++		});
++		add(branches);
++	}
++
++	@SuppressWarnings("unchecked")
++	public IModel<T> getModel()
++	{
++		return (IModel<T>)getDefaultModel();
++	}
++
++	public T getModelObject()
++	{
++		return getModel().getObject();
++	}
++
++	protected BranchItem<T> newBranchItem(String id, int index, IModel<T> model)
++	{
++		return new BranchItem<T>(id, index, model);
++	}
++
++	@Override
++	public boolean isVisible()
++	{
++		T t = getModel().getObject();
++		if (t == null)
++		{
++			// roots always visible
++			return true;
++		}
++		else
++		{
++			return tree.getState(t) == State.EXPANDED;
++		}
++	}
++
++	private final class ModelIterator implements Iterator<IModel<T>>
++	{
++		private Iterator<? extends T> children;
++
++		public ModelIterator()
++		{
++			T t = getModel().getObject();
++			if (t == null)
++			{
++				children = tree.getProvider().getRoots();
++			}
++			else
++			{
++				children = tree.getProvider().getChildren(t);
++			}
++		}
++
++		public void remove()
++		{
++			throw new UnsupportedOperationException();
++		}
++
++		public boolean hasNext()
++		{
++			return children.hasNext();
++		}
++
++		public IModel<T> next()
++		{
++			return tree.getProvider().model(children.next());
++		}
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/AbstractTreeColumn.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/AbstractTreeColumn.java
index 0000000,0000000..fd1fa8b
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/AbstractTreeColumn.java
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.table;
++
++import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
++import org.apache.wicket.extensions.markup.html.repeater.tree.TableTree;
++import org.apache.wicket.model.IModel;
++
++/**
++ * @author svenmeier
++ */
++public abstract class AbstractTreeColumn<T> extends AbstractColumn<T> implements ITreeColumn<T>
++{
++
++	private TableTree<T> tree;
++
++	public AbstractTreeColumn(IModel<String> displayModel)
++	{
++		super(displayModel);
++	}
++
++	public AbstractTreeColumn(IModel<String> displayModel, String sortProperty)
++	{
++		super(displayModel, sortProperty);
++	}
++
++	public void setTree(TableTree<T> tree)
++	{
++		this.tree = tree;
++	}
++
++	public TableTree<T> getTree()
++	{
++		return tree;
++	}
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/ITreeColumn.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/ITreeColumn.java
index 0000000,0000000..856c93c
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/ITreeColumn.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.table;
++
++import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
++import org.apache.wicket.extensions.markup.html.repeater.tree.TableTree;
++
++/**
++ * @author svenmeier
++ */
++public interface ITreeColumn<T> extends IColumn<T>
++{
++	public void setTree(TableTree<T> t);
++}

http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/ITreeDataProvider.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/ITreeDataProvider.java
index 0000000,0000000..dd56242
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/ITreeDataProvider.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements.  See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License.  You may obtain a copy of the License at
++ *
++ *      http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.wicket.extensions.markup.html.repeater.tree.table;
++
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.markup.repeater.data.IDataProvider;
++
++/**
++ * An adapter from a {@link ITreeProvider} to a {@link IDataProvider}.
++ * 
++ * @author svenmeier
++ */
++public interface ITreeDataProvider<T> extends IDataProvider<T>
++{
++	/**
++	 * Wrap the given node in a {@link NodeModel} which provides additional branch information
++	 * needed by {@link TreeColumn}
++	 */
++	public NodeModel<T> model(T object);
++}