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

[9/9] WICKET-4240 moved old tree together into extensions

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.java
new file mode 100644
index 0000000..f96514a
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/BaseTree.java
@@ -0,0 +1,478 @@
+/*
+ * 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.tree;
+
+import javax.swing.tree.TreeModel;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.IClusterable;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.IAjaxLink;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.head.CssHeaderItem;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.Response;
+import org.apache.wicket.request.resource.PackageResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.util.string.Strings;
+
+/**
+ * An abstract Tree component that should serve as a base for custom Tree Components.
+ * 
+ * It has one abstract method - {@link #newNodeComponent(String, IModel)} that needs to be
+ * overridden.
+ * 
+ * @author Matej Knopp
+ */
+@Deprecated
+public abstract class BaseTree extends AbstractTree
+{
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 */
+	public BaseTree(String id)
+	{
+		this(id, null);
+	}
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 * @param model
+	 */
+	public BaseTree(String id, IModel<? extends TreeModel> model)
+	{
+		super(id, model);
+	}
+
+	// default stylesheet resource
+	private static final ResourceReference CSS = new PackageResourceReference(BaseTree.class,
+		"res/base-tree.css");
+
+	/**
+	 * Returns the stylesheet reference
+	 * 
+	 * @return stylesheet reference
+	 */
+	protected ResourceReference getCSS()
+	{
+		return CSS;
+	}
+
+	private static final long serialVersionUID = 1L;
+
+	private static final String JUNCTION_LINK_ID = "junctionLink";
+	private static final String NODE_COMPONENT_ID = "nodeComponent";
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.AbstractTree#populateTreeItem(org.apache.wicket.markup.html.WebMarkupContainer,
+	 *      int)
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	protected void populateTreeItem(WebMarkupContainer item, int level)
+	{
+		// add junction link
+		Object node = item.getDefaultModelObject();
+		Component junctionLink = newJunctionLink(item, JUNCTION_LINK_ID, node);
+		junctionLink.add(new JunctionBorder(node, level));
+		item.add(junctionLink);
+
+		// add node component
+		Component nodeComponent = newNodeComponent(NODE_COMPONENT_ID,
+			(IModel<Object>)item.getDefaultModel());
+		item.add(nodeComponent);
+
+		// add behavior that conditionally adds the "selected" CSS class name
+		item.add(new Behavior()
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void onComponentTag(Component component, ComponentTag tag)
+			{
+				Object node = component.getDefaultModelObject();
+				String klass = getItemClass(node);
+				if (!Strings.isEmpty(klass))
+				{
+					CharSequence oldClass = tag.getAttribute("class");
+					if (Strings.isEmpty(oldClass))
+					{
+						tag.put("class", klass);
+					}
+					else
+					{
+						tag.put("class", oldClass + " " + klass);
+					}
+				}
+			}
+		});
+	}
+
+	protected String getItemClass(Object node)
+	{
+		if (getTreeState().isNodeSelected(node))
+		{
+			return getSelectedClass();
+		}
+		else
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * Returns the class name that will be added to row's CSS class for selected rows
+	 * 
+	 * @return CSS class name
+	 */
+	protected String getSelectedClass()
+	{
+		return "selected";
+	}
+
+	/**
+	 * Creates a new component for the given TreeNode.
+	 * 
+	 * @param id
+	 *            component ID
+	 * @param model
+	 *            model that returns the node
+	 * @return component for node
+	 */
+	protected abstract Component newNodeComponent(String id, IModel<Object> model);
+
+	/**
+	 * Returns whether the provided node is last child of it's parent.
+	 * 
+	 * @param node
+	 *            The node
+	 * @return whether the provided node is the last child
+	 */
+	private boolean isNodeLast(Object node)
+	{
+		Object parent = getParentNode(node);
+		if (parent == null)
+		{
+			return true;
+		}
+		else
+		{
+			return getChildAt(parent, getChildCount(parent) - 1).equals(node);
+		}
+	}
+
+	/**
+	 * Class that wraps a link (or span) with a junction table cells.
+	 * 
+	 * @author Matej Knopp
+	 */
+	private class JunctionBorder extends Behavior
+	{
+		private static final long serialVersionUID = 1L;
+
+		// TODO this field is not serializable but nested inside an serializable component
+		private final Object node;
+		private final int level;
+
+		/**
+		 * Construct.
+		 * 
+		 * @param node
+		 * @param level
+		 */
+		public JunctionBorder(Object node, int level)
+		{
+			this.node = node;
+			this.level = level;
+		}
+
+		/**
+		 * @see org.apache.wicket.behavior.AbstractBehavior#onRendered(org.apache.wicket.Component)
+		 */
+		@Override
+		public void afterRender(final Component component)
+		{
+			component.getResponse().write("</td>");
+		}
+
+		/**
+		 * @see org.apache.wicket.behavior.Behavior#beforeRender(org.apache.wicket.Component)
+		 */
+		@Override
+		public void beforeRender(final Component component)
+		{
+			Response response = component.getResponse();
+			Object parent = getParentNode(node);
+
+			CharSequence classes[] = new CharSequence[level];
+			for (int i = 0; i < level; ++i)
+			{
+				if (parent == null || isNodeLast(parent))
+				{
+					classes[i] = "spacer";
+				}
+				else
+				{
+					classes[i] = "line";
+				}
+
+				parent = getParentNode(parent);
+			}
+
+			for (int i = level - 1; i >= 0; --i)
+			{
+				response.write("<td class=\"" + classes[i] + "\"><span></span></td>");
+			}
+
+			if (isNodeLast(node))
+			{
+				response.write("<td class=\"half-line\">");
+			}
+			else
+			{
+				response.write("<td class=\"line\">");
+			}
+		}
+	}
+
+	/**
+	 * Creates the junction link for given node. Also (optionally) creates the junction image. If
+	 * the node is a leaf (it has no children), the created junction link is non-functional.
+	 * 
+	 * @param parent
+	 *            parent component of the link
+	 * @param id
+	 *            wicket:id of the component
+	 * @param node
+	 *            tree node for which the link should be created.
+	 * @return The link component
+	 */
+	protected Component newJunctionLink(MarkupContainer parent, final String id, final Object node)
+	{
+		final MarkupContainer junctionLink;
+
+		if (isLeaf(node) == false)
+		{
+			junctionLink = newLink(id, new ILinkCallback()
+			{
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				public void onClick(AjaxRequestTarget target)
+				{
+					if (isNodeExpanded(node))
+					{
+						getTreeState().collapseNode(node);
+					}
+					else
+					{
+						getTreeState().expandNode(node);
+					}
+					onJunctionLinkClicked(target, node);
+
+					if (target != null)
+					{
+						updateTree(target);
+					}
+				}
+			});
+			junctionLink.add(new Behavior()
+			{
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				public void onComponentTag(Component component, ComponentTag tag)
+				{
+					if (isNodeExpanded(node))
+					{
+						tag.put("class", "junction-open");
+					}
+					else
+					{
+						tag.put("class", "junction-closed");
+					}
+				}
+			});
+		}
+		else
+		{
+			junctionLink = new WebMarkupContainer(id)
+			{
+				private static final long serialVersionUID = 1L;
+
+				/**
+				 * @see org.apache.wicket.Component#onComponentTag(org.apache.wicket.markup.ComponentTag)
+				 */
+				@Override
+				protected void onComponentTag(ComponentTag tag)
+				{
+					super.onComponentTag(tag);
+					tag.setName("span");
+					tag.put("class", "junction-corner");
+				}
+			};
+
+		}
+
+		return junctionLink;
+	}
+
+	/**
+	 * Callback function called after user clicked on an junction link. The node has already been
+	 * expanded/collapsed (depending on previous status).
+	 * 
+	 * @param target
+	 *            Request target - may be null on non-ajax call
+	 * 
+	 * @param node
+	 *            Node for which this callback is relevant
+	 */
+	protected void onJunctionLinkClicked(AjaxRequestTarget target, Object node)
+	{
+	}
+
+	/**
+	 * Helper class for calling an action from a link.
+	 * 
+	 * @author Matej Knopp
+	 */
+	public interface ILinkCallback extends IAjaxLink, IClusterable
+	{
+	}
+
+	/**
+	 * Creates a link of type specified by current linkType. When the links is clicked it calls the
+	 * specified callback.
+	 * 
+	 * @param id
+	 *            The component id
+	 * @param callback
+	 *            The link call back. {@code null} is passed for its onClick(AjaxRequestTarget) for
+	 *            {@link LinkType#REGULAR} and eventually for {@link LinkType#AJAX_FALLBACK}.
+	 * @return The link component
+	 */
+	public MarkupContainer newLink(String id, final ILinkCallback callback)
+	{
+		if (getLinkType() == LinkType.REGULAR)
+		{
+			return new Link<Void>(id)
+			{
+				private static final long serialVersionUID = 1L;
+
+				/**
+				 * @see org.apache.wicket.markup.html.link.Link#onClick()
+				 */
+				@Override
+				public void onClick()
+				{
+					callback.onClick(null);
+				}
+			};
+		}
+		else if (getLinkType() == LinkType.AJAX)
+		{
+			return new AjaxLink<Void>(id)
+			{
+				private static final long serialVersionUID = 1L;
+
+				/**
+				 * @see org.apache.wicket.ajax.markup.html.AjaxLink#onClick(org.apache.wicket.ajax.AjaxRequestTarget)
+				 */
+				@Override
+				public void onClick(AjaxRequestTarget target)
+				{
+					callback.onClick(target);
+				}
+			};
+		}
+		else
+		{
+			return new AjaxFallbackLink<Void>(id)
+			{
+				private static final long serialVersionUID = 1L;
+
+				/**
+				 * @see org.apache.wicket.ajax.markup.html.AjaxFallbackLink#onClick(org.apache.wicket.ajax.AjaxRequestTarget)
+				 */
+				@Override
+				public void onClick(AjaxRequestTarget target)
+				{
+					callback.onClick(target);
+				}
+			};
+		}
+	}
+
+	/**
+	 * Returns the current type of links on tree items.
+	 * 
+	 * @return The link type
+	 */
+	public LinkType getLinkType()
+	{
+		return linkType;
+	}
+
+	/**
+	 * Sets the type of links on tree items. After the link type is changed, the whole tree must be
+	 * rebuilt (call invalidateAll).
+	 * 
+	 * @param linkType
+	 *            type of links
+	 */
+	public void setLinkType(LinkType linkType)
+	{
+		if (this.linkType != linkType)
+		{
+			this.linkType = linkType;
+		}
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.AbstractTree#isForceRebuildOnSelectionChange()
+	 */
+	@Override
+	protected boolean isForceRebuildOnSelectionChange()
+	{
+		return false;
+	}
+
+	@Override
+	public void renderHead(IHeaderResponse response)
+	{
+		super.renderHead(response);
+		ResourceReference css = getCSS();
+		if (css != null)
+		{
+			response.render(CssHeaderItem.forReference(css));
+		}
+
+	}
+
+	private LinkType linkType = LinkType.AJAX;
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultAbstractTree.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultAbstractTree.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultAbstractTree.java
index 34d0772..c46c05d 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultAbstractTree.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultAbstractTree.java
@@ -32,9 +32,6 @@ import org.apache.wicket.markup.head.CssHeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.link.Link;
-import org.apache.wicket.markup.html.tree.AbstractTree;
-import org.apache.wicket.markup.html.tree.LinkType;
-import org.apache.wicket.markup.html.tree.WicketTreeModel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.Response;
@@ -57,6 +54,7 @@ import org.apache.wicket.request.resource.ResourceReference;
  * 
  * @author Matej Knopp
  */
+@Deprecated
 public abstract class DefaultAbstractTree extends AbstractTree
 {
 	private static final long serialVersionUID = 1L;

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultTreeState.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultTreeState.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultTreeState.java
new file mode 100644
index 0000000..a26113a
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/DefaultTreeState.java
@@ -0,0 +1,297 @@
+/*
+ * 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.tree;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.IClusterable;
+import org.apache.wicket.model.IDetachable;
+
+/**
+ * Default implementation of TreeState.
+ * <p>
+ * This implementation tries to be as lightweight as possible. By default all nodes are collapsed.
+ * 
+ * @author Matej Knopp
+ */
+public class DefaultTreeState implements ITreeState, IClusterable, IDetachable
+{
+	private static final long serialVersionUID = 1L;
+
+	/** Whether multiple selections can be done. */
+	private boolean allowSelectMultiple = false;
+
+	/** Tree state listeners. */
+	private final List<ITreeStateListener> listeners = new ArrayList<ITreeStateListener>(1);
+
+	/**
+	 * set of nodes which are collapsed or expanded (depends on nodesCollapsed variable).
+	 */
+	private final Set<Object> nodes = new HashSet<Object>();
+
+	/** Whether the nodes set should be treated as set of collapsed or expanded nodes. */
+	private boolean nodesCollapsed = false; // by default treat the node set as expanded nodes
+
+	/** Set selected nodes. */
+	private final Set<Object> selectedNodes = new HashSet<Object>();
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeState#addTreeStateListener(org.apache.wicket.extensions.markup.html.tree.ITreeStateListener)
+	 */
+	@Override
+	public void addTreeStateListener(ITreeStateListener l)
+	{
+		if (listeners.contains(l) == false)
+		{
+			listeners.add(l);
+		}
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeState#collapseAll()
+	 */
+	@Override
+	public void collapseAll()
+	{
+		if (nodes.isEmpty() && nodesCollapsed == false)
+		{
+			// all nodes are already collapsed, do nothing
+		}
+		else
+		{
+			// clear all nodes from the set and sets the nodes as expanded
+			nodes.clear();
+			nodesCollapsed = false;
+
+			for (ITreeStateListener listener : listeners)
+				listener.allNodesCollapsed();
+		}
+	}
+
+	@Override
+	public void collapseNode(Object node)
+	{
+		if (nodesCollapsed == true)
+		{
+			nodes.add(node);
+		}
+		else
+		{
+			nodes.remove(node);
+		}
+
+		for (ITreeStateListener listener : listeners)
+			listener.nodeCollapsed(node);
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeState#expandAll()
+	 */
+	@Override
+	public void expandAll()
+	{
+		if (nodes.isEmpty() && nodesCollapsed == true)
+		{
+			// all nodes are already expanded, do nothing
+		}
+		else
+		{
+			// clear node set and set nodes policy as collapsed
+			nodes.clear();
+			nodesCollapsed = true;
+
+			for (ITreeStateListener listener : listeners)
+				listener.allNodesExpanded();
+		}
+	}
+
+	@Override
+	public void expandNode(Object node)
+	{
+		if (nodesCollapsed == false)
+		{
+			nodes.add(node);
+		}
+		else
+		{
+			nodes.remove(node);
+		}
+
+		for (ITreeStateListener listener : listeners)
+			listener.nodeExpanded(node);
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeState#getSelectedNodes()
+	 */
+	@Override
+	public Collection<Object> getSelectedNodes()
+	{
+		return Collections.unmodifiableList(new ArrayList<Object>(selectedNodes));
+	}
+
+	protected void removeSelectedNodeSilent(Object node)
+	{
+		selectedNodes.remove(node);
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeState#isAllowSelectMultiple()
+	 */
+	@Override
+	public boolean isAllowSelectMultiple()
+	{
+		return allowSelectMultiple;
+	}
+
+	@Override
+	public boolean isNodeExpanded(Object node)
+	{
+		if (nodesCollapsed == false)
+		{
+			return nodes.contains(node);
+		}
+		else
+		{
+			return nodes.contains(node) == false;
+		}
+	}
+
+	@Override
+	public boolean isNodeSelected(Object node)
+	{
+		return selectedNodes.contains(node);
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeState#removeTreeStateListener(org.apache.wicket.extensions.markup.html.tree.ITreeStateListener)
+	 */
+	@Override
+	public void removeTreeStateListener(ITreeStateListener l)
+	{
+		listeners.remove(l);
+	}
+
+
+	/**
+	 * If <code>node</code> is currently selected, it will be deselected and the
+	 * <code>nodeUnselected</code> method will be called on all registered
+	 * <code>ITreeStateListeners</code>.
+	 * 
+	 * @param node
+	 *            the node to be deselected
+	 */
+	private void deselectNode(Object node)
+	{
+		if (selectedNodes.remove(node))
+		{
+			for (ITreeStateListener listener : listeners.toArray(new ITreeStateListener[listeners.size()]))
+			{
+				listener.nodeUnselected(node);
+			}
+		}
+	}
+
+	/**
+	 * Selects <code>node</code> and calls the <code>nodeSelected</code> method on all registered
+	 * <code>ITreeStateListeners</code>. If <code>isAllowSelectMultiple</code> is <code>false</code>
+	 * , any currently selected nodes are deselected.
+	 * 
+	 * @param node
+	 *            the node to be selected
+	 */
+	private void selectNode(Object node)
+	{
+		// if multiple selections are not allowed, deselect current selections
+		if (selectedNodes.size() > 0 && !isAllowSelectMultiple())
+		{
+			for (Object currentlySelectedNode : selectedNodes.toArray())
+			{
+				if (!currentlySelectedNode.equals(node))
+				{
+					deselectNode(currentlySelectedNode);
+				}
+			}
+		}
+
+		if (!selectedNodes.contains(node))
+		{
+			selectedNodes.add(node);
+			for (ITreeStateListener listener : listeners.toArray(new ITreeStateListener[listeners.size()]))
+			{
+				listener.nodeSelected(node);
+			}
+		}
+	}
+
+	/**
+	 * Selects or deselects <code>node</code> and calls the corresponding method on all registered
+	 * <code>ITreeStateListeners</code>. If <code>isAllowSelectMultiple</code> is <code>false</code>
+	 * , any currently selected nodes are deselected.
+	 * 
+	 * @param node
+	 *            the node to be selected
+	 * @param selected
+	 *            true if node is to be selected, false if node is to be deselected
+	 */
+	@Override
+	public void selectNode(Object node, boolean selected)
+	{
+		if (selected)
+		{
+			selectNode(node);
+		}
+		else
+		{
+			deselectNode(node);
+		}
+	}
+
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.ITreeState#setAllowSelectMultiple(boolean)
+	 */
+	@Override
+	public void setAllowSelectMultiple(boolean value)
+	{
+		allowSelectMultiple = value;
+	}
+
+	@Override
+	public void detach()
+	{
+		for (Object node : nodes)
+		{
+			if (node instanceof IDetachable)
+			{
+				((IDetachable)node).detach();
+			}
+		}
+		for (Object node : selectedNodes)
+		{
+			if (node instanceof IDetachable)
+			{
+				((IDetachable)node).detach();
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeState.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeState.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeState.java
new file mode 100644
index 0000000..b06b671
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeState.java
@@ -0,0 +1,128 @@
+/*
+ * 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.tree;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ * Tree state holds information about a tree such as which nodes are expanded / collapsed and which
+ * nodes are selected, It can also fire callbacks on listener in case any of the information
+ * changed.
+ * 
+ * @author Matej Knopp
+ */
+@Deprecated
+public interface ITreeState extends Serializable
+{
+	/**
+	 * Adds a tree state listener. On state change events on the listener are fired.
+	 * 
+	 * @param l
+	 *            Listener to add
+	 */
+	void addTreeStateListener(ITreeStateListener l);
+
+	/**
+	 * Collapses all nodes of the tree.
+	 */
+	void collapseAll();
+
+	/**
+	 * Collapses the given node.
+	 * 
+	 * @param node
+	 *            Node to collapse
+	 */
+	void collapseNode(Object node);
+
+	/**
+	 * Expands all nodes of the tree.
+	 */
+	void expandAll();
+
+	/**
+	 * Expands the given node.
+	 * 
+	 * @param node
+	 *            Node to expand
+	 */
+	void expandNode(Object node);
+
+	/**
+	 * Returns the collection of all selected nodes.
+	 * 
+	 * @return The collection of selected nodes
+	 */
+	Collection<Object> getSelectedNodes();
+
+	/**
+	 * Returns whether multiple nodes can be selected.
+	 * 
+	 * @return True if multiple nodes can be selected
+	 */
+	boolean isAllowSelectMultiple();
+
+	/**
+	 * Returns true if the given node is expanded.
+	 * 
+	 * @param node
+	 *            The node to inspect
+	 * @return True if the node is expanded
+	 */
+	boolean isNodeExpanded(Object node);
+
+	/**
+	 * Returns true if the given node is selected, false otherwise.
+	 * 
+	 * @param node
+	 *            The node to inspect
+	 * @return True if the node is selected
+	 */
+	boolean isNodeSelected(Object node);
+
+	/**
+	 * Removes a tree state listener.
+	 * 
+	 * @param l
+	 *            The listener to remove
+	 */
+	void removeTreeStateListener(ITreeStateListener l);
+
+
+	/**
+	 * Marks given node as selected (or unselected) according to the selected value.
+	 * <p>
+	 * If tree is in single selection mode and a new node is selected, old node is automatically
+	 * unselected (and the event is fired on listeners).
+	 * 
+	 * @param node
+	 *            The node to select or deselect
+	 * @param selected
+	 *            If true, the node will be selected, otherwise, the node will be unselected
+	 */
+	void selectNode(Object node, boolean selected);
+
+	/**
+	 * Sets whether multiple nodes can be selected.
+	 * 
+	 * @param value
+	 *            If true, multiple nodes can be selected. If false, only one node at a time can be
+	 *            selected
+	 */
+	void setAllowSelectMultiple(boolean value);
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeStateListener.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeStateListener.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeStateListener.java
new file mode 100644
index 0000000..ad6fb2b
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/ITreeStateListener.java
@@ -0,0 +1,69 @@
+/*
+ * 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.tree;
+
+import java.io.Serializable;
+
+/**
+ * Methods this interface are called when tree state is changing.
+ * 
+ * @author Matej Knopp
+ */
+@Deprecated
+public interface ITreeStateListener extends Serializable
+{
+	/**
+	 * Fired when all nodes are collapsed.
+	 */
+	void allNodesCollapsed();
+
+	/**
+	 * Fired when all nodes are expanded.
+	 */
+	void allNodesExpanded();
+
+	/**
+	 * Fired when given node is collapsed.
+	 * 
+	 * @param node
+	 *            The node that was collapsed
+	 */
+	void nodeCollapsed(Object node);
+
+	/**
+	 * Fired when given node is expanded.
+	 * 
+	 * @param node
+	 */
+	void nodeExpanded(Object node);
+
+	/**
+	 * Fired when given node gets selected.
+	 * 
+	 * @param node
+	 *            The node that was selected
+	 */
+	void nodeSelected(Object node);
+
+	/**
+	 * Fired when given node gets unselected.
+	 * 
+	 * @param node
+	 *            The node that was unselected
+	 */
+	void nodeUnselected(Object node);
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.html
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.html b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.html
new file mode 100644
index 0000000..2c10b30
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.html
@@ -0,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 class="icon-panel"><tr><td><img wicket:id="icon" /></td><td wicket:id="content" class="content"></td></tr></table>
+</wicket:panel>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.java
new file mode 100644
index 0000000..76b339e
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelIconPanel.java
@@ -0,0 +1,188 @@
+/*
+ * 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.tree;
+
+import javax.swing.tree.TreeModel;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.image.Image;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.resource.PackageResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+
+/**
+ * Simple panel that contains an icon next to a label.
+ * 
+ * @author Matej Knopp
+ */
+public class LabelIconPanel extends Panel
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Constructs the panel.
+	 * 
+	 * @param id
+	 *            component id
+	 * @param model
+	 *            model that is used to access the TreeNode
+	 * @param tree
+	 */
+	public LabelIconPanel(String id, IModel<Object> model, BaseTree tree)
+	{
+		super(id, model);
+
+		addComponents(model, tree);
+	}
+
+	/**
+	 * Adds the icon and content components to the panel. You can override this method if you want
+	 * custom components to be added
+	 * 
+	 * @param model
+	 *            model that can be used to retrieve the TreeNode
+	 * 
+	 * @param tree
+	 */
+	protected void addComponents(IModel<Object> model, BaseTree tree)
+	{
+		add(newImageComponent("icon", tree, model));
+		add(newContentComponent("content", tree, model));
+	}
+
+	/**
+	 * Creates the icon component for the node
+	 * 
+	 * @param componentId
+	 * @param tree
+	 * @param model
+	 * @return icon image component
+	 */
+	protected Component newImageComponent(String componentId, final BaseTree tree,
+		final IModel<Object> model)
+	{
+		return new Image(componentId)
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected ResourceReference getImageResourceReference()
+			{
+				return LabelIconPanel.this.getImageResourceReference(tree, model.getObject());
+			}
+
+			@Override
+			protected boolean shouldAddAntiCacheParameter()
+			{
+				return false;
+			}
+		};
+	}
+
+	/**
+	 * Creates the content component (label in this case) for the node
+	 * 
+	 * @param componentId
+	 * @param tree
+	 * @param model
+	 * @return content component
+	 */
+	protected Component newContentComponent(String componentId, BaseTree tree, IModel<?> model)
+	{
+		return new Label(componentId, model);
+	}
+
+	/**
+	 * Returns the image resource reference based on the give tree node type.
+	 * 
+	 * @param tree
+	 * @param node
+	 * @return image resource reference
+	 */
+	protected ResourceReference getImageResourceReference(BaseTree tree, Object node)
+	{
+		TreeModel model = (TreeModel)tree.getDefaultModelObject();
+		if (model.isLeaf(node))
+		{
+			return getResourceItemLeaf(node);
+		}
+		else
+		{
+			if (tree.getTreeState().isNodeExpanded(node))
+			{
+				return getResourceFolderOpen(node);
+			}
+			else
+			{
+				return getResourceFolderClosed(node);
+			}
+		}
+	}
+
+	/**
+	 * Optional method for wrapping (creating an intermediate model) for the tree node model
+	 * 
+	 * @param nodeModel
+	 * @return wrapped model
+	 */
+	protected IModel<Object> wrapNodeModel(IModel<Object> nodeModel)
+	{
+		return nodeModel;
+	}
+
+	/**
+	 * Returns resource reference for closed folder icon.
+	 * 
+	 * @param node
+	 * @return resource reference
+	 */
+	protected ResourceReference getResourceFolderClosed(Object node)
+	{
+		return RESOURCE_FOLDER_CLOSED;
+	}
+
+	/**
+	 * Returns resource reference for open folder icon.
+	 * 
+	 * @param node
+	 * @return resource reference
+	 */
+	protected ResourceReference getResourceFolderOpen(Object node)
+	{
+		return RESOURCE_FOLDER_OPEN;
+	}
+
+	/**
+	 * Returns resource reference for a leaf icon.
+	 * 
+	 * @param node
+	 * @return resource reference
+	 */
+	protected ResourceReference getResourceItemLeaf(Object node)
+	{
+		return RESOURCE_ITEM;
+	}
+
+	private static final ResourceReference RESOURCE_FOLDER_OPEN = new PackageResourceReference(
+		LabelIconPanel.class, "res/folder-open.gif");
+	private static final ResourceReference RESOURCE_FOLDER_CLOSED = new PackageResourceReference(
+		LabelIconPanel.class, "res/folder-closed.gif");
+	private static final ResourceReference RESOURCE_ITEM = new PackageResourceReference(
+		LabelIconPanel.class, "res/item.gif");
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelTree.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelTree.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelTree.java
new file mode 100644
index 0000000..2c1e6ff
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LabelTree.java
@@ -0,0 +1,110 @@
+/*
+ * 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.tree;
+
+import javax.swing.tree.TreeModel;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+
+/**
+ * Simple tree component that uses label to render tree node.
+ * 
+ * @author Matej Knopp
+ */
+@Deprecated
+public class LabelTree extends BaseTree
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 */
+	public LabelTree(String id)
+	{
+		super(id);
+	}
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 * @param model
+	 *            model that provides the {@link TreeModel}
+	 */
+	public LabelTree(String id, IModel<? extends TreeModel> model)
+	{
+		super(id, model);
+	}
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 * @param model
+	 *            Tree model
+	 */
+	public LabelTree(String id, TreeModel model)
+	{
+		super(id, new WicketTreeModel());
+		setModelObject(model);
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.BaseTree#newNodeComponent(java.lang.String,
+	 *      org.apache.wicket.model.IModel)
+	 */
+	@Override
+	protected Component newNodeComponent(String id, IModel<Object> model)
+	{
+		return new LabelIconPanel(id, model, this)
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected Component newContentComponent(String componentId, BaseTree tree,
+				IModel<?> model)
+			{
+				return super.newContentComponent(componentId, tree, getNodeTextModel(model));
+			}
+		};
+	}
+
+	/**
+	 * Provides the model that will be used to feed the node text.
+	 * 
+	 * Example implementation: <code>return new PropertyModel(nodeModel, "object.name");</code>
+	 * which will translate to <code>YourTreeNodeSubclass.getObject().getName();</code>
+	 * 
+	 * NOTE: remember that the nodeModel represents the TreeNode object, not the model object inside
+	 * it
+	 * 
+	 * NOTE: this method is called from the default implementation of
+	 * {@link #newNodeComponent(String, IModel)}, so if it is subclassed this method may no longer
+	 * be called unless the subclassing code maintains the callback explicitly
+	 * 
+	 * @param nodeModel
+	 *            model representing the current tree node
+	 * @return model used for text
+	 */
+	protected IModel<?> getNodeTextModel(IModel<?> nodeModel)
+	{
+		return nodeModel;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.html
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.html b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.html
new file mode 100644
index 0000000..a18bbec
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.html
@@ -0,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 class="icon-panel"><tr><td wicket:id="iconLink"><img wicket:id="icon" class="icon"/></td><td class="content"><a wicket:id="contentLink"><span wicket:id="content" class="content"></span></a></td></tr></table>
+</wicket:panel>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.java
new file mode 100644
index 0000000..ed230d3
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkIconPanel.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tree;
+
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.model.IModel;
+
+/**
+ * Simple panel that contains a link with icon and a link with a label.
+ * 
+ * @author Matej Knopp
+ */
+public class LinkIconPanel extends LabelIconPanel
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Constructs the panel.
+	 * 
+	 * @param id
+	 *            component id
+	 * @param model
+	 *            model that is used to access the TreeNode
+	 * @param tree
+	 */
+	public LinkIconPanel(String id, IModel<Object> model, BaseTree tree)
+	{
+		super(id, model, tree);
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.LabelIconPanel#addComponents(org.apache.wicket.model.IModel,
+	 *      org.apache.wicket.extensions.markup.html.tree.BaseTree)
+	 */
+	@Override
+	protected void addComponents(final IModel<Object> model, final BaseTree tree)
+	{
+		BaseTree.ILinkCallback callback = new BaseTree.ILinkCallback()
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void onClick(AjaxRequestTarget target)
+			{
+				onNodeLinkClicked(model.getObject(), tree, target);
+			}
+		};
+
+		MarkupContainer link = tree.newLink("iconLink", callback);
+		add(link);
+		link.add(newImageComponent("icon", tree, model));
+
+		link = tree.newLink("contentLink", callback);
+		add(link);
+		link.add(newContentComponent("content", tree, model));
+	}
+
+	/**
+	 * Handler invoked when the link is clicked. By default makes the node selected
+	 * 
+	 * @param node
+	 * @param tree
+	 * @param target
+	 */
+	protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target)
+	{
+		tree.getTreeState().selectNode(node, !tree.getTreeState().isNodeSelected(node));
+
+		if (target != null)
+		{
+			tree.updateTree(target);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkTree.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkTree.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkTree.java
new file mode 100644
index 0000000..5b917df
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkTree.java
@@ -0,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.extensions.markup.html.tree;
+
+import javax.swing.tree.TreeModel;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.model.IModel;
+
+/**
+ * Simple tree component that provides node panel with link allowing user to select individual
+ * nodes.
+ * 
+ * @author Matej Knopp
+ */
+@Deprecated
+public class LinkTree extends LabelTree
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 */
+	public LinkTree(String id)
+	{
+		super(id);
+	}
+
+	/**
+	 * 
+	 * Construct.
+	 * 
+	 * @param id
+	 * @param model
+	 *            model that provides the {@link TreeModel}
+	 */
+	public LinkTree(String id, IModel<? extends TreeModel> model)
+	{
+		super(id, model);
+	}
+
+	/**
+	 * 
+	 * Construct.
+	 * 
+	 * @param id
+	 * @param model
+	 *            Tree model
+	 */
+	public LinkTree(String id, TreeModel model)
+	{
+		super(id, new WicketTreeModel());
+		setModelObject(model);
+	}
+
+	/**
+	 * @see org.apache.wicket.extensions.markup.html.tree.BaseTree#newNodeComponent(java.lang.String,
+	 *      org.apache.wicket.model.IModel)
+	 */
+	@Override
+	protected Component newNodeComponent(String id, IModel<Object> model)
+	{
+		return new LinkIconPanel(id, model, LinkTree.this)
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target)
+			{
+				super.onNodeLinkClicked(node, tree, target);
+				LinkTree.this.onNodeLinkClicked(node, tree, target);
+			}
+
+			@Override
+			protected Component newContentComponent(String componentId, BaseTree tree,
+				IModel<?> model)
+			{
+				return new Label(componentId, getNodeTextModel(model));
+			}
+		};
+	}
+
+	/**
+	 * Method invoked after the node has been selected / unselected.
+	 * 
+	 * @param node
+	 * @param tree
+	 * @param target
+	 */
+	protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target)
+	{
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkType.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkType.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkType.java
new file mode 100644
index 0000000..50cda47
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/LinkType.java
@@ -0,0 +1,62 @@
+/*
+ * 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.tree;
+
+import org.apache.wicket.util.lang.EnumeratedType;
+
+/**
+ * The type of junction links and node selection links.
+ * <dl>
+ * <dt>Regular link</dt>
+ * <dd>Non-ajax link, always refreshes the whole page. Works with javascript disabled.</dd>
+ * <dt>Ajax link</dt>
+ * <dd>Links that supports partial updates. Doesn't work with javascript disabled</dd>
+ * <dt>Ajax fallback link</dt>
+ * <dd>Link that supports partial updates. With javascript disabled acts like regular link. The
+ * drawback is that generated url (thus the entire html) is larger then using the other two</dd>
+ * </dl>
+ */
+public final class LinkType extends EnumeratedType
+{
+
+	/** partial updates with no fallback. */
+	public static final LinkType AJAX = new LinkType("AJAX");
+
+	/**
+	 * partial updates that falls back to a regular link in case the client does not support
+	 * javascript.
+	 */
+	public static final LinkType AJAX_FALLBACK = new LinkType("AJAX_FALLBACK");
+
+	/**
+	 * non-ajax version that always re-renders the whole page.
+	 */
+	public static final LinkType REGULAR = new LinkType("REGULAR");
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param name
+	 *            the name of the type of the link
+	 */
+	public LinkType(String name)
+	{
+		super(name);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/Tree.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/Tree.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/Tree.java
index 41a0d57..b9337b0 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/Tree.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/Tree.java
@@ -34,6 +34,7 @@ import org.apache.wicket.model.IModel;
  * 
  * @author Matej Knopp
  */
+@Deprecated
 public class Tree extends DefaultAbstractTree
 {
 	private static final long serialVersionUID = 1L;

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/WicketTreeModel.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/WicketTreeModel.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/WicketTreeModel.java
new file mode 100644
index 0000000..de9e758
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/WicketTreeModel.java
@@ -0,0 +1,56 @@
+/*
+ * 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.tree;
+
+import javax.swing.tree.TreeModel;
+
+import org.apache.wicket.model.util.GenericBaseModel;
+
+/**
+ * @author Timo Rantalaiho
+ */
+@Deprecated
+public class WicketTreeModel extends GenericBaseModel<TreeModel>
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Construct.
+	 */
+	public WicketTreeModel()
+	{
+	}
+
+	/**
+	 * Construct.
+	 * 
+	 * @param treeModel
+	 */
+	public WicketTreeModel(final TreeModel treeModel)
+	{
+		setObject(treeModel);
+	}
+
+	/**
+	 * @see org.apache.wicket.model.util.GenericBaseModel#createSerializableVersionOf(java.lang.Object)
+	 */
+	@Override
+	protected TreeModel createSerializableVersionOf(TreeModel object)
+	{
+		return object;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/package.html
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/package.html b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/package.html
new file mode 100644
index 0000000..75c7655
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/package.html
@@ -0,0 +1,27 @@
+<!--
+   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.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML 3.2 Final//NL">
+<html>
+<head>
+<title>wicket.extensions.markup.html.tree package</title>
+</head>
+<body>
+<p>
+Package for Tree components.
+</p>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree-images.png
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree-images.png b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree-images.png
new file mode 100644
index 0000000..361644d
Binary files /dev/null and b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree-images.png differ

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree.css
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree.css b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree.css
new file mode 100644
index 0000000..a5c0b9a
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/base-tree.css
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+table.wicket-tree-content {
+	border-collapse: collapse;
+	empty-cells: show;
+}
+
+table.wicket-tree-content,
+table.wicket-tree-content td,
+table.wicket-tree-content tr,
+table.wicket-tree-content th {
+	padding: 0;
+	margin: 0;
+}
+
+table.wicket-tree-content td.spacer {
+	width: 18px;
+	height: 100%;
+}
+
+table.wicket-tree-content td.spacer span,
+table.wicket-tree-content td.line span {
+	display: block;
+	width: 18px;
+	height: 18px;
+}
+
+table.wicket-tree-content td.line {
+	width: 18px;
+	height: 100%;
+	background: url("base-tree-images.png") repeat-y -36px center;
+}
+
+table.wicket-tree-content td.half-line {
+	width: 18px;
+	height: 100%;
+	background: url("base-tree-images.png") no-repeat -72px center;
+}
+
+table.wicket-tree-content a.junction-open,
+table.wicket-tree-content a.junction-closed,
+table.wicket-tree-content span.junction-corner {
+	width: 18px;
+	height: 18px;
+	display: block;
+	background: url("base-tree-images.png") no-repeat;
+	margin: 0;
+	padding: 0;
+	border-width: 0;
+}
+
+table.wicket-tree-content a.junction-open {
+	background-position: -18px center;
+}
+
+table.wicket-tree-content a.junction-closed {
+	background-position: 0 center;
+}
+
+table.wicket-tree-content span.junction-corner {
+	background-position: -54px center;
+}
+
+table.wicket-tree-content table.icon-panel {
+	border-collapse: collapse;
+	empty-cells: show;
+}
+
+table.wicket-tree-content table.icon-panel, 
+table.wicket-tree-content table.icon-panel tr,
+table.wicket-tree-content table.icon-panel td {
+	margin: 0;
+	padding: 0;
+}
+
+table.wicket-tree-content table.icon-panel img {
+	padding: 0;
+	margin: -1px 0 0 2px;
+	display: block;
+	border-width: 0;
+}
+
+table.wicket-tree-content.selected {
+	background-color: #E0E8FF;
+	font-weight: bold;	
+}
+
+table.wicket-tree-content.selected .content {
+	font-weight: bold;	
+}
+
+table.wicket-tree-content table.icon-panel a {
+	text-decoration: none;
+	color: #3311aa;
+}
+
+table.wicket-tree-content table.icon-panel a:hover {
+	text-decoration: underline;
+	color: #3311aa;
+}
+
+table.wicket-tree-content table.icon-panel img.icon {
+	cursor: pointer;
+}
+
+table.icon-panel {
+	width: 100%;
+}
+
+table.icon-panel td.content {
+	width: 100%;
+}
+
+table.icon-panel td.content a {
+	padding-right: 0.4em;
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/tree.js
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/tree.js b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/tree.js
new file mode 100644
index 0000000..0273dfe
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/res/tree.js
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+if (typeof(Wicket) == "undefined")
+	Wicket = { };
+
+Wicket.Tree = { };
+
+Wicket.Tree.askForReload = function()
+{
+   if (confirm("There was a problem updating the tree. It might be caused be the old page being cached by the browser. \n" +
+   "It is recommended to reload the page. Do you want to reload it?"))
+   {
+      window.location.reload();
+   }
+};
+
+Wicket.Tree.removeNodes = function(prefix, nodeList)
+{
+   var problem = false;
+   for (var i = 0; i < nodeList.length; i++)
+   {
+      var e = document.getElementById(prefix + nodeList[i]);
+      if (e != null)
+      {
+         e.parentNode.removeChild(e);
+      }
+      else
+      {
+         // while developing alert a warning
+         problem = true;
+         Wicket.Log.error("Can't find node with id " + prefix + nodeList[i] +
+         ". This shouldn't happen - possible bug in tree?");
+      }
+   }
+   if (problem == true)
+   {
+      Wicket.Tree.askForReload();
+   }
+};
+
+Wicket.Tree.createElement = function(elementId, afterId)
+{
+   var existing = Wicket.$(elementId);
+   if (typeof(existing) != "undefined" && existing != null)
+   {
+      Wicket.Tree.askForReload();
+   }
+
+   var after = document.getElementById(afterId);
+   var newNode = document.createElement(after.tagName);
+   newNode.setAttribute("id", elementId);
+
+   var p = after.parentNode;
+
+   for (var i = 0; i < p.childNodes.length; ++i)
+   {
+      if (after == p.childNodes[i])
+         break;
+   }
+   if (i == p.childNodes.length - 1)
+   {
+      p.appendChild(newNode);
+   }
+   else
+   {
+      p.insertBefore(newNode, p.childNodes[i + 1]);
+   }
+};
+
+Wicket.TreeTable = { };
+
+/* Javascript that resizes the tree table header so that it matches size of the content.
+   This is needed when the scrollbar next to content is show, so that the columns are 
+   properly aligned */
+Wicket.TreeTable.update = function(elementId)
+{
+
+   var element = document.getElementById(elementId);
+
+   if (element != null && typeof(element) != "undefined")
+   {
+
+      try
+      {
+
+         /// find the div containing the inner header div
+         var headerParent = element.getElementsByTagName("div")[1];
+
+         // find the inner header div
+         var header = headerParent.getElementsByTagName("div")[0];
+
+         // body div should be next div after header parent
+         var body = headerParent.nextSibling;
+
+         // interate until div is found
+         while (body.tagName != "DIV")
+         {
+            body = body.nextSibling;
+         }
+
+         // last check to find out if we are updating the right component
+         if (body.className == "wicket-tree-table-body")
+         {
+
+            // get the right padding from header - we need to substract it from new width
+            var padding;
+            if (document.defaultView && document.defaultView.getComputedStyle)
+            {
+               padding = document.defaultView.getComputedStyle(headerParent, '').getPropertyValue("padding-right");
+            } else if (headerParent.currentStyle)
+            {
+               padding = headerParent.currentStyle.paddingRight;
+            }
+            else
+            {
+               padding = 6;
+            }
+
+            padding = parseInt(padding, 10);
+
+            // set the new width
+            var w = (body.getElementsByTagName("div")[0].clientWidth - padding) + "px";
+
+            if (w == (-padding) + "px")
+            { // this can happen if the first row is hidden (e.g. rootless mode)
+               // try to get the width from second row
+               w = (body.getElementsByTagName("div")[1].clientWidth - padding) + "px";
+
+            }
+
+            if (w != "0px")
+            {
+               header.style.width = w;
+            }
+
+         }
+      }
+      catch (ignore)
+      {
+      }
+   }
+};
+
+Wicket.TreeTable.attached = new Object();
+
+Wicket.TreeTable.attachUpdate = function(treeTableId)
+{
+   // get the object that contains ids of elements on which the update method was already attached
+   var attached = Wicket.TreeTable.attached;
+
+   // force updating the element
+   Wicket.TreeTable.update(treeTableId);
+
+   // if the update has not been attached to this tree table yet...
+   if (typeof(attached[treeTableId]) == "undefined")
+   {
+      // ... attach it
+      attached[treeTableId] = window.setInterval(function()
+      {
+         Wicket.TreeTable.update(treeTableId);
+      }, 100);
+   }
+};
+

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/table/TreeTable.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/table/TreeTable.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/table/TreeTable.java
index 50fb913..ae8cc89 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/table/TreeTable.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/tree/table/TreeTable.java
@@ -23,16 +23,16 @@ import org.apache.wicket.Component;
 import org.apache.wicket.IClusterable;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.extensions.markup.html.tree.AbstractTree;
 import org.apache.wicket.extensions.markup.html.tree.DefaultAbstractTree;
+import org.apache.wicket.extensions.markup.html.tree.WicketTreeModel;
 import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation.Alignment;
 import org.apache.wicket.markup.ComponentTag;
-import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.Fragment;
-import org.apache.wicket.markup.html.tree.AbstractTree;
-import org.apache.wicket.markup.html.tree.WicketTreeModel;
 import org.apache.wicket.model.AbstractReadOnlyModel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.resource.PackageResourceReference;
@@ -45,6 +45,7 @@ import org.apache.wicket.request.resource.ResourceReference;
  * 
  * @author Matej Knopp
  */
+@Deprecated
 public class TreeTable extends DefaultAbstractTree
 {
 	/**
@@ -207,7 +208,7 @@ public class TreeTable extends DefaultAbstractTree
 			public void renderHead(final Component component, final IHeaderResponse response)
 			{
 				response.render(OnDomReadyHeaderItem.forScript("Wicket.TreeTable.attachUpdate(\"" +
-						getMarkupId() + "\")"));
+					getMarkupId() + "\")"));
 			}
 		});
 	}

http://git-wip-us.apache.org/repos/asf/wicket/blob/567a5932/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tree/TreeTest.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tree/TreeTest.java b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tree/TreeTest.java
new file mode 100644
index 0000000..68dab56
--- /dev/null
+++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/tree/TreeTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.tree;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.extensions.markup.html.tree.AbstractTree;
+import org.apache.wicket.extensions.markup.html.tree.LinkTree;
+import org.apache.wicket.markup.IMarkupResourceStreamProvider;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.junit.Test;
+
+/**
+ * @author Pedro Santos
+ */
+public class TreeTest extends WicketTestCase
+{
+
+	/**
+	 * Asserting that {@link AbstractTree#treeNodesInserted(javax.swing.event.TreeModelEvent)} adds
+	 * the new item to the dirtyItemsCreateDOM, since there is no parent node at client to be
+	 * recreated.
+	 * 
+	 * @see <a href="https://issues.apache.org/jira/browse/WICKET-3309">WICKET-3309</a>
+	 */
+	@Test
+	public void addChildOnRootAtAnOnRootLessTree()
+	{
+		TestPage testPage = new TestPage();
+		testPage.tree.setRootLess(true);
+		tester.startPage(testPage);
+		tester.clickLink("addToRoot", true);
+		assertTrue(tester.getLastResponseAsString().contains("rootChild"));
+	}
+
+	/**
+	 * Asserting that {@link AbstractTree#treeNodesInserted(javax.swing.event.TreeModelEvent)} don't
+	 * add and not presented node to the AJAX response by invalidating it.
+	 * 
+	 * @see <a href="https://issues.apache.org/jira/browse/WICKET-3309">WICKET-3309</a>
+	 */
+	@Test
+	public void addGrandchildOnRootAtAnRootLessTree()
+	{
+		TestPage testPage = new TestPage();
+		testPage.tree.setRootLess(true);
+		DefaultMutableTreeNode rootChild = new DefaultMutableTreeNode("rootChild");
+		testPage.rootNode.add(rootChild);
+		testPage.tree.getTreeState().selectNode(rootChild, true);
+		tester.startPage(testPage);
+		tester.clickLink("addChildToSelected", true);
+		assertTrue(tester.getLastResponseAsString().contains("newNode"));
+	}
+
+	/**
+	 * Asserting the old leaf root node gets a junction link when adding its first child
+	 * 
+	 * @see <a href="https://issues.apache.org/jira/browse/WICKET-3449">WICKET-3449</a>
+	 */
+	@Test
+	public void junctionLinkRendered()
+	{
+		TestPage testPage = new TestPage();
+		tester.startPage(testPage);
+		tester.clickLink("addToRoot", true);
+		assertTrue(tester.getLastResponseAsString().contains("junctionLink"));
+	}
+
+	/** */
+	public static class TestPage extends WebPage implements IMarkupResourceStreamProvider
+	{
+		private static final long serialVersionUID = 1L;
+
+		AbstractTree tree;
+		DefaultTreeModel treeModel;
+		DefaultMutableTreeNode rootNode;
+
+		/** */
+		public TestPage()
+		{
+			rootNode = new DefaultMutableTreeNode("ROOT");
+			treeModel = new DefaultTreeModel(rootNode);
+			tree = new LinkTree("tree", treeModel);
+			add(tree);
+			add(new AjaxLink<Void>("addToRoot")
+			{
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				public void onClick(AjaxRequestTarget target)
+				{
+					DefaultMutableTreeNode child = new DefaultMutableTreeNode("rootChild");
+					treeModel.insertNodeInto(child, rootNode, rootNode.getChildCount());
+					tree.updateTree(target);
+				}
+			});
+			add(new AjaxLink<Void>("addChildToSelected")
+			{
+				private static final long serialVersionUID = 1L;
+
+				@Override
+				public void onClick(AjaxRequestTarget target)
+				{
+					DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)tree.getTreeState()
+						.getSelectedNodes()
+						.iterator()
+						.next();
+					treeModel.insertNodeInto(new DefaultMutableTreeNode("newNode"), selectedNode,
+						selectedNode.getChildCount());
+					tree.updateTree(target);
+				}
+			});
+		}
+
+		@Override
+		public IResourceStream getMarkupResourceStream(MarkupContainer container,
+			Class<?> containerClass)
+		{
+			return new StringResourceStream("<html><body>" + "<div wicket:id=\"tree\"></div>"
+				+ "<a wicket:id=\"addToRoot\"></a><a wicket:id=\"addChildToSelected\"></a>"
+				+ "</body></html>");
+		}
+
+	}
+}