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    }