001 package org.apache.myfaces.tobago.renderkit.html.scarborough.standard.tag;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one or more
005 * contributor license agreements. See the NOTICE file distributed with
006 * this work for additional information regarding copyright ownership.
007 * The ASF licenses this file to You under the Apache License, Version 2.0
008 * (the "License"); you may not use this file except in compliance with
009 * the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 import org.apache.myfaces.tobago.component.UITreeMenu;
021 import org.apache.myfaces.tobago.component.UITreeNode;
022 import org.apache.myfaces.tobago.context.Markup;
023 import org.apache.myfaces.tobago.context.ResourceManagerUtils;
024 import org.apache.myfaces.tobago.context.UserAgent;
025 import org.apache.myfaces.tobago.event.TreeExpansionEvent;
026 import org.apache.myfaces.tobago.internal.component.AbstractUITree;
027 import org.apache.myfaces.tobago.layout.Display;
028 import org.apache.myfaces.tobago.renderkit.LayoutComponentRendererBase;
029 import org.apache.myfaces.tobago.renderkit.css.Classes;
030 import org.apache.myfaces.tobago.renderkit.css.Style;
031 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
032 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
033 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
034 import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
035 import org.apache.myfaces.tobago.util.ComponentUtils;
036 import org.apache.myfaces.tobago.util.VariableResolverUtils;
037 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
038 import org.slf4j.Logger;
039 import org.slf4j.LoggerFactory;
040
041 import javax.faces.component.NamingContainer;
042 import javax.faces.component.UIComponent;
043 import javax.faces.context.FacesContext;
044 import java.io.IOException;
045 import java.util.Map;
046
047 public class TreeMenuNodeRenderer extends LayoutComponentRendererBase {
048
049 private static final Logger LOG = LoggerFactory.getLogger(TreeMenuNodeRenderer.class);
050
051 @Override
052 public void decode(FacesContext facesContext, UIComponent component) {
053
054 UITreeNode node = (UITreeNode) component;
055
056 super.decode(facesContext, node);
057
058 if (ComponentUtils.isOutputOnly(node)) {
059 return;
060 }
061
062 UITreeMenu tree = ComponentUtils.findAncestor(node, UITreeMenu.class);
063 String treeId = tree.getClientId(facesContext);
064 String nodeStateId = node.nodeStateId(facesContext);
065 Map<String, String> requestParameterMap = facesContext.getExternalContext().getRequestParameterMap();
066 String id = node.getClientId(facesContext);
067
068 // expand state
069 boolean expanded = Boolean.parseBoolean(requestParameterMap.get(id + ComponentUtils.SUB_SEPARATOR + "expanded"));
070 if (node.isExpanded() != expanded) {
071 new TreeExpansionEvent(node, node.isExpanded(), expanded).queue();
072 }
073
074 // marker
075 String marked = requestParameterMap.get(treeId + AbstractUITree.MARKED);
076 if (marked != null) {
077 String searchString = treeId + NamingContainer.SEPARATOR_CHAR + nodeStateId;
078 node.setMarked(marked.equals(searchString));
079 } else {
080 LOG.warn("This log message is help clarifying the occurrence of this else case.");
081 }
082 }
083
084 @Override
085 public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
086 super.prepareRender(facesContext, component);
087
088 final UITreeNode node = (UITreeNode) component;
089 if (node.isMarked()) {
090 node.setCurrentMarkup(Markup.MARKED.add(node.getCurrentMarkup()));
091 }
092 if (node.isFolder()) {
093 node.setCurrentMarkup(Markup.FOLDER.add(node.getCurrentMarkup()));
094 }
095 }
096
097 @Override
098 public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException {
099
100 final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
101 final UITreeNode node = (UITreeNode) component;
102 final UITreeMenu tree = ComponentUtils.findAncestor(node, UITreeMenu.class);
103
104 final boolean folder = node.isFolder();
105 final String id = node.getClientId(facesContext);
106 final int level = node.getLevel();
107 final boolean root = level == 0;
108 final boolean expanded = node.isExpanded() || level == 0;
109 final boolean showRoot = tree.isShowRoot();
110 final boolean ie6
111 = VariableResolverUtils.resolveClientProperties(facesContext).getUserAgent().equals(UserAgent.MSIE_6_0);
112
113 if (showRoot || !root) {
114 writer.startElement(HtmlElements.DIV, null);
115 writer.writeIdAttribute(id);
116 writer.writeClassAttribute(Classes.create(node));
117
118 if (folder) {
119 encodeExpandedHidden(writer, node, id, expanded);
120 }
121
122 if (folder) {
123 encodeIcon(facesContext, writer, expanded, node);
124 }
125
126 if (!folder && ie6) { // XXX IE6: without this hack, we can't click beside the label text. Why?
127 final String src = ResourceManagerUtils.getImageWithPath(facesContext, "image/1x1.gif");
128 writer.startElement(HtmlElements.IMG, null);
129 writer.writeClassAttribute(Classes.create(node, "icon"));
130 writer.writeAttribute(HtmlAttributes.SRC, src, false);
131 writer.writeAttribute(HtmlAttributes.ALT, "", false);
132 writer.writeStyleAttribute("width: 0px");
133 writer.endElement(HtmlElements.IMG);
134 }
135
136 RenderUtils.encodeChildren(facesContext, node);
137
138 writer.endElement(HtmlElements.DIV);
139 }
140
141 if (folder) {
142 writer.startElement(HtmlElements.DIV, null);
143 writer.writeIdAttribute(id + ComponentUtils.SUB_SEPARATOR + "content");
144 Style contentStyle = new Style();
145 contentStyle.setDisplay(expanded ? Display.BLOCK : Display.NONE);
146 writer.writeStyleAttribute(contentStyle);
147 }
148 }
149
150 private void encodeExpandedHidden(
151 TobagoResponseWriter writer, UITreeNode node, String clientId, boolean expanded) throws IOException {
152 writer.startElement(HtmlElements.INPUT, node);
153 writer.writeAttribute(HtmlAttributes.TYPE, "hidden", false);
154 writer.writeClassAttribute(Classes.create(node, "expanded", Markup.NULL));
155 writer.writeNameAttribute(clientId + ComponentUtils.SUB_SEPARATOR + "expanded");
156 writer.writeAttribute(HtmlAttributes.VALUE, Boolean.toString(expanded), false);
157 writer.endElement(HtmlElements.INPUT);
158 }
159
160 private void encodeIcon(FacesContext facesContext, TobagoResponseWriter writer, boolean expanded, UITreeNode node)
161 throws IOException {
162 final String srcOpen = ResourceManagerUtils.getImageWithPath(facesContext, "image/treeMenuOpen.gif");
163 final String srcClose = ResourceManagerUtils.getImageWithPath(facesContext, "image/treeMenuClose.gif");
164 final String src = expanded ? srcOpen : srcClose;
165 writer.startElement(HtmlElements.IMG, null);
166 writer.writeClassAttribute(Classes.create(node, "toggle"));
167 writer.writeAttribute(HtmlAttributes.SRC, src, false);
168 writer.writeAttribute(HtmlAttributes.SRCOPEN, srcOpen, false);
169 writer.writeAttribute(HtmlAttributes.SRCCLOSE, srcClose, false);
170 writer.writeAttribute(HtmlAttributes.ALT, "", false);
171 writer.endElement(HtmlElements.IMG);
172 }
173
174 @Override
175 public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException {
176 UITreeNode node = (UITreeNode) component;
177 if (node.isFolder()) {
178 TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
179 writer.endElement(HtmlElements.DIV);
180 }
181 }
182 }