001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.extensions.markup.html.repeater.tree.content;
018
019import java.util.Optional;
020
021import org.apache.wicket.ajax.AjaxRequestTarget;
022import org.apache.wicket.core.util.string.CssUtils;
023import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
024import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree.State;
025import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
026import org.apache.wicket.model.IModel;
027
028/**
029 * A typical folder representation of nodes in a tree.
030 * 
031 * The link is used to expand/collapse the tree depending on the {@link State} of the current node.
032 * Nodes without children are not clickable. Subclasses may change this behavior by overriding
033 * {@link #isClickable()} and {@link #onClick(Optional)}
034 * 
035 * @author svenmeier
036 */
037public class Folder<T> extends StyledLinkLabel<T>
038{
039        private static final long serialVersionUID = 1L;
040
041        public static final String OTHER_CSS_CLASS_KEY = CssUtils.key(Folder.class, "other");
042
043        public static final String CLOSED_CSS_CLASS_KEY = CssUtils.key(Folder.class, "closed");
044
045        public static final String OPEN_CSS_CLASS_KEY = CssUtils.key(Folder.class, "open");
046
047        public static final String SELECTED_CSS_CLASS_KEY = CssUtils.key(Folder.class, "selected");
048
049        private AbstractTree<T> tree;
050
051        public Folder(String id, AbstractTree<T> tree, IModel<T> model)
052        {
053                super(id, model);
054
055                this.tree = tree;
056        }
057
058        /**
059         * Clickable if node can be expanded/collapsed, i.e. has children.
060         * 
061         * @see ITreeProvider#hasChildren(Object)
062         */
063        @Override
064        protected boolean isClickable()
065        {
066                T t = getModelObject();
067
068                return tree.getProvider().hasChildren(t);
069        }
070
071        /**
072         * Toggle the node's {@link State} on click.
073         */
074        @Override
075        protected void onClick(Optional<AjaxRequestTarget> targetOptional)
076        {
077                T t = getModelObject();
078                if (tree.getState(t) == State.EXPANDED)
079                {
080                        tree.collapse(t);
081                }
082                else
083                {
084                        tree.expand(t);
085                }
086        }
087
088        /**
089         * Delegates to others methods depending wether the given model is a folder, expanded, collapsed
090         * or selected.
091         * 
092         * @see ITreeProvider#hasChildren(Object)
093         * @see AbstractTree#getState(Object)
094         * @see #isSelected()
095         * @see #getOpenStyleClass()
096         * @see #getClosedStyleClass()
097         * @see #getOtherStyleClass(Object)
098         * @see #getSelectedStyleClass()
099         */
100        @Override
101        protected String getStyleClass()
102        {
103                T t = getModelObject();
104
105                String styleClass;
106                if (tree.getProvider().hasChildren(t))
107                {
108                        if (tree.getState(t) == State.EXPANDED)
109                        {
110                                styleClass = getOpenStyleClass();
111                        }
112                        else
113                        {
114                                styleClass = getClosedStyleClass();
115                        }
116                }
117                else
118                {
119                        styleClass = getOtherStyleClass(t);
120                }
121
122                if (isSelected())
123                {
124                        styleClass += " " + getSelectedStyleClass();
125                }
126
127                return styleClass;
128        }
129
130        /**
131         * Optional attribute which decides if an additional "selected" style class should be rendered.
132         * 
133         * @return defaults to <code>false</code>
134         */
135        protected boolean isSelected()
136        {
137                return false;
138        }
139
140        /**
141         * Get a style class for nodes without children.
142         * 
143         * @param t
144         *            node
145         * @return CSS style class
146         * 
147         * @see ITreeProvider#hasChildren(Object)
148         */
149        protected String getOtherStyleClass(T t)
150        {
151                return getString(OTHER_CSS_CLASS_KEY);
152        }
153
154        /**
155         * Get a style class for anything other than closed or open folders.
156         * 
157         * @return CSS style class
158         * 
159         * @see State#COLLAPSED
160         */
161        protected String getClosedStyleClass()
162        {
163                return getString(CLOSED_CSS_CLASS_KEY);
164        }
165
166        /**
167         * Get a style class for anything other than closed or open folders.
168         * 
169         * @return CSS style class
170         * 
171         * @see State#EXPANDED
172         */
173        protected String getOpenStyleClass()
174        {
175                return getString(OPEN_CSS_CLASS_KEY);
176        }
177
178        /**
179         * Get a style class to render for a selected folder.
180         * 
181         * @return CSS style class
182         * 
183         * @see #isSelected()
184         */
185        protected String getSelectedStyleClass()
186        {
187                return getString(SELECTED_CSS_CLASS_KEY);
188        }
189}