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.SelectBooleanCommand;
25  import org.apache.myfaces.tobago.component.SelectOneCommand;
26  import org.apache.myfaces.tobago.component.UIMenuSelectOne;
27  import org.apache.myfaces.tobago.component.UIToolBar;
28  import org.apache.myfaces.tobago.component.UIToolBarSeparator;
29  import org.apache.myfaces.tobago.config.Configurable;
30  import org.apache.myfaces.tobago.context.Markup;
31  import org.apache.myfaces.tobago.context.ResourceManager;
32  import org.apache.myfaces.tobago.context.ResourceManagerUtils;
33  import org.apache.myfaces.tobago.internal.component.AbstractUICommandBase;
34  import org.apache.myfaces.tobago.internal.component.AbstractUIMenu;
35  import org.apache.myfaces.tobago.internal.context.ResourceManagerFactory;
36  import org.apache.myfaces.tobago.internal.util.StringUtils;
37  import org.apache.myfaces.tobago.layout.Measure;
38  import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
39  import org.apache.myfaces.tobago.renderkit.LayoutComponentRendererBase;
40  import org.apache.myfaces.tobago.renderkit.css.Classes;
41  import org.apache.myfaces.tobago.renderkit.css.Style;
42  import org.apache.myfaces.tobago.renderkit.html.Command;
43  import org.apache.myfaces.tobago.renderkit.html.CommandMap;
44  import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
45  import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
46  import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
47  import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
48  import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
49  import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
50  import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
51  import org.apache.myfaces.tobago.util.ComponentUtils;
52  import org.apache.myfaces.tobago.util.CreateComponentUtils;
53  import org.apache.myfaces.tobago.util.FacetUtils;
54  import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  import javax.faces.component.UIComponent;
59  import javax.faces.context.FacesContext;
60  import javax.faces.model.SelectItem;
61  import java.io.IOException;
62  import java.util.List;
63  
64  public abstract class ToolBarRendererBase extends LayoutComponentRendererBase {
65  
66    private static final Logger LOG = LoggerFactory.getLogger(ToolBarRendererBase.class);
67  
68    @Override
69    public void prepareRender(FacesContext facesContext, UIComponent component) throws IOException {
70      super.prepareRender(facesContext, component);
71      HtmlRendererUtils.renderDojoDndSource(facesContext, component);
72    }
73  
74    protected String getLabelPosition(UIComponent component) {
75      return (String) component.getAttributes().get(Attributes.LABEL_POSITION);
76    }
77  
78    protected String getIconSize(UIComponent component) {
79      return (String) component.getAttributes().get(Attributes.ICON_SIZE);
80    }
81  
82    protected boolean isRightAligned(UIToolBar toolBar) {
83      return UIToolBar.ORIENTATION_RIGHT.equals(toolBar.getOrientation());
84    }
85  
86    @Override
87    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
88      UIToolBar toolBar = (UIToolBar) component;
89  
90      TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(context);
91  
92      Measure width = Measure.valueOf(-1);
93      for (UIComponent command : toolBar.getChildren()) {
94        if (command instanceof AbstractUICommandBase) {
95          width = renderToolbarCommand(context, toolBar, (AbstractUICommandBase) command, writer, width);
96        } else if (command instanceof UIToolBarSeparator) {
97          width = renderSeparator(context, toolBar, (UIToolBarSeparator) command, writer, width);
98        } else {
99          LOG.error("Illegal UIComponent class in toolbar (not a AbstractUICommandBase):" + command.getClass().getName());
100       }
101     }
102   }
103 
104   private Measure renderToolbarCommand(FacesContext facesContext, UIToolBar toolBar, AbstractUICommandBase command,
105       TobagoResponseWriter writer, Measure width) throws IOException {
106     if (command instanceof SelectBooleanCommand) {
107       return renderSelectBoolean(facesContext, toolBar, command, writer, width);
108     } else if (command instanceof SelectOneCommand) {
109       return renderSelectOne(facesContext, toolBar, command, writer, width);
110     } else {
111       if (command.getFacet(Facets.RADIO) != null) {
112         return renderSelectOne(facesContext, toolBar, command, writer, width);
113       } else if (command.getFacet(Facets.CHECKBOX) != null) {
114         return renderSelectBoolean(facesContext, toolBar, command, writer, width);
115       } else {
116         final CommandMap map = new CommandMap(new Command(facesContext, command));
117         return renderToolbarButton(
118             facesContext, toolBar, command, writer, false, width, map, null);
119       }
120     }
121   }
122 
123   // todo: remove component creation in renderer, for JSF 2.0
124   // todo: One solution is to make <tx:toolBarSelectOne> instead of <tc:toolBarSelectOne>
125   private Measure renderSelectOne(FacesContext facesContext, UIToolBar toolBar, AbstractUICommandBase command,
126       TobagoResponseWriter writer, Measure width) throws IOException {
127 
128     final List<SelectItem> items;
129 
130     UIMenuSelectOne radio = (UIMenuSelectOne) command.getFacet(Facets.RADIO);
131     if (radio == null) {
132       items = RenderUtils.getSelectItems(command);
133       radio = CreateComponentUtils.createUIMenuSelectOneFacet(facesContext, command);
134       radio.setId(facesContext.getViewRoot().createUniqueId());
135     } else {
136       items = RenderUtils.getSelectItems(radio);
137     }
138 
139     if (radio != null) {
140       writer.startElement(HtmlElements.SPAN, radio);
141       writer.writeClassAttribute(Classes.createWorkaround("toolBar", "selectOne", null));
142       Object value = radio.getValue();
143 
144       String currentValue = "";
145       boolean markFirst = !ComponentUtils.hasSelectedValue(items, value);
146       String radioId = radio.getClientId(facesContext);
147       for (SelectItem item : items) {
148         final String labelText = item.getLabel();
149         if (labelText != null) {
150           command.getAttributes().put(Attributes.LABEL, labelText);
151         } else {
152           LOG.warn("Menu item has label=null. UICommand.getClientId()=" + command.getClientId(facesContext));
153         }
154 
155         String image = null;
156         if (item instanceof org.apache.myfaces.tobago.model.SelectItem) {
157           image = ((org.apache.myfaces.tobago.model.SelectItem) item).getImage();
158         } else if (LOG.isDebugEnabled()) {
159           LOG.debug("select item is not " + org.apache.myfaces.tobago.model.SelectItem.class.getName());
160         }
161         if (image == null) {
162           image = "image/1x1.gif";
163         }
164         command.getAttributes().put(Attributes.IMAGE, image);
165 
166         if (item.getDescription() != null) {
167           command.getAttributes().put(Attributes.TIP, item.getDescription());
168         }
169 
170         String formattedValue = RenderUtils.getFormattedValue(facesContext, radio, item.getValue());
171         final boolean checked;
172         if (item.getValue().equals(value) || markFirst) {
173           checked = true;
174           markFirst = false;
175           currentValue = formattedValue;
176         } else {
177           checked = false;
178         }
179 
180         final CommandMap map = new CommandMap(new Command());
181         width = renderToolbarButton(
182             facesContext, toolBar, command, writer, checked, width, map, formattedValue);
183       }
184 
185       writer.startElement(HtmlElements.INPUT, null);
186       writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
187       writer.writeNameAttribute(radioId);
188       writer.writeAttribute(HtmlAttributes.VALUE, currentValue, true);
189       writer.endElement(HtmlElements.INPUT);
190       writer.endElement(HtmlElements.SPAN);
191     }
192     return width;
193   }
194 
195   // todo: remove component creation in renderer, for JSF 2.0
196   // todo: One solution is to make <tx:toolBarCheck> instead of <tc:toolBarCheck>
197   // may be renamed to toolBarSelectBoolean?
198   private Measure renderSelectBoolean(FacesContext facesContext, UIToolBar toolBar, AbstractUICommandBase command,
199       TobagoResponseWriter writer, Measure width) throws IOException {
200 
201     UIComponent checkbox = command.getFacet(Facets.CHECKBOX);
202     if (checkbox == null) {
203       checkbox = CreateComponentUtils.createUISelectBooleanFacetWithId(facesContext, command);
204     }
205 
206     final boolean checked = ComponentUtils.getBooleanAttribute(checkbox, Attributes.VALUE);
207     final String clientId = checkbox.getClientId(facesContext);
208 
209     writer.startElement(HtmlElements.SPAN, checkbox);
210     writer.writeClassAttribute(Classes.createWorkaround("toolBar", "selectBoolean", null));
211     final CommandMap map = new CommandMap(new Command());
212     width = renderToolbarButton(facesContext, toolBar, command, writer, checked, width, map, null);
213 
214     writer.startElement(HtmlElements.INPUT, null);
215     writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
216     writer.writeNameAttribute(clientId);
217     writer.writeAttribute(HtmlAttributes.VALUE, Boolean.toString(checked), false);
218     writer.endElement(HtmlElements.INPUT);
219     writer.endElement(HtmlElements.SPAN);
220 
221     return width;
222   }
223 
224 
225   private Measure renderToolbarButton(
226       FacesContext facesContext, UIToolBar toolBar, AbstractUICommandBase command, TobagoResponseWriter writer,
227       boolean selected, Measure width, CommandMap map, String value)
228       throws IOException {
229     if (!command.isRendered()) {
230       return width;
231     }
232 
233     //final String clientId = command.getClientId(facesContext);
234     final boolean disabled = ComponentUtils.getBooleanAttribute(command, Attributes.DISABLED);
235     final LabelWithAccessKey label = new LabelWithAccessKey(command);
236     final AbstractUIMenu dropDownMenu = FacetUtils.getDropDownMenu(command);
237     final ResourceManager resources = getResourceManager();
238 
239     final String labelPosition = getLabelPosition(command.getParent());
240     final String iconSize = getIconSize(command.getParent());
241     final String iconName = (String) command.getAttributes().get(Attributes.IMAGE);
242     final boolean lackImage = iconName == null;
243     final String image = lackImage ? null : getImage(facesContext, iconName, iconSize, disabled, selected);
244 
245     final boolean showIcon = !UIToolBar.ICON_OFF.equals(iconSize);
246     final boolean iconBig = UIToolBar.ICON_BIG.equals(iconSize);
247 
248     final boolean showLabelBottom = UIToolBar.LABEL_BOTTOM.equals(labelPosition);
249     final boolean showLabelRight = UIToolBar.LABEL_RIGHT.equals(labelPosition);
250     final boolean showLabel = showLabelBottom || showLabelRight;
251     final boolean showDropDownMenu = dropDownMenu != null && dropDownMenu.isRendered();
252     // two separate buttons for the command and the sub menu
253     final boolean separateButtons = hasAnyCommand(command) && showDropDownMenu;
254 
255     final Measure paddingTop = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-top");
256     final Measure paddingMiddle = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-middle");
257     final Measure paddingBottom = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-bottom");
258     final Measure paddingLeft = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-left");
259     final Measure paddingCenter = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-center");
260     final Measure paddingRight = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-right");
261     final Measure iconBigHeight = resources.getThemeMeasure(facesContext, toolBar, "custom.icon-big-height");
262     final Measure iconSmallHeight = resources.getThemeMeasure(facesContext, toolBar, "custom.icon-small-height");
263     final Measure iconBigWidth = resources.getThemeMeasure(facesContext, toolBar, "custom.icon-big-width");
264     final Measure iconSmallWidth = resources.getThemeMeasure(facesContext, toolBar, "custom.icon-small-width");
265 
266     // label style
267     final Style labelStyle;
268     if (showLabel) {
269       labelStyle = new Style();
270       labelStyle.setLeft(paddingLeft);
271       labelStyle.setTop(paddingTop);
272       labelStyle.setWidth(RenderUtils.calculateStringWidth(facesContext, toolBar, label.getText()));
273       labelStyle.setHeight(resources.getThemeMeasure(facesContext, toolBar, "custom.label-height"));
274     } else {
275       labelStyle = null;
276     }
277 
278     // button style
279     final Style buttonStyle = new Style();
280     buttonStyle.setLeft(Measure.ZERO);
281     buttonStyle.setTop(Measure.ZERO);
282     buttonStyle.setWidth(paddingLeft.add(paddingRight));
283     buttonStyle.setHeight(paddingBottom.add(paddingTop));
284 
285     // icon style
286     final Style iconStyle;
287 
288     if (showIcon) {
289       iconStyle = new Style();
290       iconStyle.setLeft(paddingLeft);
291       iconStyle.setTop(paddingTop);
292       iconStyle.setHeight(iconBig ? iconBigHeight : iconSmallHeight);
293       if (lackImage && showLabelRight && StringUtils.isNotBlank(label.getText())) {
294         iconStyle.setWidth(Measure.valueOf(1));
295       } else {
296         iconStyle.setWidth(iconBig ? iconBigWidth : iconSmallWidth);
297       }
298       if (showLabelBottom) {
299         labelStyle.setTop(labelStyle.getTop().add(iconStyle.getHeight()).add(paddingMiddle));
300         if (labelStyle.getWidth().lessThan(iconStyle.getWidth())) {
301           // label smaller than icon
302           labelStyle.setLeft(labelStyle.getLeft().add(iconStyle.getWidth().subtract(labelStyle.getWidth()).divide(2)));
303           buttonStyle.setWidth(buttonStyle.getWidth().add(iconStyle.getWidth()));
304         } else {
305           // label bigger than icon
306           iconStyle.setLeft(iconStyle.getLeft().add(labelStyle.getWidth().subtract(iconStyle.getWidth()).divide(2)));
307           buttonStyle.setWidth(buttonStyle.getWidth().add(labelStyle.getWidth()));
308         }
309         buttonStyle.setHeight(
310             buttonStyle.getHeight().add(iconStyle.getHeight()).add(paddingMiddle).add(labelStyle.getHeight()));
311       } else if (showLabelRight) {
312         labelStyle.setTop(labelStyle.getTop().add(iconStyle.getHeight().subtract(labelStyle.getHeight()).divide(2)));
313         labelStyle.setLeft(labelStyle.getLeft().add(iconStyle.getWidth()).add(paddingCenter));
314         buttonStyle.setWidth(
315             buttonStyle.getWidth().add(iconStyle.getWidth()).add(paddingCenter).add(labelStyle.getWidth()));
316         buttonStyle.setHeight(buttonStyle.getHeight().add(iconStyle.getHeight()));
317       } else {
318         buttonStyle.setWidth(buttonStyle.getWidth().add(iconStyle.getWidth()));
319         buttonStyle.setHeight(buttonStyle.getHeight().add(iconStyle.getHeight()));
320       }
321     } else {
322       iconStyle = null;
323       if (showLabel) {
324         // only label
325         buttonStyle.setWidth(buttonStyle.getWidth().add(labelStyle.getWidth()));
326         if (StringUtils.isBlank(label.getText())) {
327           buttonStyle.setWidth(buttonStyle.getWidth().add(iconSmallWidth));
328         }
329         buttonStyle.setHeight(buttonStyle.getHeight().add(labelStyle.getHeight()));
330       } else {
331         // both off: use some reasonable defaults
332         buttonStyle.setWidth(buttonStyle.getWidth().add(iconSmallWidth));
333         buttonStyle.setHeight(buttonStyle.getHeight().add(iconSmallWidth));
334       }
335     }
336 
337     // opener style (for menu popup)
338     final Style openerStyle = new Style();
339     openerStyle.setWidth(resources.getThemeMeasure(facesContext, toolBar, "custom.opener-width"));
340     openerStyle.setHeight(resources.getThemeMeasure(facesContext, toolBar, "custom.opener-height"));
341 
342     final Style menuStyle = new Style();
343     menuStyle.setLeft(buttonStyle.getWidth());
344     menuStyle.setTop(Measure.ZERO);
345     menuStyle.setWidth(paddingLeft.add(openerStyle.getWidth()).add(paddingRight));
346     menuStyle.setHeight(buttonStyle.getHeight());
347 
348     // opener style (for menu popup)
349     openerStyle.setLeft(menuStyle.getWidth().subtract(openerStyle.getWidth()).divide(2));
350     openerStyle.setTop(menuStyle.getHeight().subtract(openerStyle.getHeight()).divide(2));
351 
352     // item style
353     final Style itemStyle = new Style();
354     if (isRightAligned(toolBar)) { // overrides the default in the CSS file.
355       itemStyle.setLeft(resources.getThemeMeasure(facesContext, toolBar, "css.border-right-width"));
356     }
357     itemStyle.setWidth(
358         showDropDownMenu ? buttonStyle.getWidth().add(menuStyle.getWidth()) : buttonStyle.getWidth());
359     itemStyle.setHeight(buttonStyle.getHeight());
360 
361     // XXX hack
362     if (showDropDownMenu && lackImage && !showLabel) {
363       itemStyle.setWidth(openerStyle.getWidth());
364       buttonStyle.setWidth(openerStyle.getWidth());
365     }
366 
367     // change values when only have one button
368     if (showDropDownMenu && !separateButtons && (!lackImage || StringUtils.isNotBlank(label.getText()))) {
369       openerStyle.setLeft(openerStyle.getLeft().add(buttonStyle.getWidth()));
370       buttonStyle.setWidth(buttonStyle.getWidth().add(menuStyle.getWidth()));
371     }
372     
373     // start rendering
374     writer.startElement(HtmlElements.SPAN, command);
375     Markup itemMarkup = Markup.NULL;
376     if (selected) {
377       itemMarkup = itemMarkup.add(Markup.SELECTED);
378     }
379     if (disabled) {
380       itemMarkup = itemMarkup.add(Markup.DISABLED);
381     }
382     writer.writeClassAttribute(Classes.create(toolBar, "item", itemMarkup));
383     HtmlRendererUtils.renderTip(command, writer);
384     writer.writeStyleAttribute(itemStyle);
385 
386     writer.startElement(HtmlElements.SPAN, command);
387     if (separateButtons || !showDropDownMenu) {
388       writer.writeClassAttribute(Classes.create(toolBar, "button", selected ? Markup.SELECTED : Markup.NULL));
389     } else {
390       writer.writeClassAttribute(Classes.create(toolBar, "menu"));
391     }
392     writer.writeStyleAttribute(buttonStyle);
393     if (!toolBar.isTransient()) {
394       writer.writeIdAttribute(command.getClientId(facesContext));
395     }
396     if (map != null) {
397       writer.writeAttribute(DataAttributes.COMMANDS, JsonUtils.encode(map), true);
398     }
399     HtmlRendererUtils.writeDataAttributes(facesContext, writer, command);
400     if (value != null) {
401       writer.writeAttribute(DataAttributes.VALUE, value, true);
402     }
403 
404     // render icon
405     if (showIcon && iconName != null) {
406       writer.startElement(HtmlElements.IMG, command);
407       writer.writeAttribute(HtmlAttributes.SRC, image, false);
408       String imageHover
409           = ResourceManagerUtils.getImageWithPath(facesContext, HtmlRendererUtils.createSrc(iconName, "Hover"), true);
410       if (imageHover != null) {
411         writer.writeAttribute(DataAttributes.SRC_DEFAULT, image, false);
412         writer.writeAttribute(DataAttributes.SRC_HOVER, imageHover, false);
413       }
414       writer.writeAttribute(HtmlAttributes.ALT, label.getText(), true);
415       writer.writeStyleAttribute(iconStyle);
416       writer.endElement(HtmlElements.IMG);
417     }
418     // render label
419     if (showLabel) {
420       writer.startElement(HtmlElements.SPAN, command);
421       writer.writeClassAttribute(Classes.create(toolBar, "label"));
422       writer.writeStyleAttribute(labelStyle);
423       if (label.getText() != null) {
424         HtmlRendererUtils.writeLabelWithAccessKey(writer, label);
425       }
426       writer.endElement(HtmlElements.SPAN);
427     }
428 
429     if (separateButtons) {
430       writer.endElement(HtmlElements.SPAN);
431 
432       writer.startElement(HtmlElements.SPAN, command);
433       writer.writeClassAttribute(Classes.create(toolBar, "menu"));
434       writer.writeStyleAttribute(menuStyle);
435       // todo: span has not type: use data-tobago-type here (TOBAGO-1004)
436       writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.BUTTON, false);
437     }
438 
439     // render sub menu popup button
440     if (showDropDownMenu) {
441       writer.startElement(HtmlElements.IMG, command);
442       boolean dropDownDisabled = ComponentUtils.getBooleanAttribute(dropDownMenu, Attributes.DISABLED) || disabled;
443       String menuImage = ResourceManagerUtils
444           .getImageOrDisabledImageWithPath(facesContext, "image/toolbarButtonMenu.gif", dropDownDisabled);
445       writer.writeAttribute(HtmlAttributes.SRC, menuImage, false);
446       writer.writeStyleAttribute(openerStyle);
447       writer.endElement(HtmlElements.IMG);
448       renderDropDownMenu(facesContext, writer, dropDownMenu);
449     }
450     writer.endElement(HtmlElements.SPAN);
451     writer.endElement(HtmlElements.SPAN);
452 
453     return width.add(itemStyle.getWidth()).add(2); // XXX
454     // computation of the width of the toolBar will not be used in the moment.
455   }
456 
457   private Measure renderSeparator(
458       FacesContext facesContext, UIToolBar toolBar, UIToolBarSeparator separator, TobagoResponseWriter writer,
459       Measure width)
460       throws IOException {
461     if (!separator.isRendered()) {
462       return width;
463     }
464 
465     writer.startElement(HtmlElements.SPAN, separator);
466     writer.writeClassAttribute(Classes.create(toolBar, "item", Markup.DISABLED));
467     Style itemStyle = new Style();
468     itemStyle.setHeight(getItemHeight(facesContext, toolBar));
469     itemStyle.setWidth(Measure.valueOf(10));
470     writer.writeStyleAttribute(itemStyle);
471 
472     writer.startElement(HtmlElements.SPAN, separator);
473     writer.writeClassAttribute(Classes.create(toolBar, "separator"));
474     writer.endElement(HtmlElements.SPAN);
475 
476     writer.endElement(HtmlElements.SPAN);
477 
478     return width.add(itemStyle.getWidth()).add(2); // XXX
479     // computation of the width of the toolBar will not be used in the moment.
480   }
481 
482   protected Measure getItemHeight(FacesContext facesContext, Configurable toolBar) {
483     final String iconSize = getIconSize((UIComponent) toolBar);
484     final String labelPosition = getLabelPosition((UIComponent) toolBar);
485 
486     final boolean showIcon = !UIToolBar.ICON_OFF.equals(iconSize);
487     final boolean iconBig = UIToolBar.ICON_BIG.equals(iconSize);
488     final boolean iconSmall = UIToolBar.ICON_SMALL.equals(iconSize);
489     final boolean showLabelBottom = UIToolBar.LABEL_BOTTOM.equals(labelPosition);
490     final boolean showLabelRight = UIToolBar.LABEL_RIGHT.equals(labelPosition);
491     final boolean showLabel = showLabelBottom || showLabelRight;
492 
493     final ResourceManager resources = getResourceManager();
494 
495     final Measure paddingTop = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-top");
496     final Measure paddingMiddle = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-middle");
497     final Measure paddingBottom = resources.getThemeMeasure(facesContext, toolBar, "custom.padding-bottom");
498     final Measure iconHeight = iconBig
499         ? resources.getThemeMeasure(facesContext, toolBar, "custom.icon-big-height")
500         : resources.getThemeMeasure(facesContext, toolBar, "custom.icon-small-height");
501     final Measure labelHeight = resources.getThemeMeasure(facesContext, toolBar, "custom.label-height");
502 
503     Measure result = paddingTop;
504     if (showIcon) {
505       result = result.add(iconHeight);
506       if (showLabel && showLabelBottom) {
507         result = result.add(paddingMiddle);
508         result = result.add(labelHeight);
509       }
510     } else {
511       if (showLabel) {
512         result = result.add(labelHeight);
513       } else {
514         // both off: use some reasonable defaults
515         result = result.add(16);
516       }
517     }
518     result = result.add(paddingBottom);
519     return result;
520   }
521 
522   private boolean hasAnyCommand(AbstractUICommandBase command) {
523     return !hasNoCommand(command);
524   }
525 
526   private boolean hasNoCommand(AbstractUICommandBase command) {
527     return command.getAction() == null
528         && command.getActionListener() == null
529         && command.getActionListeners().length == 0
530         && command.getLink() == null
531         && command.getAttributes().get(Attributes.ONCLICK) == null;
532   }
533 
534   private String getImage(
535       FacesContext facesContext, String name, String iconSize, boolean disabled, boolean selected) {
536     int pos = name.lastIndexOf('.');
537     if (pos == -1) {
538       pos = name.length(); // avoid exception if no '.' in name
539     }
540     String key = name.substring(0, pos);
541     String ext = name.substring(pos);
542 
543     String size = "";
544     if (UIToolBar.ICON_SMALL.equals(iconSize)) {
545       size = "16";
546     } else if (UIToolBar.ICON_BIG.equals(iconSize)) {
547       size = "32";
548     }
549     String image = null;
550     ResourceManager resourceManager = ResourceManagerFactory.getResourceManager(facesContext);
551     if (disabled && selected) {
552       image = resourceManager.getImage(facesContext, key + "SelectedDisabled" + size + ext, true);
553       if (image == null) {
554         image = resourceManager.getImage(facesContext, key + "SelectedDisabled" + ext, true);
555       }
556     }
557     if (image == null && disabled) {
558       image = resourceManager.getImage(facesContext, key + "Disabled" + size + ext, true);
559       if (image == null) {
560         image = resourceManager.getImage(facesContext, key + "Disabled" + ext, true);
561       }
562     }
563     if (image == null && selected) {
564       image = resourceManager.getImage(facesContext, key + "Selected" + size + ext, true);
565       if (image == null) {
566         image = resourceManager.getImage(facesContext, key + "Selected" + ext, true);
567       }
568     }
569     if (image == null) {
570       image = resourceManager.getImage(facesContext, key + size + ext, true);
571       if (image == null) {
572         image = resourceManager.getImage(facesContext, key + ext, true);
573       }
574     }
575 
576     return facesContext.getExternalContext().getRequestContextPath() + image;
577   }
578 
579   public static void renderDropDownMenu(
580       FacesContext facesContext, TobagoResponseWriter writer, AbstractUIMenu dropDownMenu)
581       throws IOException {
582     writer.startElement(HtmlElements.OL, dropDownMenu);
583     // XXX fix naming conventions for CSS classes
584     writer.writeClassAttribute("tobago-menuBar tobago-menu-dropDownMenu");
585     RenderUtils.encode(facesContext, dropDownMenu);
586     writer.endElement(HtmlElements.OL);
587   }
588 
589   @Override
590   public void encodeChildren(FacesContext facesContext, UIComponent component)
591       throws IOException {
592   }
593 
594   @Override
595   public boolean getRendersChildren() {
596     return true;
597   }
598 }