View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.renderkit.html.scarborough.standard.tag;
21  
22  import org.apache.myfaces.tobago.component.Attributes;
23  import org.apache.myfaces.tobago.component.Facets;
24  import org.apache.myfaces.tobago.component.RendererTypes;
25  import org.apache.myfaces.tobago.component.UIMenuCommand;
26  import org.apache.myfaces.tobago.component.UISelectBooleanCheckbox;
27  import org.apache.myfaces.tobago.context.Markup;
28  import org.apache.myfaces.tobago.context.ResourceManagerUtils;
29  import org.apache.myfaces.tobago.internal.util.AccessKeyMap;
30  import org.apache.myfaces.tobago.layout.Measure;
31  import org.apache.myfaces.tobago.renderkit.CommandRendererBase;
32  import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
33  import org.apache.myfaces.tobago.renderkit.css.Classes;
34  import org.apache.myfaces.tobago.renderkit.css.Style;
35  import org.apache.myfaces.tobago.renderkit.html.Command;
36  import org.apache.myfaces.tobago.renderkit.html.CommandMap;
37  import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
38  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
39  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
40  import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
41  import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
42  import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
43  import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
44  import org.apache.myfaces.tobago.util.ComponentUtils;
45  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  import javax.faces.component.UIComponent;
50  import javax.faces.component.UISelectOne;
51  import javax.faces.context.FacesContext;
52  import javax.faces.model.SelectItem;
53  import java.io.IOException;
54  import java.util.List;
55  
56  /**
57   * Does the decoding with parent class CommandRendererBase.
58   *
59   * @see CommandRendererBase
60   */
61  public class MenuCommandRenderer extends CommandRendererBase {
62  
63    private static final Logger LOG = LoggerFactory.getLogger(MenuCommandRenderer.class);
64  
65    @Override
66    public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException {
67      UIMenuCommand menu = (UIMenuCommand) component;
68      TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
69  
70      boolean disabled = menu.isDisabled();
71      boolean firstLevel = RendererTypes.MENU_BAR.equals(menu.getParent().getRendererType());
72      LabelWithAccessKey label = new LabelWithAccessKey(menu);
73  
74      if (menu.getFacet(Facets.CHECKBOX) != null) {
75        // checkbox menu
76        UISelectBooleanCheckbox checkbox = (UISelectBooleanCheckbox) menu.getFacet(Facets.CHECKBOX);
77        boolean checked = ComponentUtils.getBooleanAttribute(checkbox, Attributes.VALUE);
78        String image = checked ? "image/MenuCheckmark.gif" : null;
79        String hiddenId = checkbox.getClientId(facesContext);
80        final CommandMap map = new CommandMap(new Command());
81        encodeItem(facesContext, writer, menu, label, map, disabled, firstLevel, image, null, "selectBoolean");
82        encodeHidden(writer, hiddenId, checked);
83      } else if (menu.getFacet(Facets.RADIO) != null) {
84        // radio menu
85        String clientId = menu.getClientId(facesContext);
86        UISelectOne radio = (UISelectOne) menu.getFacet(Facets.RADIO);
87        List<SelectItem> items = RenderUtils.getSelectItems(radio);
88        String hiddenId = radio.getClientId(facesContext);
89        for (SelectItem item : items) {
90          boolean checked = item.getValue().equals(radio.getValue());
91          String image = checked ? "image/MenuRadioChecked.gif" : null;
92          final String labelText = item.getLabel();
93          label.reset();
94  
95          if (labelText != null) {
96            if (labelText.indexOf(LabelWithAccessKey.INDICATOR) > -1) {
97              label.setup(labelText);
98            } else {
99              label.setText(labelText);
100           }
101         } else {
102           LOG.warn("Menu item has label=null where clientId=" + clientId);
103         }
104         final String formattedValue = RenderUtils.getFormattedValue(facesContext, radio, item.getValue());
105         final CommandMap map = new CommandMap(
106             new Command(clientId, null, null, null, null, null, null, null, null, null));
107         encodeItem(facesContext, writer, null, label, map, disabled, firstLevel, image, formattedValue, "selectOne");
108       }
109       encodeHidden(writer, hiddenId, getCurrentValue(facesContext, radio));
110     } else {
111       // normal menu command
112       String image = menu.getImage();
113       CommandMap map = new CommandMap(new Command(facesContext, menu));
114       encodeItem(facesContext, writer, menu, label, map, disabled, firstLevel, image, null, null);
115     }
116   }
117 
118   private void encodeHidden(TobagoResponseWriter writer, String hiddenId, Object value) throws IOException {
119     writer.startElement(HtmlElements.INPUT, null);
120     writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
121     writer.writeNameAttribute(hiddenId);
122     if (value != null) {
123       writer.writeAttribute(HtmlAttributes.VALUE, value.toString(), true);
124     }
125     writer.endElement(HtmlElements.INPUT);
126   }
127 
128   private void encodeItem(
129       FacesContext facesContext, TobagoResponseWriter writer, UIMenuCommand component, LabelWithAccessKey label,
130       CommandMap map, boolean disabled, boolean firstLevel, String image, String value, String sub)
131       throws IOException {
132 
133     writer.startElement(HtmlElements.LI, null);
134     if (component != null && !component.isTransient()) {
135       writer.writeIdAttribute(component.getClientId(facesContext));
136     }
137     Markup markup = null;
138     if (component != null) {
139       markup = component.getCurrentMarkup();
140       if (firstLevel) {
141         markup = Markup.TOP.add(markup);
142       }
143     }
144      // todo: solve workaround
145     String css = Classes.createWorkaround("menu", markup).getStringValue();
146     if (sub != null) {
147       css += " tobago-menu-" + sub;
148     }
149     writer.writeClassAttribute(css);
150     if (!disabled) {
151       writer.writeAttribute(DataAttributes.COMMANDS, JsonUtils.encode(map), true);
152     }
153     writer.writeAttribute(DataAttributes.VALUE, value, true);
154 
155     if (component != null) {
156       HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
157     }
158 
159     if (image != null) {
160       if (firstLevel) {
161         Style iconStyle = new Style();
162         iconStyle.setLeft(Measure.valueOf(0));
163         iconStyle.setTop(Measure.valueOf(0));
164         iconStyle.setHeight(Measure.valueOf(16));
165         iconStyle.setWidth(Measure.valueOf(16));
166 
167         writer.startElement(HtmlElements.IMG, null);
168         String imageWithPath = ResourceManagerUtils.getImageOrDisabledImageWithPath(facesContext, image, disabled);
169         writer.writeAttribute(HtmlAttributes.SRC, imageWithPath, false);
170         String imageHover = ResourceManagerUtils
171             .getImageOrDisabledImageWithPath(facesContext, HtmlRendererUtils.createSrc(image, "Hover"), disabled);
172         if (imageHover != null) {
173           writer.writeAttribute(DataAttributes.SRC_DEFAULT, imageWithPath, false);
174           writer.writeAttribute(DataAttributes.SRC_HOVER, imageHover, false);
175         }
176 
177         writer.writeAttribute(HtmlAttributes.ALT, label.getText(), true);
178         writer.writeStyleAttribute(iconStyle);
179         writer.endElement(HtmlElements.IMG);
180       } else {
181         Style style = new Style();
182         style.setBackgroundImage("url('"
183             + ResourceManagerUtils.getImageOrDisabledImageWithPath(facesContext, image, disabled)
184             + "')");
185         writer.writeStyleAttribute(style);
186       }
187     }
188 
189     writer.startElement(HtmlElements.A, null);
190     writer.writeAttribute(HtmlAttributes.HREF, "#", false);
191     if (image != null && firstLevel) {
192       writer.writeStyleAttribute("vertical-align:top");
193     }
194 //    writer.writeIdAttribute(clientId);
195 
196     if (label.getText() != null) {
197       if (label.getAccessKey() != null) {
198         if (LOG.isInfoEnabled()
199             && !AccessKeyMap.addAccessKey(facesContext, label.getAccessKey())) {
200           LOG.info("duplicated accessKey : " + label.getAccessKey());
201         }
202         if (!disabled && component != null && label.getAccessKey() != null) {
203           writer.writeAttribute(HtmlAttributes.ACCESSKEY, Character.toString(label.getAccessKey()), false);
204         }
205       }
206       HtmlRendererUtils.writeLabelWithAccessKey(writer, label);
207     }
208     writer.endElement(HtmlElements.A);
209     writer.endElement(HtmlElements.LI);
210   }
211 
212   public void encodeChildren(FacesContext facesContext, UIComponent component)
213       throws IOException {
214   }
215 
216   public boolean getRendersChildren() {
217     return true;
218   }
219   
220 
221 }